在 Entity Framework 中按权限过滤行的最佳方式

best way to filter rows by permission in Entity Framework

在我的项目中我有一些 Post 像 News , Article , Announcement 和 ...

每个 post 都有权限,例如 Id 1 的新闻 post 只有管理员用户可以,并且...

如果登录,我应该按用户角色显示 post。

为此我尝试了一些方法:

方式一: 我添加了一个扩展方法来过滤 News,Article , ... Like this

public static class EfExtensionMethods
{
   public  IEnumerable<News> Filter( IDbSet<News> dbset) 
   {
      return (dbset.Where(...));
   }
}

方式 2: 我为过滤行添加了一个方法到服务层,像这样:

public void FilterNews(IEnumerable<News> newsList)
{
   var roles = _currentUserService.GetCurrentUserRoles();
   if (roles.Count == 0)
   {
      newsList = newsList.Where(...).ToList();
   }
   else
   {
      newsList = newsList.Where(...).ToList();
   }
}

在任何地方,我只需要将 NewsList 的列表传递给此方法进行过滤。 一个人被困?

有什么好的方法吗? 我想要一种可重复使用且不为每个 class

复制的方法

我假设您在谈论行级安全性。这是一项相当复杂的任务。 有几种方法可以做到这一点。

对于 MSSQL,请尝试查看 MSDN Security Blog about RLS

你应该用静态 Expressions 制作一个静态 class,那些你可以在你的查询中使用然后它在数据库上过滤(现在你正在从数据库中获取所有行并丢弃那些你不需要的,这是一种浪费):

partial class News
{
    public static class Q
    {
        public static Expression<Func<News,bool>> HasPermission(int permissionID)
        {
            return expr => expr.PermissionID == permissionID
        }
     }
 }

然后你可以这样查询,例如:

db.News.Where(News.Q.HasPermission(1)).ToList();

并且您将仅从 News table 中获取权限等于“1”的行。我真的不知道你的权限是如何存储的,但我想你明白了:)

编辑:对于泛型,我认为最简单的方法是让您的模型继承自 Permissionbase class 或其他东西:

public abstract class PermissionBase
{
   public int PermissionID {get; set;}
}

public static class QueryExtensions
{
   public static Expression<Func<T,bool>> HasPermission<T>(int permissionID) where T: PermissionBase
   {
       return expr => expr.PermissionID == permissionID
   }
}

没有 baseclass 也应该可以通过在 运行 上构建 Expression:

public static Expression<Func<T, bool>> HasPermission<T>(int PermissionID) where T : class
{
   ParameterExpression pe = Expression.Parameter(typeof(T), "p");
   System.Reflection.PropertyInfo pi = typeof(T).GetProperty("PermissionID");
   MemberExpression me = Expression.MakeMemberAccess(pe, pi);
   ConstantExpression ce = Expression.Constant(PermissionID);
   BinaryExpression be = Expression.Equal(me, ce);
   return Expression.Lambda<Func<T, bool>>(be, pe);
}

显然,只需确保所有模型中的属性名称都是正确的,或者您可以将属性名称传递给带有字符串的方法。