C# 实体框架包含在Where子查询中
我相信我在这里遗漏了一些明显的东西,我想不出来。我收到一个例外: LINQ to实体无法识别方法“System.LINQ.IQueryable1[Privilege]包括[Privilege,PrivilegeGetType]System.LINQ.IQueryable1[Privilege],System.LINQ.Expressions.Expression1[System.Func2[Privilege,PrivilegeGetType]]”方法,并且无法将此方法转换为存储表达式 运行此EF查询时:C# 实体框架包含在Where子查询中,c#,entity-framework,linq,C#,Entity Framework,Linq,我相信我在这里遗漏了一些明显的东西,我想不出来。我收到一个例外: LINQ to实体无法识别方法“System.LINQ.IQueryable1[Privilege]包括[Privilege,PrivilegeGetType]System.LINQ.IQueryable1[Privilege],System.LINQ.Expressions.Expression1[System.Func2[Privilege,PrivilegeGetType]]”方法,并且无法将此方法转换为存储表达式 运行此E
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);
}
}
下面是我想做的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))
)
))
您当前的查询实际上没有执行与不同实体相关的任何类型的筛选:
return _context.Categories.Where(c =>
_context.Privileges
.Include(p => p.PrivilegeType)
Where(p => p.PrivilegeType.Code == "code").Any()).ToList();
如果数据库中存在PrivilegeType.code值为code的特权实体,则此代码将转到数据库并获取数据库中的所有类别。它不过滤任何内容,只返回所有类别
正如@dcg在评论中提到的,您在第一个lambda表达式中没有使用c变量,我确信这是您作为c对象接收到的实际异常的原因,在这种情况下,上下文不能传递到像这样的where表达式中
决议如下:
_context.Categories.Where(c => c.Privileges.Any(p => p.PrivilegeType.Code == "code")).ToList();
这假定类别和权限之间存在外键关系。如果不这样做,则需要重新构造这两个表的关联方式,或者提供有关您试图使用什么连接这两个实体的更多详细信息
编辑:
在您的帖子中澄清“是”之后,上面的查询就是您要查找的,但是您需要实际映射类别和特权实体之间的关系。在类别实体映射配置上执行以下操作:
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
问题是:
正如评论中所说,类别和特权之间没有外键关系。存在一种软关系:Privilege.ObjectID指向表的主键。问题是您的子查询没有关联类别和特权,因为没有使用CategoryID。对于任何类别,条件要么为真,要么为假
“包含”仅在填充查询结果中实体的导航属性时有效。换句话说,Privileges.Includep=>p.privilegegetype仅在返回特权时有效。除此之外,不能过滤包含项,因此它们不能用作过滤条件
所以首先要做的是:匹配CategoryID。任何这种性质的查询都应该像
_context.Categories
.Where(c => !_context.Privileges
.Any(p => p.ObjectId == c.CategoryId
&& ...))
第二种方法是以允许对其进行筛选的方式使用PrivilegeType:p.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)))
);
您是否映射了外键关系?你能给我们看看你的实体代码和映射代码吗?你不应该在lambda中的任何地方使用c吗?@RobertPetz我在编辑中添加了映射代码。你为什么在where表达式中使用_context.Privileges?你不能这样做,你想做什么?也许可以试着找一个关于查询相关实体的教程?是的,那么根据实体框架,两者之间没有关系?然后,您必须在Where.Tanks中手动加入这两个,以便快速回复!我更详细地更新了我上面的问题。@INVARZIM我更新了我的答案-我没有直接针对数据库运行此操作,因此如果有任何运行时错误,请告诉我,但它应该为您指出正确的方向来执行您试图执行的操作。嗯,我理解FulentApi创建两个实体之间的虚拟关系,谢谢你教我怎么做;然而,如何将上面的SQL查询转换成我所需要的,我正在努力。你有什么建议吗?@investrzim我更新了我的答案,我相信这将重新创建你的查询-如果没有,至少应该给你一个合适的跳转开始。请注意,我没有使用我最初的回答中提到的关系——这是因为你的问题本质上很奇怪,它实际上不适合这种关系。非常感谢你花时间和精力帮助我。如果
联接中的objectId不是静态的,但我需要category表中的category Id。格茨阿诺德提出了一个对我有用的问题:太棒了,这就像一个魅力!我不知道includes不能用在where子句中。我仍然在思考EF,谢谢你的详细回答。随机附带问题,你知道有什么好方法可以将这个where条件重新用于其他实体,不仅仅是类别,还有表a,B,您必须在泛型方法中构建表达式,该方法使用泛型类型参数来构建lambda表达式的参数部分。如果你需要更具体的帮助,你最好问一个新问题。
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)))
);