Thursday, 13 November 2014

8 Entity Framework Gotchas


As a developer, it is no surprise to encounter unexpected behavior when working with a new technology.Microsoft added the Entity Framework (EF) to ADO.NET with the .NET 3.5 Service Pack 1 released in 2008 enabling developers to incorporate a data model directly in their application and interact with their data through the model rather than
working directly against the database. For background on EF, see my previous article, “Introducing ADO.NET Entity Framework” in the Nov/Dec 2007 issue of CODE Magazine.
Because ADO.NET Entity Framework approaches data access from a perspective that is new to many developers, what might be expected behavior in EF can stump developers who are new to working with the technology. I have been working with EF for over two years and spent a good part of that time writing an in-depth book on the topic (Programming Entity Framework, O’Riely). So along the way I encountered many bumps. Once I had a better understanding of EF, I was able to avoid many of these issues or at least understand them quickly when I saw an error or exception message. Being a version 1 product, there are also a number of surprises that are not “by design”.
Advertisement
In this article, I will introduce you to some of the more painful “gotchas” that you are likely to encounter while working with Entity Framework. These are not the obvious pain points about EF that you may have heard about frequently such as lack of lazy loading or the difficulties of change tracking across tiers. Instead I will focus on some lesser known gotchas which I have experienced myself, and whose repeated occurrence in the MSDN Forums [http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/threads/] assures me that I am not alone with these surprises. For each one of the gotchas, I’ll explain the behavior, why it is occurring and how to avoid or work around it, if possible. My goal is that you will be aware and prepared for these behaviors and therefore your workflow won’t be severely interrupted by them.
1. Runtime LINQ Query Failures
LINQ is an enhancement to both the Visual Basic and C# languages. LINQ provides you with strongly typed query syntax and IntelliSense to aid you in constructing queries. With LINQ to Entities, and LINQ to SQL for that matter, there is something that you need to be considerate of when constructing LINQ queries. Not everything that is valid in the eyes of the .NET Framework is equally valid in the eyes of your database engine. It is quite possible to write a LINQ to Entities query that compiles perfectly well, yet throws an exception at run time.
It is quite possible to write a LINQ to Entities query that compiles perfectly well, yet throws an exception at run time.
Take, for example, this very tempting query that tries to format my return values as part of the query using a very convenient .NET method-ToShortDateString.
C#
from c in context.Customers
select new {
 FirstName = c.FirstName.Trim(),
 LastName = c.FirstName.Trim(),
 ModDate=c.ModifiedDate.ToShortDateString()
 }
   
VB
From c In context.Customers
Select
 FirstName = c.FirstName.Trim,
 LastName = c.LastName.Trim,
 ModDate = c.ModifiedDate.ToShortDateString
This feels right when I construct it and both the C# and VB compilers are happy. The ToShortDateString method is a huge convenience and I expect it to return nice looking results.
But at runtime, the query throws a NotSupportedException when EF attempts to compile the query. The exception message is straightforward.
LINQ to Entities does not recognize the method 'System.String ToShortDateString()' method, and this method cannot be translated into a store expression.
Why is this? SQL Server has no method, operator or function that is comparable to ToShortDateString.
Entity Framework and LINQ to SQL can only compile methods and functions that have some counterpart in the target store. In the case of Entity Framework, the store could just as easily be Firebird or Oracle as it could be SQL Server. Even if SQL Server had a comparable method, every EF provider would also need to target a database that supports this method as well. Therefore, LINQ to Entities does not support the method.
But the .NET compiler has no way of knowing this at compile time because of the way Visual Studio evaluates LINQ expressions. All the compiler sees is valid .NET syntax. It is not until runtime that the process of translating the LINQ to Entities query into a store command (e.g., T-SQL or PL/SQL) is performed; and it is at that time that the problem with the query is detected.
As a side note, because Entity SQL is a string-based syntax, the compilation of its queries is completely deferred until runtime. Entity SQL does, however, have the advantage of leveraging provider-specific functions and operators.
There’s no way to avoid this gotcha except to have an awareness of what methods are reasonable store functions. Other than that, testing your application and all of your queries will protect you from releasing these problems into the wild.
2. Modified SSDL Gotcha
The Entity Framework metadata (the Entity Data Model (EDM) schema along with the mapping and store schema) support a wide variety of customization possibilities. However, the current iteration of the EF designer tools does not support all of these features. One of the interesting features of the Store Schema Definition Language (SSDL) is that while it is a representation of the schema of your database, you can modify the SSDL to enhance your model. A change to the SSDL does not impact your database, but allows you to build logic into your model that is not part of the data store.
Some of these enhancements are DefiningQueries (which allow you to inject store commands), modifying attributes of columns or even defining new parameters in stored procedures.
You can solve a lot of quandaries by modifying the SSDL directly. However, there is an expensive gotcha when taking advantage of this powerful capability. Some (but not all) of these manually added enhancements to the SSDL will be overwritten when you use the Update Model Wizard.
Advertisement
Why would this happen? The Update Model Wizard recreates any element in the SSDL which represents an object in the database. For example, if you have a Customer table in the database, the wizard’s job is to ensure that the SSDL correctly represents that table. If you have made any manual changes to the elements or attributes of that table’s definition in the SSDL, you will lose them completely as the wizard rebuilds the description of the Customer table.
The wizard will not touch elements that it does not recognize. For example, if you create a DefiningQuery to define a “virtual” stored procedure, in other words a procedure that does not exist in the database, but one which you want Entity Framework to be able to execute, this DefiningQuery will remain intact when you update the model. There is nothing to overwrite it with.
In all, the Update Model Wizard does not completely rewrite the SSDL, but only updates pieces of it. Those pieces that have a true counterpart in the database will be rewritten from scratch and any changes to those particular elements will be lost.
I’ve suffered from this overwrite enough times to make me think twice about customizing entities in the SSDL.
There is, unfortunately, no workaround for this problem. I tend to be very strict with my use of SSDL customizations and when I do insert them, I keep a separate file that I can use to copy and paste them back in if necessary. I hope that someday, some clever developer will find a way to create a tool for merging SSDL elements, perhaps something like “partial classes for XML”.
3. Function Mapping to Stored Procedures in Inheritance Hierarchies
While the EDM does support stored procedures in a number of scenarios, the rules by which these can be implemented are very strict. You may find yourself in a situation where you have happily mapped some stored procedures (called “functions” in the model) and then suddenly realize that you are about to unravel a large ball of yarn.
I have hit one particular function mapping rule numerous times. The rule is that if you have a stored procedure mapped to an entity that is part of an inheritance hierarchy, you must also map stored procedures to every entity in that hierarchy. If you have a lot of inheritance built into your model and you have not planned ahead for this, you will certainly be surprised.
If you have a stored procedure mapped to an entity that is part of an inheritance hierarchy, you must also map stored procedures to every entity in that hierarchy.
Imagine that in your database, you have a Person table, a Customer table and a SalesPerson table. The Customer and Salesperson tables provide additional details for those people who are either a Customer or a Salesperson as shown in Figure 1.
http://www.codemag.com/Article/Image/0907071/Lerman_fig01.pngFigure 1: Table schema in database.
In your model you have identified the Customer and SalesPerson as entities which derive from Person in a Table per Type inheritance (Figure 2).
http://www.codemag.com/Article/Image/0907071/Lerman_fig02.pngFigure 2: SalesPerson and Customer entities inheriting from Person.
This is very nice because you can easily query data and interact with these entities without having to constantly navigate through the Person entity in order to get to properties such as FirstName and LastName. For example, rather than writing a query that looks like this:
C#
from c in context.Customer.Include("Person")
orderby c.Person.LastName
select c;
   
VB
From c In context.Customer.Include("Person")
Order By c.Person.LastName
AdventureWorksLTEntity
You could express the query without navigating through Person to get to the LastName property.
C#
from c in context.People.OfType<Customer>
orderby c.LastName
select c;
   
VB
From c In context.People.OfType(Of Customer)
Order By c.LastName
Similarly, when interacting with the resulting Customer objects, you don’t need to go through the Person navigation property to get at the scalar properties of Person.
Now imagine that you have a nice set of insert, update and delete procedures in your database for the Person table. You can map them directly to the Customer entity in the model and SaveChanges will call those stored procedures when persisting modifications to a Person entity. However, when you validate the model, you’ll get an error telling you:
If an EntitySet mapping includes a function binding, function bindings must be included for all types. The following types do not have function bindings: Model.Customer, Model.SalesPerson.
Advertisement
The message says that you must also map insert, update and delete procedures to Customer and SalesPerson.
This is not a limitation of the designer or a problem with the model. Rather, it is an effect of how the model mappings work and more importantly, how the Entity Framework interprets the model in order to send the updates from your application to the database. As you grow to understand EF more thoroughly, this will not come as a surprise but might act as a deterrent from mapping the functions if you were hoping to quickly build a model and get to the task of building your program.
To me, this rule is not a deterrent or annoyance. A well-designed data model is an investment in your application. Either way, it is definitely something that will surprise you if you are not prepared for it.
NOTE: This rule applies to associated entities as well in certain scenarios. If you map functions to an entity which is the 1 or 0..1 end of a relationship you are also required to map the associated entities. Therefore if you mapped functions to Customer and Customer has a one-to-many relationship with Order, you must also map functions to the Order entity. However, if you map functions to the Order entity, which is the “many” end of this association, you don’t have to map functions to Customer.
4. ObjectContext.Refresh and Entity Graphs
A convenient and important feature of the ObjectContext class is the ability to refresh cached objects from the database. This can be used in response to OptimisticConcurrency Exceptions or as part of the general logic of your application. ObjectContext.Refresh supports both ClientWins and StoreWins options.
A ClientWins refresh will push entity values into the database and then set the state of the entity or entities involved to Unchanged. A StoreWins refresh will pull the current server values into the target entities and again, set their EntityState to Unchanged. In effect, StoreWins overwrites any changes made by the user with the database values.
When calling Refresh, you must identify which cached entities are to be refreshed. This could be a single entity or some set of entities that are contained in a variable, for example, a variable that points to a list of customer entities. What Refresh does not do is simply refresh every entity in the cache. The following code snippet shows Refresh being used with a specific list of Customer entities.
C#
List<Customer> custList=
    context.Customers
   .where(c=>c.FirstName=="Julie")
   .ToList();
context.Refresh
 (RefreshMode.ClientWins,custList);
   
VB
Dim custlist As List(Of Customer) = _
    context.Customers _
   .Where(Function(c) c.FirstName="Julie") _
   .ToList
context.Refresh _
 (RefreshMode.ClientWins,custList)
However there is something else that Refresh does not do. I discovered this when hammering on Refresh relentlessly while I was writing a chapter about Entity Framework Exceptions for my book. Refresh will not update graphs. It will only update the parent entity in a graph. In other words if you call Refresh and pass in a Customer graph, e.g., a Customer with Orders and LineItems attached to it as shown in the following example, only the Customer entity will get refreshed.
C#
Customer cust=
 context.Customers.Include("Orders.Items")
 .Where(c=>c.FirstName=="Julie")
 .First();
context.Refresh
 (RefreshMode.ClientWins,cust);
   
VB
Dim cust As Customer = _
 context.Customers.Include("Orders.Items") _
 .Where(Function(c) c.FirstName="Julie") _
  .First
context.Refresh
 (RefreshMode.ClientWins,cust)
Looking in SQL Profiler at the store command sent by the Refresh, you can see that only the Customer was refreshed and not the other entities in the graph.
SELECT
1 AS [C1],
[Extent1].[PersonID] AS [PersonID],
[Extent1].[AccountNumber] AS [AccountNumber],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent1].[TimeStamp] AS [TimeStamp],
[Extent1].[CustomerTypeID] AS [CustomerTypeID]
FROM [dbo].[Customer] AS [Extent1]
WHERE [Extent1].[PersonID] = 54
What’s worse is that there is no exception thrown. I call this a quiet failure. The way Refresh works is by design and the fact that you have requested that the full graph be refreshed is simply ignored. I imagine that many applications out there use Refresh with graphs and the developers have no idea that this method is not doing what they had expected.
I don’t know of a workaround for this except to be aware of the behavior.
5. Destroying an ObjectContext without Detaching Its Cached Entities
This gotcha really surprised me to the tune of many hours of wondering what on earth I was doing wrong. Finally, I asked about it in the MSDN forums (see the MSDN Forum Thread sidebar) and learned that is purely the result of the logic of how Entity Framework’s ObjectContext manages its entities.
You will likely discover this gotcha if you are attempting to cache entities to be used by different contexts throughout the lifetime of your application.
Here’s how I first approached this task. I executed a query that retrieved some Product entities that I would use in various places in my application.
C#
prodList =
 context.Products
        .OrderBy(p => p.ProductName)
        .ToList();
   
VB
prodList = _
 context.Products _
        .OrderBy(Function(p) p.ProductName)_
        .ToList
Then I disposed the ObjectContext, leaving the objects in memory. Next I spun up a new context and within that context I created a new Order and then a Line Item. As part of the Line Item definition, I attached one of the products from the prodList.
C#
newItem.Product= prodList[0];
   
VB
newItem.Product=prodList(0)
At runtime, when I finally attached the new order graph to the context, an InvalidOperationException was thrown with the message:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
Advertisement
It took me a while to understand where it was coming from. The problem was caused by the Product I had attached to the LineItem. Although the original context which was used to query the Products was long gone, deep within the Product object there was a lingering reference to that ObjectContext. When I attempted to add the Order graph, including the particular Product that I had attached to the LineItem, it was not possible to attach the Product to the new context.
I would have had to explicitly detach each of the products from that original context before disposing the context. Unfortunately, that was not something that seemed necessary because this is very subtle behavior. As explained by Jeff Derstadt in the MDSN thread mentioned earlier, the fact that Detach didn’t happen implicitly when the context was disposed was a design decision.
Now that I understand EF much better, it makes perfect sense to me that I should use the NoTracking MergeOption for the initial Product query when I intend to use the results as a reference list. This solution completely avoids attaching the products to the context and therefore, no detach would be necessary.
Because I originally used a LINQ to Entities query to retrieve the Products, the following code first casts that query to an ObjectQuery, then applies the MergeOption before executing the query.
C#
IEnumerable<Product> linqQuery =
 context.Products
 .OrderBy(p => p.ProductName);
((ObjectQuery)linqQuery).MergeOption =
 MergeOption.NoTracking;
prodList = linqQuery.ToList();
   
VB
Dim linqQuery As IEnumerable(Of Product) = _
  context.Products _
  .OrderBy(Function(p) p.ProductName)
CType(linqQuery,ObjectQuery).MergeOption =
 MergeOption.NoTracking;
prodList = linqQuery.ToList
This solves the problem of the context. But allow me to point out two other problems with this code that may surprise you as well.
First, note that the Product is now attached to the new context. Prior to disposing the new context, you’ll need to explicitly detach the product from this context. In order to do this, you’ll want to have a variable by which to reference the instance.
Rather than merely assigning the product using prodList[0], create an instance of it first and use that instance to assign to the LineItem. Then before disposing the context, call Detach (see Listing 1).
That’s the first problem. The second is so common that it deserves to be highlighted as my next gotcha.
6. Adding Graphs with Both New and Existing Entities to the ObjectContext
If you ran the code in Listing 1, you will still run into one more gotcha-one that developers frequently ask about not only in the forums, but in e-mails that I receive.
When the ObjectContext executes the AddToOrders method, the runtime will throw an InvalidOperationException with the message:
The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
The message is clear, yet it still leaves a mystery. Most often, when presented with this error, the developer will focus on the Order because that is the target of the AddToOrders method. But, in fact, there are three entities in the Order graph-newOrder, newItem, and existingProduct.
The existingProduct is the entity that already has an EntityKey and is creating the problem. This EntityKey was defined when the Product entity was initially materialized as a result of the earlier query.
The code-generated AddToOrders method leans on the ObjectContext.AddObject method. AddObject applies the same logic to everything in the graph. So internally it tries to Add the newOrder to the context and then call Add with newItem, and finally call Add with the existingProduct.
And there is your problem. Entity Framework is calling Add on existingProduct when that object is not in the proper state to be added. You need to attach the existingProduct object, not add it.
Attach Reference Entities to the Context before Attaching them to a New Entity
There is an easy way to solve this problem, but the pattern you need for the solution is not one that is obvious or discoverable. You need to separate your steps into two sets of logic. First, explicitly add the Product to the new context and add the Order graph (with its LineItem) to the context. Then after you’ve attached these both to the context, you can hook the pre-existing Product up to the Added Order.LineItem. With this pattern, everything will align properly.
C#
context.Attach(existingProduct);
context.AddToOrders(newOrder);
newItem.Product = existingProduct;
context.SaveChanges();
context.Detach(existingProduct);
   
VB
context.Attach(existingProduct)
context.AddToOrders(newOrder)
newItem.Product = existingProduct
context.SaveChanges()
context.Detach(existingProduct)
Interestingly, if you attempt to set the newItem.Product when only one of these has been bound to the ObjectContext (either the existingProduct or the Order graph, but not both), then you will get yet another exception.
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
Advertisement
The logic of this is certainly not apparent. When you detach one entity in the graph, its ObjectContext property evaluates to null. The other, which is now attached to the context, returns the ObjectContext instance. Entity Framework considers the null ObjectContext to be a “different” context and therefore won’t allow objects from different contexts to be joined. This looks to me like an oversight in the EF classes. I have spent plenty of time trying to figure out the one true and simple way to join pre-existing entities to new entities. Hopefully this saves you some time and aggravation.
I have spent plenty of time trying to figure out the one true and simple way to join pre-existing entities to new entities.
Avoid Attaching Issues by Working Directly with EntityKeys
I’ll now offer another more direct way to get around this problem. Rather than working with the reference entity, existingProduct, to identify the LineItem’s product, you can work directly with its EntityKey. Using the product’s EntityKey is akin to simply assigning a foreign key value to the LineItem.Product property. And since you will be working with an EntityKey and not the entity itself, you will avoid the collision caused by the EntityState of the various entities in the graph.
To solve this problem, you will work directly with the EntityReference property that is created by the code generator along with the navigation reference property from the model. In this case, LineItem has a Product property as well as a ProductReference property. Product points to the actual product instance while ProductReference contains only the EntityKey for that product.
With this pattern you won’t ever have to worry about the product getting attached to the context and then having to detach it again so that you can reuse it.
Listing 2 shows the code that creates the Order and LineItem, assigns the LineItem’s Product using the EntityKey and then saves the graph back to the database using a new ObjectContext instance.
Note that all of the extra code dealing with the product’s binding to the context has gone away, including the last bit of code to detach the product from the context.
Additionally, the code assigns the order’s pre-existing customer using the same logic and the assumption that you have an existing list of Customer entities available.
7. ObjectContext Does Not Track Changes Naturally in Web Apps
The Visual Studio compiler does not alert you when you are using bad patterns. There is a very bad pattern that you could easily code using EF in websites without realizing that EF will completely drop the ball. This is more likely to impact developers who are building RAD Web apps using EF but not using the EntityDataSource control.
Here’s the setup. Suppose you declare an ObjectContext (MyEntities) in the Page class. In the Page Load event, query for some data and bind it to one or more UI controls. Then, using a button on the page (Save), insert an ObjectContext.SaveChanges method into the button’s Click event.
It looks right (see Listing 3) and, in fact, is a pattern that works perfectly with Windows Forms and WPF apps.
What’s wrong with this innocent-looking code? The problem is specific to how Web pages work. While this may be obvious to experienced Web developers, it is not so obvious to those who are newer to creating Web pages. When the Page Load event is run, this is occurring on the server. The page class does all of its work including executing the EF query and then creates the HTML that represents the page. The HTML is sent down to the browser and the server is finished with its job. At that point, the page class and all of its objects are disposed. That means the ObjectContext that was created and the objects are also disposed.
When the user does their work on the Web page and then clicks the Save button, the server creates a brand new page class, processes all of the required code and creates a new set of HTML to send back to the browser. That means a brand new ObjectContext is created.
The ObjectContext that is used to call SaveChanges in the Save button event is not the same one that was used to execute the query. What you lose when working with the Web page is all of the change tracking capabilities that you would normally have with a long-running ObjectContext. Therefore, when SaveChanges is called, there are no changes for the context to save. Nothing will happen and the user’s edits will not be persisted to the database.
If you are doing RAD development for a website, you have a number of options to allow changes to be saved. The EntityDataSource control is similar to SqlDataSource and LINQDataSource, but is designed specifically to work with Entity Framework. It is configured as a UI control and by default, hides all of the work that it does to ensure that the users’ modifications are persisted to the database. You can customize the behavior on the server side. Another technology, ASP.NET Dynamic Data, allows you to create dynamic sites using pre-defined page templates and controls which work with a data model (e.g., LINQ to SQL or Entity Data Model) to automate website creation. By default, Dynamic Data templates and controls hide all of the plumbing so that you don’t have to worry about getting data to the page or back to the database. You can also use entities with ASP.NET MVC applications or the ASP.NET ObjectDataSource control, where you can provide more separation of business logic in your entities. Finally, if you are building a highly layered application, you can use Web Services, WCF Services, ADO.NET Data Services or construct your own business logic for persisting changes from the UI through to the server.
Advertisement
8. Strongly Typed Variables in a Query Break Eager Loading
Here’s a gotcha that came up in the MSDN forums as I was writing this article.
Entity Framework provides the capability to easily query for graphs. In other words, you can request customers along with their orders using the ObjectContext.Include method. For example context.customers.Include(“Orders”). This is referred to as eager loading.
A developer had written a query that used this method to eager load some data along with its related data. In the query, he had strongly typed the reference variable. The query below demonstrates this strong-typing by declaring the reference variable, c, as a Customer type in the query.
C#
IQueryable<Customer> query =
 from Customer c
 in context.Customers.Include("Orders")
 select c;
List<Customer> customers = query.ToList();
   
VB
Dim query As IQueryable(Of Customer) = _
  From c As Customer _
  In context.Customers.Include("Orders")
Dim customers As List(Of Customer) = _
 query.ToList()
When the query executed, it only returned Customers. None of the related Orders were in the graph. Digging a little further, you can see in the T-SQL snippet below that the store query that hit the database only asks for Customers. There is no indication that the client requested that the Orders be eager-loaded with the Customers.
SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]
Strong typing causes the problem when used in tandem with an Include method. The fact that the query specifically indicates to return Customers overrides the Include which is then completely ignored when the query is compiled.
Strong typing causes the problem when used in tandem with an Include method.
By removing the strong typing, the Include works as expected and the Orders are brought back with their Customers.
C#
IQueryable<Customer> query =
 from c
 in context.Customers.Include("Orders")
 select c;
   
VB
Dim query As IQueryable(Of Customer) = _
  From c _
  In context.Customers.Include("Orders")
Again, this is another quiet failure. The compiler does not indicate that the strong typing and the eager loading are not compatible.
Summary
Entity Framework is an important addition to ADO.NET, but as with any new technology, it takes a while to work some of the kinks out and to get enough shared knowledge about how the tool works for developers to leverage.
I find the MSDN forums to be a great resource when I get stuck on something that just doesn’t make sense. More and more developers are gaining expertise with Entity Framework and many of them, along with quite a few members of the Entity Framework team, spend time on the forums.
The gotchas that I covered in this article may be aggravating, but once you are familiar with them and prepared for them, you’ll have a much smoother journey with Entity Framework and be able to focus on its many benefits.
MSDN Forum Thread
Entities and Out of Scope ObjectContext
Listing 1: Detaching the cached product entity before disposing the context ensures that you can reuse the product elsewhere.
C#
LineItem newItem = LineItem.CreateLineItem(0, 25, 1, 0, 1);
Product existingProduct=prodList[0];
newItem.Product = existingProduct;
newOrder.LineItems.Add(newItem);
using (MyEntities context = new MyEntities())
{
  context.AddToOrders(newOrder);
  context.SaveChanges(); //still one more Gotcha to deal with
  context.Detach(existingProduct);
}
     
VB
Dim newItem As LineItem = _
 LineItem.CreateLineItem(0, 25, 1, 0, 1)
Dim existingProduct As Product=prodList(0)
newItem.Product = existingProduct
newOrder.LineItems.Add(newItem)
Using context As New MyEntities
  context.AddToOrders(newOrder)
  context.SaveChanges() 'still one more Gotcha to deal with
  context.Detach(existingProduct)
End Using
Listing 2: Inserting a new Order/LineItem graph along with the LineItem’s product reference.
C#
Order newOrder = new Order();
newOrder.CustomerReference.EntityKey = custList[0].EntityKey;
LineItem newItem = LineItem.CreateLineItem(0, 25, 1, 0, 1);
newItem.ProductReference.EntityKey = prodList[0].EntityKey;
newOrder.LineItems.Add(newItem);
using (MyEntities context = new NorthwindModifiedEntities())
{
  context.AddToOrders(newOrder); //add the graph
  context.SaveChanges();
}
     
VB
Dim newOrder As New Order()
newOrder.CustomerReference.EntityKey = custList(0).EntityKey
Dim newItem As LineItem = _
 LineItem.CreateLineItem(0, 25, 1, 0, 1)
newItem.ProductReference.EntityKey = prodList(0).EntityKey
newOrder.LineItems.Add(newItem)
Using context As MyEntities = New NorthwindModifiedEntities()
  context.AddToOrders(newOrder) 'add the graph
  context.SaveChanges()
End Using
Listing 3: A seemingly logical use of EF in a Web page.
C#
public partial class _Default : System.Web.UI.Page
{
  MyEntities context =new MyEntities();
    protected void Page_Load(object sender, EventArgs e)
    {
      if (IsPostBack)
      {
        ObjectResult<Person> result =
          context.Person.OrderBy("it.LastName")
                        .Execute(MergeOption.AppendOnly);
        PeopleGrid.DataSource = result;
        DataBind();
      }
    }
     
    protected void Save_Click(object sender, EventArgs e)
    {
      context.SaveChanges();
    }
}
     
VB
Partial Public Class _Default
   Inherits System.Web.UI.Page
  Private context As New MyEntities()
  Protected Sub Page_Load
           (ByVal sender As Object, ByVal e As EventArgs)
    If IsPostBack Then
      Dim result As ObjectResult(Of Person) = _
          context.Person _
                 .OrderBy("it.LastName") _
                 .Execute(MergeOption.AppendOnly)
      PeopleGrid.DataSource = result
      DataBind()
    End If
  End Sub
     
  Protected Sub Save_Click _
           (ByVal sender As Object, ByVal e As EventArgs)
    context.SaveChanges()
  End Sub
End Class

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...