【问题标题】:Implement a "join" with a variety of tables in Entity Framework使用实体框架中的各种表实现“连接”
【发布时间】:2018-12-10 08:18:11
【问题描述】:

我有三张桌子:

材料

  1. 身份证
  2. 标题
  3. 内容

喜欢

  1. 身份证
  2. 材料 ID
  3. 用户 ID
  4. 喜欢

访客

  1. 身份证
  2. 用户 ID
  3. 材料 ID
  4. 日期
  5. 立即阅读

我想得到一个像这样的对象:

  1. 标题
  2. 内容
  3. 点赞数
  4. CountVisitors

我尝试执行以下操作:

from mat in ctx.materials
let visitors = mat.VisitorsCollection.Where(x=>x.ReadNow).Count()
let likes = mat.LikesCollection.Where(x=>x.IsLiked).Count()
let iliked = mat.LikesCollection.Where(x=>x.UserID == myID && x.IsLiked).Any()
select new {
   Material = mat,
   Visitors = visitors,
   Likes = likes,
   Liked = iliked
}

我选择了一些材料,实体框架分别接收访问者数量等数据。

我还尝试了以下方法:

from mat in ctx.materials
join lik in ctx.Likes.Where(x=>x.UserID == myID && x.IsLiked) on map.ID equals lik.MaterialID 
select new {
   Material = mat,
   Liked = lik.Any()
}

但现在出现错误:

Microsoft.EntityFrameworkCore.Query:警告:无法翻译 LINQ 表达式“Any()”,将在本地进行评估。

【问题讨论】:

  • 试试ctx.Likes.Any(x=>x.UserID == myID && x.IsLiked)是否有效。
  • @PeterB 感谢您的回答!不幸的是,如果我做 LikesCollection.Any (...) 那么什么都不会改变,我会收到很多单独的请求。
  • 问题/问题是什么?第一个查询看起来是 EF 预期/推荐的连接方式。客户评价/N+1 次查询?请更新帖子。还要指定确切的 EF Core 版本,因为它真的很重要。
  • @IvanStoev 感谢您的回答。我回答了先驱报的回答。拜托,我在那里写了问题所在。问题是我有上面指出的其他数据。结果,我收到了 100 个请求而不是 1 个。

标签: c# sql-server entity-framework linq entity-framework-core


【解决方案1】:

如果您使用实体框架,请考虑使用 ICollections,而不是自己执行连接。

您有一个Materials 序列,其中每个Material 有零个或多个Likes 和零个或多个Visitors,两者都是一对多关系,使用Material 的外键。

如果您关注entity framework code first conventions,您将获得类似于以下的课程

class Material
{
     public int Id {get; set;}
     public string Title {get; set;}
     public string Content {get; set;}

     // every Material has zero or more Likes (one-to-many)
     public virtual ICollection<Like> Likes {get; set;}

     // every Material has zero or more Visitors (one-to-many)
     public virtual ICollection<Visitor> Visitors {get; set;}
}

喜欢和访客:

class Like
{
     public int Id {get; set;}
     public bool IsLiked {get; set;}
     ...

     // every Like belongs to exactly one Material, using foreign key
     public int MaterialId {get; set;}
     public virtual Material Material {get; set;}
}

class Visitor
{
     public int Id {get; set;}
     ...

     // every Visitor belongs to exactly one Material, using foreign key
     public int MaterialId {get; set;}
     public virtual Material Material {get; set;}
}

这就是实体框架检测一对多关系所需的全部内容。您可能需要不同的表名或列的不同标识符。在这种情况下,需要属性或流畅的 API

在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多、多对多等)

一旦你的类定义正确,你的查询就很简单并且非常直观:

要求:

从我收集的材料中,给我每个材料的标题、内容、喜欢的数量和访问者的数量:

var result = myDbContext.Materials
   .Where(material => ...)            // only if you don't want all Materials
   .Select(material => new            // from every Material make one new object
   {                                  // containing the following properties
       Title = material.Title,
       Content = material.Content,

       // if you want any information of the likes of this material, use property Likes
       LikeCount = material.Likes
           .Where(like => like.IsLiked)  // optional, only if you don't want all likes
           .Count(),
       NrOfVisitors = material.Visitors
           .Where(visitor => ...)        // only if you don't want all visitors
           .Count(),
   });

换句话说:从我完整的材料集合中,只保留那些材料......从所有剩余的材料中,制作一个新对象:

  • Title 是 Material 的标题
  • Content 是 Material 的内容
  • LikeCount 是该材料的点赞数(具有真正的 IsLiked)
  • NrOfVisitors 是此材料的访问者数量(即...)

实体框架知道您的关系,并且知道需要 GroupJoin。

【讨论】:

  • 感谢您的回答!问题是我没有收到单独字段的数据,而是收到了整个对象的数据。告诉我,有没有可能以某种方式解决这个问题?恐怕以后当我有很多字段时,我会忘记将它们包含在此查询中,从而丢失重要数据。而且“select”中的每个依赖表都有一个额外的表是正常的吗?
  • 天哪!太多问题无法发表评论。关于缺少属性的问题。设计模式的原作者提出你应该“为变化而设计”,如果你让所有的用户直接与数据库通信,你会在变化后遇到问题。设计一个外观模式(谷歌为它),并让它成为对您的数据库的唯一访问。使用 Select new MyFacadeClass 代替 Select new ...。如果您的需求在需要不同属性的地方发生变化,您只需更改此面部类代码,并针对您的新需求进行一次测试
  • 感谢您的帮助!另一个小问题。 :) 那么随附的表格呢?一对多的关系特别有趣。
  • Google 我也不知道一对多的关系。你的意思是多对多吗?谷歌你会发现
  • 我有一张带有各种图像的桌子。图像表与材质表相关。所以对我来说,Material->Images->Different sizes
【解决方案2】:

如果你在数据库中有外键,那么 EF 会在对象之间生成链接,所以你需要做的就是:

var result = ctx.materials.Select(x => 
     new SomeClass{
          Material = x,
          Visitors = x.Visitors.Where(v => v.ReadNow).Count(),
          Likes = x.Likes.Where(y => y.IsLiked).Count(),
          Liked = x.Likes.Where(z => z.IsLiked && z.UserID == myID).Count()
     }).ToList();

语法可能并不完全正确,但你明白了......

【讨论】:

  • 感谢您的回答!此查询还为数据库中的每一行返回许多附加查询。
  • 我为访客添加了一个过滤器。ReadNow。它返回了哪些额外的查询以及您到底需要什么?我会尽力为你解决的
猜你喜欢
  • 1970-01-01
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 2017-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多