Entity Framework 6 Recipes 2nd Edition (9-3) Translation-& gt; find out what has changed in the Web API, entityrecipes

Source: Internet
Author: User
Tags connectionstrings

Entity Framework 6 Recipes 2nd Edition (9-3)

9-3. Find out what has changed in the Web API

Problem

You do not have to write a separate update method for each object class. in addition, EF6 Code Frist is used for data access management.

In this example, we simulate an N-layer scenario and use a separate client (console application) to call a separate REST-based Web site (web api application)

Note: Each layer uses a separate Visual Studio solution, which makes it easier to configure, debug, and simulate an N-layer application.

Suppose there is a travel agency and reservation model shown in Figure 9-3.

 

Figure 9-3.A travel agency and Reservation Model

The model shows the relationship between the travel agency and their reservation. We need to put the model and database access behind the web api service so that any client can insert, update, and delete orders through HTTP.

Create a service and perform the following steps:

1. Create a new ASP. net mvc 4 Web application. In the wizard, select the Web API template and name the project Recipe3.Service.

2. Add a Web API controller to the project named TravelAgentController.

3. Next, add the TravelAgent and Booking object classes such as Listing 9-12.

Listing 9-12.Travel Agent and Booking Entity Classes

Public class TravelAgent

{

Public TravelAgent ()

{

This. Bookings = new HashSet <Booking> ();

}

Public int AgentId {get; set ;}

Public string Name {get; set ;}

Public virtual ICollection <Booking> Bookings {get; set ;}

}

Public class Booking

{

Public int BookingId {get; set ;}

Public int AgentId {get; set ;}

Public string Customer {get; set ;}

Public DateTime BookingDate {get; set ;}

Public bool Paid {get; set ;}

Public virtual TravelAgent {get; set ;}

}

4. Add a reference to EF6 in the "Recipe1.Service" project. It is best to use the NuGet Package Manager to add. Right-click "Reference" and choose "manage NuGet packages. From the" online "tab, locate and install the EF6 package. This will download, install and configure the EF6 library to your project ..

5. Add a class named Recipe3Context and add Code such as Listing 9-13,

Make sure it inherits the DbContext class.

Listing 9-13.Context Class

Public class Recipe3Context: DbContext

{

Public Recipe3Context (): base ("Recipe3ConnectionString "){}

Public DbSet <TravelAgent> TravelAgents {get; set ;}

Public DbSet <Booking> Bookings {get; set ;}

 

Protected override void OnModelCreating (DbModelBuilder modelBuilder)

{

ModelBuilder. Entity <TravelAgent> (). HasKey (x => x. AgentId );

ModelBuilder. Entity <TravelAgent> (). ToTable ("Chapter9.TravelAgent ");

ModelBuilder. Entity <Booking> (). ToTable ("Chapter9.Booking ");

}

}

6. Add the Recipe3ConnectionString connection string such as Listing 9-14 to the ConnectionStrings section in the Web. Config file.

Listing 9-14.Connection string of the Recipe3Context in the Web API Service

<ConnectionStrings>

<Add name = "Recipe3ConnectionString"

ConnectionString = "Data Source = .;

Initial Catalog = EFRecipes;

Integrated Security = True;

MultipleActiveResultSets = True"

ProviderName = "System. Data. SqlClient"/>

</ConnectionStrings>

7. insert the code in Listing 9-15 to Global. in the Application_Start method of asax. these codes prohibit EF from conducting model compatibility checks and indicating that loop references caused by bidirectional reference of navigation attributes between TravelAgent and Booking classes are ignored when JSON sequences are indicated.Listing 9-15.Disable the Entity Framework Model Compatibility Check

Protected void Application_Start ()

{

// Disable EF for model compatibility check

Database. SetInitializer <Recipe3Context> (null );

// If the navigation attribute between objects is cyclically referenced, the web api will encounter an error when serializing an object in Json format. This is the default condition of Json.net in sequence. To solve this problem, simply configure the JSON serializer to ignore circular references.

GlobalConfiguration. Configuration. Formatters. JsonFormatter

. SerializerSettings. ReferenceLoopHandling =

Newtonsoft. Json. ReferenceLoopHandling. Ignore;

...

}

8. Modify the routing configuration of the RouteConfig. cs file of the Web API according to Listing 9-16

Listing 9-16.Modifications to RouteConfig Class to Accommodate RPC-Style Routing

Public static void Register (HttpConfiguration config)

{

Config. Routes. MapHttpRoute (

Name: "ActionMethodSave ",

RouteTemplate: "api/{controller}/{action}/{id }",

Defaults: new {id = RouteParameter. Optional}

);

}

9. Finally, use the code in Listing 9-17 to replace the code in TravelAgentController.

Listing 9-17.Travel Agent Web API Controller

Public class TravelAgentController: ApiController

{

// GET api/travelagent

[HttpGet]

Public IEnumerable <TravelAgent> Retrieve ()

{

Using (var context = new Recipe3Context ())

{

Return context. TravelAgents. Include (x => x. Bookings). ToList ();

}

}

/// <Summary>

/// Use a Web API route to update the TravelAgent Method

/// </Summary>

Public HttpResponseMessage Update (TravelAgent travelAgent)

{

Using (var context = new Recipe3Context ())

{

Var newParentEntity = true;

// Add an object and set a State value for its object graph (including Parent and Child entities) to let context know which objects need to be updated

Context. TravelAgents. Add (travelAgent );

If (travelAgent. AgentId> 0)

{

// The value of ID is greater than 0, indicating that this is an object that already exists in the Database. We need to set the state of this object to updated.

Context. Entry (travelAgent). State = EntityState. Modified;

NewParentEntity = false;

}

// Iterate the child body and assign the correct state.

Foreach (var booking in travelAgent. Bookings)

{

If (booking. BookingId> 0)

// If the value of ID is greater than 0, it indicates that booking already exists and the object State is set to Modified.

Context. Entry (booking). State = EntityState. Modified;

}

Context. SaveChanges ();

HttpResponseMessage response;

// Set the Http status code based on the object State

Response = Request. CreateResponse (newParentEntity

? HttpStatusCode. Created: HttpStatusCode. OK, travelAgent );

Return response;

}

}

[HttpDelete]

Public HttpResponseMessage Cleanup ()

{

Using (var context = new Recipe3Context ())

{

Context. Database. ExecuteSqlCommand ("delete from chapter9.booking ");

Context. Database. ExecuteSqlCommand ("delete from chapter9.travelagent ");

}

Return Request. CreateResponse (HttpStatusCode. OK );

}

}

Next, create a client that calls the above services.

10. Create a solution and add a console application named Recipe3.Client.

11. Use the code in program. cs from the Listing 9-18 code

Listing 9-18.Our Windows Console Application That Serves as Our Test Client

Internal class Program

{

Private HttpClient _ client;

Private TravelAgent _ agent1, _ agent2;

Private Booking _ booking1, _ booking2, _ booking3;

Private HttpResponseMessage _ response;

Private static void Main ()

{

Task t = Run ();

T. Wait ();

Console. WriteLine ("\ nPress <enter> to continue ...");

Console. ReadLine ();

}

Private static async Task Run ()

{

Var program = new Program ();

Program. ServiceSetup ();

// Do not run the command until the data is cleared.

Await program. CleanupAsync ();

Program. CreateFirstAgent ();

// Do not run the following command before creating the agent:

Await program. AddAgentAsync ();

Program. CreateSecondAgent ();

// Do not run the following command before creating the agent:

Await program. AddSecondAgentAsync ();

Program. ModifyAgent ();

// Do not run the following command before updating the agent.

Await program. UpdateAgentAsync ();

// Do not proceed until agents are fetched

Await program. FetchAgentsAsync ();

}

Private void ServiceSetup ()

{

// Construct a Web API call instance

_ Client = new HttpClient {BaseAddress = new Uri ("http: // localhost: 6687 /")};

// Add the media type of the receiving header so that the request can receive resources in Json format returned by the Web API _ client. DefaultRequestHeaders. Accept. Add (new MediaTypeWithQualityHeaderValue ("application/json "));

}

Private async Task CleanupAsync ()

{

// Call the cleanup method of the server

_ Response = await _ client. DeleteAsync ("api/travelagent/cleanup /");

}

Private void CreateFirstAgent ()

{

// Create a new TravelAgent and booking object

_ Agent1 = new TravelAgent {Name = "John Tate "};

_ Booking1 = new Booking

{

Customer = "Karen Steven s ",

Paid = false,

BookingDate = DateTime. Parse ("2/2/2010 ")

};

_ Booking2 = new Booking

{

Customer = "Dolly Parton ",

Paid = true,

BookingDate = DateTime. Parse ("3/10/2010 ")

};

_ Agent1.Bookings. Add (_ booking1 );

_ Agent1.Bookings. Add (_ booking2 );

}

Private async Task AddAgentAsync ()

{

// Call the common update method in the Web API service to add the agent and bookings

_ Response = await _ client. PostAsync ("api/travelagent/update /",

_ Agent1, new JsonMediaTypeFormatter ());

If (_ response. IsSuccessStatusCode)

{

// Obtain the newly created travel agent object from the server, which contains the auto-increment ID value of the database

_ Agent1 = await _ response. Content. ReadAsAsync <TravelAgent> ();

_ Booking1 = _ agent1.Bookings. FirstOrDefault (x => x. Customer = "Karen Steven s ");

_ Booking2 = _ agent1.Bookings. FirstOrDefault (x => x. Customer = "Dolly Parton ");

Console. WriteLine ("Successfully created Travel Agent {0} and {1} Booking (s )",

_ Agent1.Name, _ agent1.Bookings. Count );

}

Else

Console. WriteLine ("{0} ({1})", (int) _ response. StatusCode, _ response. ReasonPhrase );

}

Private void CreateSecondAgent ()

{

// Create a new agent and booking object

_ Agent2 = new TravelAgent {Name = "Perry Como "};

_ Booking3 = new Booking

{

Customer = "Loretta Lynn ",

Paid = true,

BookingDate = DateTime. Parse ("3/15/2010 ")

};

_ Agent2.Bookings. Add (_ booking3 );

}

Private async Task AddSecondAgentAsync ()

{

// Call the common update method in the Web API service to add the agent and booking

_ Response = await _ client. PostAsync ("api/travelagent/update /",

_ Agent2, new JsonMediaTypeFormatter ());

If (_ response. IsSuccessStatusCode)

{

// Obtain the newly created travel agent object from the server, which contains the auto-increment ID value of the database

_ Agent2 = await _ response. Content. ReadAsAsync <TravelAgent> ();

_ Booking3 = _ agent2.Bookings. FirstOrDefault (x => x. Customer = "Loretta Lynn ");

Console. WriteLine ("Successfully created Travel Agent {0} and {1} Booking (s )",

_ Agent2.Name, _ agent2.Bookings. Count );

}

Else

Console. WriteLine ("{0} ({1})", (int) _ response. StatusCode, _ response. ReasonPhrase );

}

Private void ModifyAgent ()

{

// Modify the Name attribute of agent 2 and add a booking 1

_ Agent2.Name = "Perry Como, Jr .";

_ Agent2.Bookings. Add (_ booking1 );

}

Private async Task UpdateAgentAsync ()

{

// Call the common update method in the Web API service to update agent 2

_ Response = await _ client. PostAsync ("api/travelagent/update /",

_ Agent2, new JsonMediaTypeFormatter ());

If (_ response. IsSuccessStatusCode)

{

// Obtain the travel agent returned from the server that contains the new ID

_ Agent1 = _ response. Content. ReadAsAsync <TravelAgent> (). Result;

Console. WriteLine ("Successfully updated Travel Agent {0} and {1} Booking (s )",

_ Agent1.Name, _ agent1.Bookings. Count );

}

Else

Console. WriteLine ("{0} ({1})", (int) _ response. StatusCode, _ response. ReasonPhrase );

}

Private async Task FetchAgentsAsync ()

{

// Call the Get method of the server to obtain all the Travel Agents and Bookings

_ Response = _ client. GetAsync ("api/travelagent/retrieve"). Result;

If (_ response. IsSuccessStatusCode)

{

// Obtain the newly created travel agent with the ID returned from the server

Var agents = await _ response. Content. ReadAsAsync <IEnumerable <TravelAgent> ();

Foreach (var agent in agents)

{

Console. WriteLine ("Travel Agent {0} has {1} Booking (s)", agent. Name,

Agent. Bookings. Count ());

}

}

 

Else

Console. WriteLine ("{0} ({1})", (int) _ response. StatusCode, _ response. ReasonPhrase );

}

}

12. Finally, add the same TravelAgent and Booking classes as listed in Listing 9-12.

The following Listing 9-18 is the client output result:

========================================================== ==========================================

Successfully created Travel Agent John Tate and 2 Booking (s)

Successfully created Travel Agent Perry Como and 1 Booking (s)

Successfully updated Travel Agent Perry Como, Jr. and 2 Booking (s)

Travel Agent John Tate has 1 Booking (s)

Travel Agent Perry Como, Jr. has 2 Booking (s)

========================================================== ============================================

How it works

Run the Web API application first. the home page is displayed. now, the website is running and the service is ready for use. next, open the console project and go to program. set a breakpoint on the cs homepage to run the console application. first, establish a Pipeline Connection with the server, and configure the media type of the Request Header so that it can receive the JSON format. Then, the client executes the DeleteAsync method, it calls the Cleanup method in the TravelAgent controller in the Web API. This method deletes all the previous data in the database table. Next, we create a travel agent and two booking objects on the client. The HttpClient object calls the PostAsync method and transmits the three newly created objects to the server, if you add a breakpoint before the Web API's TravelAgent controls the Update method, you will see that its parameters receive the TravelAgent and its booking object. The Update method adds the Added or Modified flag to TravelAgent, and Context tracks these objects.

Note:: You may use Add or Attach as a group of new object (for example, the IDs of multiple Booking instances are all 0)

. If you useAttach, EF will throw an exception due to primary key conflicts.

We determine that the ID value is 0, indicating that the object State is Added. Otherwise, it is Modified, and we use the newParentEntity variable as a flag for the new entity to return the Http status code correctly.

Then iterate the booking entities contained in TravelAgent, and mark them with Added or Modified,

Next, call the SaveChanges method to generate an insert statement for the Added state entity, and generate an update statement for the Modified state entity. Then, if TravelAgent is Added, return the http status code 201, if it is Modified, 200 is returned. The status code tells the caller that the operation has been completed successfully.

When the distribution is based on the REST service, it is best to use the Http status code to inform its caller of the execution of the operation.

Then we add another new travel agent and booking on the client, and use the PostAsync method to call the Update method of the server.

Next, we modify the Name attribute of the second Travel agent object and move a booking object from the first Travel agent object. When we call the Update method this time, each entity Id has a value greater than 0. Therefore, the state of these entities is set to modified. Therefore, EF is saved as an Update SQL statement.

Finally, the client calls the Retrieve Method of the server through the GetAsync method of httpclient. This method returns all the travel agent and booking entities. Here we simply use the Include () method to pre-load the booking object.

You need to know that the JSON serializer will serialize all Public attributes of the object, even if you only project several attributes (such as Select fields.

In this section, we encapsulate EF operations into a Web API service, and the client can call these services with the HttpClient object. In this example, we do not prioritize the use of Web APIs.HTTPAction-based distribution, but moreRPCRouting Method

In actual projects, you may prefer the HTTP-based action-based approach, because it is more in line with the ASP. NET Web API's REST service-based intent.

In actual projects, we may be more happy to create a separate layer (Visual Studio class library) for EF data access so that it can be stripped from the Web API service.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.