Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/73.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Linq到实体-是否可以在对DB的单个查询中实现这一点?_C#_Sql_Entity Framework_Linq To Entities - Fatal编程技术网

C# Linq到实体-是否可以在对DB的单个查询中实现这一点?

C# Linq到实体-是否可以在对DB的单个查询中实现这一点?,c#,sql,entity-framework,linq-to-entities,C#,Sql,Entity Framework,Linq To Entities,我有多个实体对象,希望通过使用存储在SQL Server数据库中的自定义访问列表ACL来保护这些对象。我的所有安全设备都实现了一个iCurable接口: public interface ISecurable { int ACLId { get; } ACL ACL { get; } } AccessList ACL实体如下所示: public class ACL { public int Id { get; set; } public virtual ICol

我有多个实体对象,希望通过使用存储在SQL Server数据库中的自定义访问列表ACL来保护这些对象。我的所有安全设备都实现了一个iCurable接口:

public interface ISecurable
{
    int ACLId { get; }
    ACL ACL { get; }
}
AccessList ACL实体如下所示:

public class ACL
{
    public int Id { get; set; }
    public virtual ICollection<ACE> ACEntries { get; set; }
}
public class MyDbContext : DbContext
{   
    public DbSet<User> Users { get; set; }
    public DbSet<Workgroup> Workgroups { get; set; }
    public DbSet<ACL> ACLs { get; set; }
    public DbSet<ACE> ACEntries { get; set; }
    public DbSet<SecurableClass> SecurableClassItems { get; set; }
}
public static IQueryable<ISecurable> FilterByACL2(this IQueryable<ISecurable> securables, User user, int requiredAction, MyDbContext db)
{
    var userId = user.Id;

    var workgroups = db.Entry(user).Collection(u => u.Workgroups).Query();

    return securables.Where(s =>
        s.ACL.ACEntries.Any(e =>
            (e.PrincipalId == userId || workgroups.Select(w => w.Id).Contains(e.PrincipalId)) &&
            (e.AllowedActions & requiredAction) == requiredAction));

}
可以将操作授予用户和工作组

Principal是一个抽象类,用户和工作组都从中继承:

public abstract class Principal
{
    public int Id { get; set; }
    public string DisplayName { get; set; }

    public virtual ICollection<ACE> ACEntries { get; set; }
}

public class User : Principal
{
    public string Email { get; set; }

    public virtual ICollection<Workgroup> Workgroups { get; set; }
}

public class Workgroup : Principal
{
    public virtual ICollection<User> Users { get; set; }
}
最后,我的问题是:我想写一个扩展方法,根据用户和所需的操作,通过ACL过滤掉我所有的ISecurable类。我想对数据库进行一次查询:

    public static IQueryable<ISecurable> FilterByACL(this IQueryable<ISecurable> securables, User user, int requiredAction)
    {
        var userId = user.Id;

        return securables.Where(s =>
            s.ACL.ACEntries.Any(e =>
                (e.PrincipalId == userId || user.Workgroups.Select(w => w.Id).Contains(userId)) &&
                (e.AllowedActions & requiredAction) == requiredAction));

    }
有没有办法处理这种情况?很抱歉提出这么长的问题,但现有的简化代码可能最能解释我的意图

使现代化 在@vittore建议之后,我的扩展方法可能是这样的:

public class ACL
{
    public int Id { get; set; }
    public virtual ICollection<ACE> ACEntries { get; set; }
}
public class MyDbContext : DbContext
{   
    public DbSet<User> Users { get; set; }
    public DbSet<Workgroup> Workgroups { get; set; }
    public DbSet<ACL> ACLs { get; set; }
    public DbSet<ACE> ACEntries { get; set; }
    public DbSet<SecurableClass> SecurableClassItems { get; set; }
}
public static IQueryable<ISecurable> FilterByACL2(this IQueryable<ISecurable> securables, User user, int requiredAction, MyDbContext db)
{
    var userId = user.Id;

    var workgroups = db.Entry(user).Collection(u => u.Workgroups).Query();

    return securables.Where(s =>
        s.ACL.ACEntries.Any(e =>
            (e.PrincipalId == userId || workgroups.Select(w => w.Id).Contains(e.PrincipalId)) &&
            (e.AllowedActions & requiredAction) == requiredAction));

}
。。理论上,我可以从dbo中选择“内部联接”或“存在1”。AllowableACLs@UserId,@RequiredAction'来自我的可治愈实体

有没有办法先从代码开始工作?

user.Workgroups是一个局部变量,由一系列c对象组成。没有将这些对象转换为SQL。EF需要这种转换,因为变量在表达式中使用

但避免异常很简单:首先创建一个基本ID值列表:

var workGroupIds = user.Workgroups.Select(w => w.Id).ToArray();
在查询中:

e.PrincipalId == userId || workGroupIds.Contains(userId)
在你的评论之后,你似乎也可以

return securables.Where(s =>
    s.ACL.ACEntries.Any(e =>
        (e.PrincipalId == userId 
             || e.Principal.Workgroups.Select(w => w.Id).Contains(userId))
    && (e.AllowedActions & requiredAction) == requiredAction));

…这将在一个查询中完成所有操作。

您需要移动用户。工作组。选择。。。无法表达。EF尝试将该表达式转换为sql,但失败,因为用户是局部变量。因此,这将有助于修复错误:

public static IQueryable<ISecurable> FilterByACL(this IQueryable<ISecurable> securables,
    User user, int requiredAction)
{
    var userId = user.Id;
    var groupIds = user.Workgroups.Select(w => w.Id).ToArray();
    return securables.Where(s =>
        s.ACL.ACEntries.Any(e =>
            (e.PrincipalId == userId || groupIds.Contains(userId)) &&
            (e.AllowedActions & requiredAction) == requiredAction));

}

请注意,为了确保这只是一个查询,您需要在从数据库检索用户时加载用户的Workgroups属性。否则,如果您启用了延迟加载,这将首先加载给定用户的工作组,因此需要进行两次查询。

为了在一次行程中获得该信息,您有许多选项

最明显的是使用join和linq语法: 接近于:

var accessbleSecurables = from s in securables
                          from u in ctx.Users
                          from g in u.Workgroups
                          from e in s.ACL.entries
                          where u.id == userId ...
                          select s
因此,在查询安全文件时,它将使用数据库中的用户和她的组

使用以下命令将工作组转换为查询 通过这种方式,您将让EF知道它需要在db级别执行包含操作

更新:CreateSourceQuery是来自EF3-EF4天的旧名称。现在您可以使用如下方法:

  var wgQuery = ctx.Entry(user).Collection(u=>u.Workgroups).Query();
然后您就可以在查询中使用它,但您需要引用EF上下文才能做到这一点

创建将执行相同sql查询的存储过程
更新2:创建存储过程的一个独特优点是能够为它映射多个返回结果集。例如,通过这种方式,您可以在一次数据库访问中查询用户的所有工作组和所有安全性

谢谢你的回答。你们都是对的,即使这意味着两次去星展银行?首先,为用户获取所有工作组ID,然后执行查询本身。当我在注释之前添加到我的答案时,一个选项是将工作组与用户一起使用在包含从数据库中获取用户的地方的“包含”。@ BuGY08也考虑至少使用基本缓存。没有理由一次又一次地为用户工作组之类的数据而麻烦数据库,因为这些数据不经常更改。缓存它们,您不需要Include.Gert,请参阅Evk答案的注释。请参阅添加内容。请参阅我的答案,您也可以对用户使用CreateSourceQuery。工作组的“主体”没有“主体.工作组”导航属性。只有“User.Workgroups”。至于你的1,我想我应该在DbContext类中这样做,但在这种情况下,我不确定是否可以将其作为扩展方法,因为它需要是静态的?如何/在何处创建SourceQuery?@buggy08查看我的更新答案,我引用了旧的EF API,现在它是DbCollectionEntry.Queryes,它工作得很好,尽管如此,正如您所提到的,我必须引用dbcontext。你能检查一下我的更新吗?@buggy08你可以用EF迁移创建那个存储过程