在实体框架中使用各种表实现“联接”

时间:2018-12-10 08:18:11

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

我有三个表:

材料

  1. ID
  2. 标题
  3. 内容

喜欢

  1. ID
  2. MaterialID
  3. 用户ID
  4. IsLiked

访问者

  1. ID
  2. 用户ID
  3. MaterialID
  4. 日期
  5. ReadNow

我想要一个像这样的对象

  1. 标题
  2. 内容
  3. CountLikes
  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()'无法翻译,将在本地进行评估。

2 个答案:

答案 0 :(得分:3)

如果您使用的是实体框架,请考虑使用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(),
   });

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

  • 标题是材料的标题
  • 内容是材料的内容
  • LikeCount是此材料的“赞”次数(具有真实的IsLiked)
  • NrOfVisitors是此材料的访问者数量(即...)

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

答案 1 :(得分:0)

如果数据库中有外键,那么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();

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