LINQ To Entities 的 Include + Where 方法

16

我有一个NxN的表,想象一下:

User(id,...)< - UserAddresses(id,userId,addressId,enabled,...) - > Addresses(id,...)

UserAddresses包含对用户和地址的外键。据我所知,由Entity Framework创建的实体User包含对UserAddresses的集合。Address包含对UserAddresses的集合,并且特定的UserAddress包含对User和Address的引用。

现在,我想通过LINQ进行下一个查询。 针对特定用户ID,仅获取已启用标志设置为true的userAddresses。 对于特定用户ID,userAddresses可以包含多个条目,但只有一个为该特定用户设置。

我可以执行以下查询:

context.User.Include( x => x.UserAddresses )
            .Include( x => x.UserAddresses.Select(y => y.Address) )
            .Single( x => x.id == USER_ID )

但我真正想要的不是加载该用户的所有UserAddresses... 而只是包含enabled设置为TRUE的那个地址!

有人能帮我编写这个查询吗?


你使用的是哪种 Include?据我所知,System.Data.Objects.ObjectQueryInclude 方法只接收一个字符串参数。 - King King
@KingKing,我已经编写了一个扩展方法,将Lambda表达式格式化为字符串,并将其传递给“本地”的ObjectQuery include方法。 - gds03
如果是这样,你问题的答案就是“不可能”,Include并不是那么强大,你无法在包含之前执行任何filter - King King
2个回答

16

在 EF 中没有部分加载关联属性的方法。尝试使用匿名类型选择所需内容:

var result = context.User
   .Where(u => u.Id == userId)
   .Select(u => new {
       Addresses = u.UserAddresses.Select(ua => ua.Address)
            .Where(a => a.Enabled),
       User = u // if you need this as well 
   })
   .Single();

这不会加载result.User.UserAddresses,但是result.Addresses将拥有你需要的一切。

如果你真的想把所有内容作为User类的一部分返回,你需要分离result.User,然后更新result.User.UserAddresses指向result.Addresses。


我需要返回包含所有相关内容的用户实体,但不是所有映射的集合。所以我认为匿名类型不对:/ - gds03
@goncaloRD:不幸的是,使用“Include”时不可能使用“where”条件。这是您最好的选择。 - Ocelot20
@ChaseMedallion 谢谢,这正是我需要的。查询已经完美地完成了,EF对象也按照我想要的方式进行了映射。 我创建了一个类型来对我的对象进行类型化,以便在我的业务层中使用。 - gds03

1

另一种可选方案是使用Load()而不是Include()

var foundUser = context.User.Single(x => x.Id == USER_ID);

context.Entry(foundUser).Collection(u =>
u.UserAddresses).Query().Where(userAddress =>
userAddress.Enabled).Load();

请记住,在某些情况下,EF可能会忽略Load()方法:
  1. 如果您正在使用EF和延迟加载功能,则获取对象将带有所有已在类中标记为Virtual的相关集合。因此,通过执行context.User.Single(x => x.id == USER_ID);,您将获得与用户关联的所有UserAddresses,除非您从User类的属性中删除Virtual关键字以关闭懒加载。

  2. 如果您在程序中添加/删除UserAddresses集合,并在不释放上下文的情况下调用context.SaveChanges();,则下次加载User对象时,UserAddresses集合将从EF上下文缓存而不是从DB(最新更改)中加载。在这种情况下,您需要在从上下文获取User之前处理上下文并实例化一个新上下文。例如,如果您的User具有5个项目的UserAddresses集合,并且您使其中一个项目无效(item.Enabled = false),然后调用context.SaveChanges()而不释放上下文,则下次从同一上下文获取User对象时,它已经具有来自上下文缓存的5个项目的集合,并且忽略了您的Load()方法。

PS:

如果满足以下所有条件,则开启Lazy Loading特性:

  1. context.Configuration.LazyLoadingEnabled = true;
  2. context.Configuration.ProxyCreationEnabled = true;
  3. 在您的User类中,UserAddresses已被定义为虚拟的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接