当前位置: 首页 > 编程语言 > C#

在EFCodeFirst中过滤导航属性Share

时间:2023-04-10 15:06:15 C#

在EFCodeFirst中过滤导航属性假设我有两个实体:publicclassFarm{....publicvirtualICollectionFruits{get;set;}}publicclassFruit{...}我的DbContext是这样的:publicclassMyDbContext:DbSet{....privateDbSetFarmSet{get;设置;}publicIQueryableFarms{get{return(fromfarminFarmSetwherefarm.owner==myownerselectfarm);}}}我这样做是为了让每个用户只能看到他的农场,我不必每次查询的位置都调用数据库。现在,我想过滤掉农场中的所有水果,我试过这个(在Farm类中):fromfruitinFruitswherefruit....selectfruit但结果查询不包含where子句,这非常重要因为我有数十万行,加载它们并将它们作为对象进行过滤效率不高。我读到延迟加载的属性在第一次访问时被填充,但它们读取所有数据并且不能应用过滤器,除非你做这样的事情:从db.Fruits中的水果中选择水果......选择水果但我不能那样做,因为Farm不知道DbContext(我认为它不应该(?)),对我来说,如果我必须处理所有数据而不仅仅是我的数据,它就会破坏使用导航属性的全部目的一个在农场。那么,我做错了什么/做出了错误的假设?有什么方法可以将过滤器应用于生成真实查询的导航属性?(我正在处理大量数据)感谢阅读!不幸的是,我认为您可能采取的任何方法都必须涉及摆弄上下文,而不仅仅是实体。如您所见,您不能直接筛选导航属性,因为它是一个ICollection而不是IQueryable,因此它会在您有机会应用任何筛选器之前立即加载。您可以做的一件事是在Farm实体中创建一个未映射的属性来保存过滤后的水果列表:publicclassFarm{....publicvirtualICollectionFruits{get;放;}[NotMapped]publicIListFilteredFruits{get;放;然后,在您的上下文/存储库中,添加一个方法来加载Farm实体并使用您想要的数据填充FilteredFruits:publicclassMyDbContext:DbContext{....publicFarmLoadFarmById(intid){Farmfarm=this.Farms.Where(f=>f.Id==id).Single();//或任何farm.FilteredFruits=this.Entry(farm).Collection(f=>f.Fruits).Query().Where(....).ToList();返回农场;}}...varmyFarm=myContext.LoadFarmById(1234);这应该只使用过滤后的集合填充myFarm.FilteredFruits,因此您可以在实体中随意使用它。但是,我自己从未尝试过这种方法,因此可能存在我没有想到的陷阱。一个主要缺点是它仅适用于使用此方法加载的农场,不适用于您在MyDbContext.Farms数据集上执行的任何常规LINQ查询。总而言之,我认为您尝试这样做的事实可能表明您在实体类中放置了太多业务逻辑,而实际上在不同的层中可能会更好。很多时候,最好将实体基本上视为数据库记录内容的容器,并将所有过滤/处理留给存储库或业务/显示逻辑所在的任何地方。我不确定您正在处理哪种应用程序,因此我无法提供任何具体建议,但这是需要考虑的事情。如果您决定移出Farm实体,一种非常常见的方法是使用预测:varresults=(fromfarminmyContext.Farmswhere....selectnew{Farm=farm,FilteredFruits=myContext.Fruits.Where(f=>f.FarmId==farm.Id&&...).ToList()}).ToList();...然后将生成的匿名对象用于您想做的任何事情,而不是尝试向Farm实体本身添加额外的数据。只是想我会为此添加另一个解决方案花了一些时间尝试将DDD原则附加到代码优先模型。搜索了一段时间后,我找到了一个像下面这样的解决方案,它对我有用。公共类FruitFarmContext:DbContext{publicDbSetFarms{get;放;}protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder){modelBuilder.Entity().HasMany(Farm.FruitsExpression).WithMany();}}publicclassFarm{publicintId{get;放;}受保护的虚拟ICollectionFruits{get;放;}publicstaticExpression>>FruitsExpression=x=>x.Fruits;publicIEnumerableFilteredFruits{get{//在水果集合上应用你想要的任何过滤器returnFruits.Where(x=>true);}}}publicclassFruit{publicintId{get;放;想法是不能直接访问农场水果集合,而是通过预过滤其属性来公开。这里的折衷是在设置映射时能够解析水果集合所需的静态表达式。我已经开始在许多我希望控制对对象子集合的访问的项目中使用这种方法。懒加载不支持过滤;使用过滤显式加载:Farmfarm=dbContext.Farms.Where(farm=>farm.Owner==someOwner).Single();dbContext.Entry(farm).Collection(farm=>farm.Fruits).Query().Where(fruit=>fruit.IsRipe).Load();显式加载方法需要两次往返数据库,一次用于主要数据,一次用于详细信息。如果坚持单个查询很重要,请使用预测:)//导致Farm.Fruit被预先加载}).Single().Farm;EF始终将导航属性绑定到它加载的实体。这意味着farm.Fruit将包含与匿名类型中的Fruit属性相同的过滤集合。(只需确保您没有将任何Fruit实体加载到应过滤掉的上下文中,如使用预测和存储库伪过滤的预加载中所述。)这就是C#学习教程的全部内容:在EF代码中过滤导航属性对你有用,需要多了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: