What’s New in Entity Framework 4? API Changes(2)(转)

EntitySets Now Return ObjectSet Classes

In the conceptual model, an EntitySet is a wrapper for a particular set of Entity types. For example, the Customers Entity Set is a wrapper for Customer entities. If a type derives from the Customer entity, the Customers entity set wraps that type as well.

When writing queries, you do so by querying against this wrapper, for example:

IQueryable<BikeRoutebikeRoutes =
  from in context.BikeRoutes 
  where b.Miles 40 
  select b;

This query tells the context to look at all of the entities that are represented by BikeRoutes and return only those with a particular distance.

In EFv1, the code generation creates a method based on each EntitySet defined in the model. These methods return an ObjectQuery<T> of the requested type.

public global::System.Data.Objects.
   ObjectQuery<BikeRouteBikeRoutes
{
 get
 {
  if ((this._BikeRoutes == null))
  {
   this._ BikeRoutes = base.
    CreateQuery<BikeRoute>("[ BikeRoutes]");
  }
  return this._BikeRoutes;
 }
}

In EF4, this method is now designed to return an ObjectSet<T> rather than an ObjectQuery<T>. An ObjectSet is a special type of ObjectQuery that provides functionality to work directly with the entities that the set is wrapping. Its additional features make it something like a subset of the ObjectContext. With ObjectSet, you can Add, Attach, Detach and search Entities that are managed by the context, but you will only be working against the subset of entities that are wrapped by that ObjectSet. This gives you a number of benefits. In EFv1, many of the manipulations require identifying the type of entity set as a string, which is a great source of typos.

When writing a query, you won’t notice a difference. However, the ability to focus on the ObjectSet will help with functions such as Attaching and Adding entities to the context.

For example, in EFv1, the syntax for adding a new entity to the context requires you to use a string to identify which EntitySet the entity belongs to.

context.AddObject
  ("RidingEntities.BikeRides", newRide);

Any place you need to use a string when coding is painful. The code generator does help by creating a special AddTo method for each entity set in the model. So you would find methods such as context.AddToPeople, AddToBikeRoutes or AddToAddresses. This was a somewhat clunky solution because you could never call those methods generically.

With ObjectSet you can more easily perform this function because the ObjectSet comes pre-defined with the knowledge of which EntitySet you’re working with. Most importantly, everything is strongly typed.

context.BikeRides.AddObject(newRide);
context.BikeRoutes.AddObject(newRoute);

"
The IObjectSet<TEntity> interface will allow test-driven developers to mock the ObjectSet and build unit tests that can emulate querying and data updates.
"

 

ObjectSet gets its AddObject, Attach and DeleteObject methods from the new IObjectSet<T> interface. This interface will allow test-driven developers to mock the ObjectSet and build unit tests that can emulate querying and data updates. It will also provide more flexibility in a variety of other coding scenarios. See the sidebar, Resources, for more information.

Lazy Loading

Many ORMs provide a feature called lazy loading, which will automatically load related data as needed. Entity Framework v1 does have a Load method for reference data which must be called explicitly. For example, if you were to query BikeRoutes without specifying that the query should also return the related BikeRides; there would be no knowledge of BideRides in memory. Each BikeRoute would appear to have zero related BikeRides.

For a given route, you could call thisBikeRoute.BikeRides.Load() to force the related rides to be retrieved from the database. This is referred to as explicit deferred loading and allows the developer to have total control over when calls to the database are made.

Alternatively, many other ORMs, including LINQ to SQL, will implicitly load the related data whenever it is needed. To developers who are used to lazy loading, having to explicitly call Load to get at the related data is a big drawback of EFv1. EF4 adds the capability to implicitly load data. The new ContextOptions.DeferredLoadingEnabledproperty of ObjectContext provides this feature. This property, which is false by default, allows you to instruct an instantiated context to lazy load related data on demand.

In an e-commerce application, if you queried a Customer without eager loading all of the Orders for that Customer, setting DeferredLoadingEnabled to true will ensure that the Orders and any other data gets retrieved from the database as needed.

The following query only returns a Customer entity but the foreach enumeration requires the orders along with even more related data, Salesperson and Person.

context.ContextOptions
  .DeferredLoadingEnabled = true;

Customer aCust = context.Customers
  .Where(c =c.Orders.Any())
  .FirstOrDefault();

foreach (Order order in aCust.Orders)
{
  Console.WriteLine
   ("Order #: {0}, SalesPerson: {1}",
    order.OrderNumber,
    order.SalesPerson.Person.LastName);
}

Because DeferredLoadingEnabled is set to true, Entity Framework will execute additional queries to satisfy the requested related data. In the case of this example above, as the enumeration steps through each Customer, a query is executed to load the related Orders. Then for each order, an additional query is executed to retrieve the SalesPerson and Person data for that Order.

Be aware that this means one additional trip to the database for each Customer. If there are ten customers, then twenty additional calls to the database will be executed. If there are 150 customers, then you are causing 300 additional database trips to be made. There are differing opinions on when to use lazy loading. I prefer to consider each scenario and determine what will provide the best performance in my application. Then I can choose between eager loading with the Include method, explicit deferred loading where I have to call the Load method or lazy loading with DeferredLoadingEnabled set to true.

The new lazy loading feature is tied to the instantiated context, which you are currently using. Some other ORMs allow you to define the lazy loading based on the related properties; for example, the Orders property of Customer might be defined to always lazy load. While this new feature will satisfy many developers who are looking for lazy loading, it’s helpful to be aware of this difference.

posted @ 2010-06-22 07:09  i'm zjz  阅读(320)  评论(0编辑  收藏  举报