Entity Framework - 3. 基本操作 (Relationship)
[ 2009-03-27 15:53:39 | 作者: yuhen ]
EF 关联操作和 LINQ to SQL 类似,使用起来很简便。
System.Data.Objects.DataClass.EntityCollection<TEntity> 为关联关系的 "Many" 端。
System.Data.Objects.DataClass.System.EntityReference<TEntity> 为 "One or Zero" 端。注意, Order.User 其实是 Order.UserReference.Vaue 的一种 "快捷方式"。
1. Add
2. Load
EF 默认使用 Deferred Loading 机制,虽然同样是 "Lazy Loading",但和 LINQ to SQL 却有很大区别。LINQ to SQL 是一种 "Implicit Lazy Loading",当访问关联属性时,框架会自动在后台完成相应的查询和载入操作。而 EF 则是一种 "Explicit Lazy Loading",除非我们显式执行载入操作,否则框架并不会自动帮我们获取关联数据。
通过下面的演示,你会发现关联数据是 "空"。
输出:
解决方法很简单,就是我们要在代码中明明白白告诉 EF,我要载入关联数据。下面给出了不同的 "载入" 方法。
对于 LINQ to Entities 而言,我们可以直接用匿名类型同时返回关联数据。
另外一种做法就是显式调用 EntityReference<TEntity>.Load() 或 EntityCollection<TEntity>.Load()。
输出:
在循环中每次调用 Load 方法都会执行一次后台数据库查询操作,必然会导致一定的性能损失。我们可以改用 Eager Loading 模式来解决这个问题。
输出:
Include() 是 ObjectQuery<T> 的方法,因此也可以用于 Entity SQL。
除此之外,Entity SQL 本身也可以实现类似的功能。
某些时候使用 CreateQuery<DbDataReader>() 会出现如下异常,那么改成 IExtendedDataRecord 即可。
附: Include 并不适用所有场合,因为该操作可能会一次返回 "太多" 的记录,反而导致性能的降低。编码人员要根据具体的需求,合理使用 Include() 和 Load()。
3. Remove
4. Clear
EntityCollection<TEntity>.Clear() 循环调用 Remove() 来删除列表中的关联对象。
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=791
[EdmEntityType(NamespaceName = "TestModel", Name = "User")]
public partial class User : EntityObject
{
...
[EdmRelationshipNavigationProperty("TestModel", "UserGroup", "Group")]
public EntityCollection<Group> Groups
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedCollection<Group>("TestModel.UserGroup", "Group");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)(this)).RelationshipManager.
InitializeRelatedCollection<Group>("TestModel.UserGroup", "Group", value);
}
}
}
}System.Data.Objects.DataClass.EntityCollection<TEntity> 为关联关系的 "Many" 端。
[EdmEntityType(NamespaceName = "TestModel", Name = "Order")]
public partial class Order : EntityObject
{
...
[EdmRelationshipNavigationProperty("TestModel", "FK_Order_User", "User")]
public User User
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<User>("TestModel.FK_Order_User", "User").Value;
}
set
{
((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<User>("TestModel.FK_Order_User", "User").Value = value;
}
}
public EntityReference<User> UserReference
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<User>("TestModel.FK_Order_User", "User");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)(this)).RelationshipManager.
InitializeRelatedReference<User>("TestModel.FK_Order_User", "User", value);
}
}
}
}System.Data.Objects.DataClass.System.EntityReference<TEntity> 为 "One or Zero" 端。注意, Order.User 其实是 Order.UserReference.Vaue 的一种 "快捷方式"。
1. Add
using (var context = new TestEntities())
{
var user = context.Users.First(u => u.Name == "user1");
// One to Many
var order = new Order { Name = "Order3" };
user.Orders.Add(order);
// Many to Many
var group2 = context.Groups.First(g => g.Name == "group2");
user.Groups.Add(group2);
// Save
context.SaveChanges();
}2. Load
EF 默认使用 Deferred Loading 机制,虽然同样是 "Lazy Loading",但和 LINQ to SQL 却有很大区别。LINQ to SQL 是一种 "Implicit Lazy Loading",当访问关联属性时,框架会自动在后台完成相应的查询和载入操作。而 EF 则是一种 "Explicit Lazy Loading",除非我们显式执行载入操作,否则框架并不会自动帮我们获取关联数据。
通过下面的演示,你会发现关联数据是 "空"。
// Many to One
using (var context1 = new TestEntities())
{
var order = (from o in context1.Orders where o.User.Name == "user1" select o).FirstOrDefault();
Console.WriteLine(order.User == null);
}
// Many to Many, LINQ to Entities
using (var context2 = new TestEntities())
{
var q = from u in context2.Users select u;
foreach (var user in q)
{
Console.WriteLine(user.Name);
foreach (var group in user.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}
// Many to Many, Entity SQL
using (var context3 = new TestEntities())
{
var sql = "SELECT VALUE u FROM TestEntities.Users AS u";
var users = context3.CreateQuery<User>(sql);
foreach (var user in users)
{
Console.WriteLine(user.Name);
foreach (var group in user.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}输出:
true user1 user2 user1 user2
解决方法很简单,就是我们要在代码中明明白白告诉 EF,我要载入关联数据。下面给出了不同的 "载入" 方法。
对于 LINQ to Entities 而言,我们可以直接用匿名类型同时返回关联数据。
using (var context = new TestEntities())
{
var q = from u in context.Users select new { User = u, Groups = u.Groups };
foreach (var item in q)
{
var user = item.User;
Console.WriteLine(user.Name);
foreach (var group in user.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}另外一种做法就是显式调用 EntityReference<TEntity>.Load() 或 EntityCollection<TEntity>.Load()。
// One to Many
using (var context1 = new TestEntities())
{
var user = (from u in context1.Users where u.Name == "user1" select u).FirstOrDefault();
user.Orders.Load();
foreach (var item in user.Orders)
{
Console.WriteLine(item.Name);
}
}
// Many to One
using (var context2 = new TestEntities())
{
var order = (from o in context2.Orders where o.User.Name == "user1" select o).FirstOrDefault();
order.UserReference.Load();
Console.WriteLine(order.User.Name);
}
// Many to Many
using (var context3 = new TestEntities())
{
var users = from u in context3.Users select u;
foreach (var item in users)
{
Console.WriteLine(item.Name);
item.Groups.Load();
foreach (var group in item.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}输出:
Order1
Order2
Order3
user1
user1
Group1
Group2
user2
Group1在循环中每次调用 Load 方法都会执行一次后台数据库查询操作,必然会导致一定的性能损失。我们可以改用 Eager Loading 模式来解决这个问题。
using (var context = new TestEntities())
{
// var users = from u in context.Users.Include("Groups") where u.Groups.Count > 1 select u;
var users = from u in context.Users.Include("Groups") select u;
Console.WriteLine((users as ObjectQuery<User>).ToTraceString());
foreach (var user in users)
{
Console.WriteLine(user.Name);
foreach (var group in user.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}输出:
SELECT
[Project2].[Id] AS [Id],
[Project2].[Name] AS [Name],
[Project2].[Age] AS [Age],
[Project2].[C1] AS [C1],
[Project2].[C2] AS [C2],
[Project2].[Id1] AS [Id1],
[Project2].[Name1] AS [Name1]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age],
1 AS [C1],
[Project1].[Id] AS [Id1],
[Project1].[Name] AS [Name1],
[Project1].[C1] AS [C2]
FROM [dbo].[User] AS [Extent1]
LEFT OUTER JOIN (SELECT
[Extent2].[UserId] AS [UserId],
[Extent3].[Id] AS [Id],
[Extent3].[Name] AS [Name],
1 AS [C1]
FROM [dbo].[UserGroup] AS [Extent2]
INNER JOIN [dbo].[Group] AS [Extent3] ON [Extent3].[Id] = [Extent2].[GroupId] )
AS [Project1] ON [Extent1].[Id] = [Project1].[UserId]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C2] ASCInclude() 是 ObjectQuery<T> 的方法,因此也可以用于 Entity SQL。
using (var context = new TestEntities())
{
var sql = "SELECT VALUE u FROM TestEntities.Users AS u";
var users = context.CreateQuery<User>(sql).Include("Groups");
foreach (var user in users)
{
Console.WriteLine(user.Name);
foreach (var group in user.Groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}除此之外,Entity SQL 本身也可以实现类似的功能。
using (var context = new TestEntities())
{
var sql = "SELECT u, u.Groups FROM TestEntities.Users AS u";
var reader = context.CreateQuery<IExtendedDataRecord>(sql);
Console.WriteLine(reader.ToTraceString());
foreach (var item in reader)
{
var user = item[0] as User;
var groups = item[1] as IList<Group>;
Console.WriteLine(user.Name);
foreach (Group group in groups)
{
Console.WriteLine("\t{0}", group.Name);
}
}
}某些时候使用 CreateQuery<DbDataReader>() 会出现如下异常,那么改成 IExtendedDataRecord 即可。
附: Include 并不适用所有场合,因为该操作可能会一次返回 "太多" 的记录,反而导致性能的降低。编码人员要根据具体的需求,合理使用 Include() 和 Load()。
3. Remove
using (var context = new TestEntities())
{
var user = context.Users.
Where("it.Name = @name", new ObjectParameter("name", "user1")).
Include("Groups").
FirstOrDefault();
foreach (var group in user.Groups)
{
Console.WriteLine(group.Name);
}
var group2 = context.Groups.FirstOrDefault(g => g.Name == "group2");
user.Groups.Remove(group2);
context.SaveChanges();
}4. Clear
EntityCollection<TEntity>.Clear() 循环调用 Remove() 来删除列表中的关联对象。
using (var context = new TestEntities())
{
var user = context.Users.
Where("it.Name = @name", new ObjectParameter("name", "user1")).
Include("Groups").
FirstOrDefault();
user.Groups.Clear();
context.SaveChanges();
}
[最后修改由 yuhen, 于 2009-03-28 09:51:56]
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=791
这篇日志没有评论。








