实体框架包含在子查询中

时间:2017-06-15 17:02:36

标签: c# entity-framework linq

我相信我错过了一些明显的东西,我无法理解。我接受了例外:

  

LINQ to Entities无法识别方法'System.Linq.IQueryable 1[Privilege] Include[Privilege,PrivilegeType](System.Linq.IQueryable 1 [Privilege],System.Linq.Expressions.Expression 1[System.Func 2 [Privilege,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条件是我想放入查询以限制将返回的记录数量的东西。我正在更新遗留数据库,并尝试匹配他们现有的数据库以保持尽可能接近。

以下是我正在尝试的完整查询:

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))
            )
    ))

2 个答案:

答案 0 :(得分:1)

问题:

  1. 正如评论中所述,CategoryPrivilege之间没有外键关系。存在一种软关系:Privilege.ObjectID指向 a 表的 主键。问题是您的子查询与类别和权限无关,因为CategoryID未被使用。 任何类别的条件为true或false。

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

  3. 所以要做的第一件事就是:匹配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)))
             );
    

答案 1 :(得分:0)

您当前的查询实际上并未执行与不同实体相关的任何类型的过滤:

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

如果数据库中的Categories个实体的Privilege值为PrivilegeType.Code,则此代码将转到数据库并获取数据库中的所有code Categories。这不会过滤任何内容,只会返回所有 c

同样如@dcg的评论中所述,您没有在第一个lambda表达式中使用_context变量,我相信这是您作为C#对象接收的实际异常的原因(在此case _context.Categories.Where(c => c.Privileges.Any(p => p.PrivilegeType.Code == "code")).ToList(); )无法传递到where表达式。

决议将是:

Categories

这假设您在PrivilegesCategories之间存在外键关系。如果不这样做,那么您将需要重新构建这两个表的关联方式,或者提供有关您尝试使用这两个实体加入的更多详细信息。

编辑:

在您的帖子中澄清之后,上述查询是您要查找的内容,但您需要实际映射PrivilegesCategories实体之间的关系。在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); } } 实体映射配置中执行以下操作:

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

这将允许您在Category和Privilege之间定义1- *的关系,而不要求Privilege实际上具有任何类型的关系回到Category。作为结果,您将能够编写这样的复杂查询。

编辑:

我试图重新创建您的SQL查询(虽然我必须从上面代码的结构构建我自己的本地上下文,但它可能不完全准确)

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

 import {
  StackNavigator,
} from 'react-navigation'
import SignupForm2 from './SignupForm2'
const App = StackNavigator({
  SignupForm2: { screen: SignupForm2 },
});
export default class SignupForm extends Component {

render () {
 const { navigate } = this.props.navigation
return (
 <Button
        title="Go to Jane's profile"
        onPress={() =>
          navigate('SignupForm2', { name: 'SignupForm2' })
        }
      />
  )
  }
}