2017-06-15 59 views
0

我相信我在这里错过了一些明显的东西,我无法弄清楚。我receieve除外:实体框架包含在哪里子查询

LINQ实体无法识别方法 'System.Linq.IQueryable 1[Privilege] Include[Privilege,PrivilegeType](System.Linq.IQueryable 1 [特权],System.Linq.Expressions.Expression 1[System.Func 2 [特权,PrivilegeType]])' 方法,并且此方法不能转换为商店表达式。

当我运行此EF查询:

return _context.Categories.Where(c => 
      _context.Privileges 
       .Include(p => p.PrivilegeType) 
       .Where(p => p.PrivilegeType.Code == "code").Any()).ToList(); 

最终的结果,我追求的是确定用户是否具有基于的categoryId特权。目前,我已经省略了Any()子句中的条件来简化语句。

这种类型的查询可能吗?

编辑: 这里是FulentApi配置:

public class PrivilegeConfiguration : EntityTypeConfiguration<Privilege> 
{ 
    public PrivilegeConfiguration() 
    { 
     ToTable("ObjectPrivileges"); 

     HasKey(p => new { p.ObjectTypeId, p.ObjectId, p.PrivilegeTypeId }); 

     Property(p => p.ObjectTypeId) 
      .HasColumnName("ObjectType") 
      .HasMaxLength(2); 

     Property(p => p.PrivilegeTypeId) 
      .HasColumnName("PrivilegeID") 
      .IsRequired(); 

     HasRequired(p => p.PrivilegeType); 
    } 
} 

public class PrivilegeTypeConfiguration : EntityTypeConfiguration<PrivilegeType> 
{ 
    public PrivilegeTypeConfiguration() 
    { 
     ToTable("PrivilegeType"); 

     HasKey(p => p.PrivilegeTypeId); 

     Property(p => p.PrivilegeTypeId) 
      .HasColumnName("PrivilegeID"); 

     Property(p => p.ObjectTypeId) 
      .HasColumnName("ObjectType") 
      .HasMaxLength(2); 

     Property(p => p.Code) 
      .HasMaxLength(50); 

     Property(p => p.Description) 
      .HasMaxLength(255); 
    } 
} 

编辑2:

WHERE条件是我想拖放到查询来限制的记录数那会回来。我正在更新遗留数据库,并尝试匹配他们目前已有的数据库,以使其尽可能接近。

这里是我attemping全面查询:

var query = _context.Categories.AsQueryable(); 

     if (!string.IsNullOrEmpty(privilege)) 
     query = query.Where(c => (!_context.Privileges 
     .Include(p => p.PrivilegeType) 
     .Any(p => p.PrivilegeValue == 1 
      && p.PrivilegeType.Code == privilege 
      && p.ObjectTypeId == objectTypeId 
      && p.ObjectId == c.CategoryId)) 
     || 
     _context.Privileges 
      .Include(p => p.PrivilegeType) 
      .Any(p => p.PrivilegeValue == 1 
       && p.PrivilegeType.Code == privilege 
       && p.ObjectTypeId == objectTypeId 
       && p.ObjectId == c.CategoryId 
       && (p.UserId == userId || _context.UserGroups.Select(ug => ug.UserID).Contains(userId)))); 

这里是我想要做什么SQL表示:

SELECT * FROM Categories WHERE(
(NOT EXISTS 

    (
     SELECT P.ObjectType 
     FROM ObjectPrivileges P 
      INNER JOIN PrivilegeType PT ON P.PrivilegeID = PT.PrivilegeID 
     WHERE 
      (
       P.PrivilegeValue > 0 
       AND PT.Code ='code' 
       AND P.ObjectType = 'SELECT' 
       AND P.ObjectID = 1 -- CategoryId 
      ) 
    ) 
) OR 

EXISTS 
    (
     SELECT P.ObjectType 
     FROM ObjectPrivileges P 
      INNER JOIN PrivilegeType PT ON P.PrivilegeID = PT.PrivilegeID 
     WHERE 
      (
       P.PrivilegeValue > 0 
       AND PT.Code = 'code' 
       AND P.ObjectType = 'SELECT' 
       AND P.ObjectID = 1 -- CategoryId 
       AND (P.UserID = 57 OR P.GroupID IN (SELECT GroupID FROM Group_User WHERE UserID = 57)) 
      ) 
    )) 
+0

你映射的外键关系?你能告诉我们你的实体代码和映射代码吗? –

+2

你不应该在你的lambda中的任何地方使用'c'吗? – dcg

+0

@RobertPetz我在编辑中添加了映射代码。 – InvaderZim

回答

1

的问题:

  1. 至于说在评论中,有CategoryPrivilege之间没有外键关系。有一个软关系:Privilege.ObjectID指向a主键a表。问题是你的子查询没有涉及类别和权限,因为没有使用CategoryID任何类别的条件是真或假。

  2. Include只有在查询结果中填充实体的导航属性时才有效。换言之,Privileges.Include(p => p.PrivilegeType)仅在返回Privileges时有效。除此之外,Include s不能被过滤,因此它们不能用作过滤条件。

所以首先要做的是:匹配CategoryID。这种性质的任何查询应该像...

_context.Categories 
     .Where(c => !_context.Privileges 
          .Any(p => p.ObjectId == c.CategoryId 
            && ...)) 

第二这是一种方式,允许它被过滤使用PrivilegeTypep.PrivilegeType.Code == "code"

应用这些修补程序,整个查询会是这样的:

var userId = 57; 
return _context.Categories 
    .Where(c => 
     !_context.Privileges 
       .Any(p => p.ObjectId == c.CategoryId 
         && p.PrivilegeValue > 0 
         && p.PrivilegeType.Code == "code" 
         && p.ObjectType = 'SELECT') 
    || _context.Privileges 
       .Any(p => p.ObjectId == c.CategoryId 
         && p.PrivilegeValue > 0 
         && p.PrivilegeType.Code == "code" 
         && p.ObjectType = 'SELECT' 
         && (p.UserId == userId 
          || _context.GroupUsers 
             .Any(gu => gu.UserId == userId 
               && gu.GroupID == p.GroupID))) 
     ); 
+0

真棒,这就像一个魅力!我没有意识到包含在where子句中不能使用。我仍然在EF的周围缠着头,谢谢你的详细解答。 – InvaderZim

+0

随机问题,你会知道一个很好的方式来重用这个条件的其他实体,而不仅仅是类别,但表“A”,“B”,“C”等? – InvaderZim

+0

您必须在使用泛型类型参数构建lambda表达式的参数部分的泛型方法中构建表达式。如果你需要更多的具体帮助,你最好问一个新问题。 –

0

您当前的查询不实际执行任何种类的过滤相关的不同实体:

return _context.Categories.Where(c => 
       _context.Privileges 
       .Include(p => p.PrivilegeType) 
       Where(p => p.PrivilegeType.Code == "code").Any()).ToList(); 

此代码将转到数据库并如果数据库中存在任何Privilege实体,并且PrivilegeType.Code的值为code,则在数据库中获取全部Categories。这不会过滤任何东西,它只是返回全部Categories

也如@dcg的评论中所述,您在第一个lambda表达式中没有使用c变量,我相信这是您作为C#对象接收的实际异常的原因(在本例中为_context)不能传递到这样的表达式。

分辨率会是这样:

_context.Categories.Where(c => c.Privileges.Any(p => p.PrivilegeType.Code == "code")).ToList(); 

这里假设你有CategoriesPrivileges之间的外键关系。如果你不这样做,那么你将需要重构这两张表格的相关性,或者提供关于你试图加入这两个实体的更多细节。

编辑:

后您澄清在您的文章是以上查询是你在找什么,但实际上你需要映射你的CategoriesPrivileges实体之间的关系。在您Categories实体映射配置做到这一点:

public class Category 
{ 
    public int CategoryId { get; set; } 
    public int PrivilegeId { get; set; } 

    public virtual ICollection<Privilege> Privilege { get; set; } 
} 

public class Privilege 
{ 
    public int PrivilegeId { get; set; } 
    public int ObjectId { get; set; } 
} 

public class CategoryMap : EntityTypeConfiguration<Category> 
{ 
    public CategoryMap() 
    { 
     ToTable("Categories"); 
     HasKey(x => x.CategoryId); 

     HasMany(x => x.Privilege) 
      .WithMany() 
      .Map(x => 
      { 
       x.MapLeftKey(nameof(Category.PrivilegeId)); 
       x.MapRightKey(nameof(Privilege.ObjectId)); 
      }); 
    } 
} 

public class PrivilegeMap : EntityTypeConfiguration<Privilege> 
{ 
    public PrivilegeMap() 
    { 
     ToTable("Categories"); 
     HasKey(x => x.PrivilegeId); 
    } 
} 

这将允许您定义的关系是1 *类别和权限之间,而不需要特权实际上有任何一种关系回到范畴。您将能够编写像这样的复杂查询作为结果。

编辑:

我试图重新创建SQL查询(尽管我必须建立从您上面的代码结构我自己当地的情况,它可能不完全准确)

相信这将生成与您的查询相同的结果集。它可能不会生成相同的SQL,但结果应该是相同的。

public IQueryable<Category> Query(int userId) 
{ 
    var db = new Context(); 

    var groupUsers = db.GroupUsers.Where(x => x.UserId == userId).Select(gu => gu.GroupId); 

    var first = db.Privileges 
     .Join(db.PrivilegeTypes, p => p.PrivilegeTypeId, pt => pt.PrivilegeTypeId, (p, pt) => new { P = p, PT = pt }) 
     .Where(join => join.P.PrivilegeValue > 0 && 
         join.PT.Code == "code" && 
         join.P.ObjectTypeId == "SELECT" && 
         join.P.ObjectTypeId == "1"); 

    var second = first.Where(join => join.P.UserId == userId || groupUsers.Contains(join.P.UserId)); 

    return db.Categories.Where(c => first.All(join => join.P.ObjectId != c.CategoryId) || 
            second.Any(join => join.P.ObjectId == c.CategoryId)); 
} 

Query(57); // Look up UserID 57 
+0

快速回复坦克!我更详细地更新了我的问题。 – InvaderZim

+0

@InvaderZim我更新了我的答案 - 我没有直接对数据库运行这个,所以如果有任何运行时错误让我知道,但它应该指向你在正确的方向做你正在尝试做的事 –

+0

嗯,我了解FulentApi以创建两个实体之间的虚拟关系,感谢您向我展示如何做到这一点;然而,我正在努力如何将上面的SQL查询翻译成我需要的东西。你有什么建议吗? – InvaderZim