10

我有一个自定义身份验证,当用户登录时,我会在会话/缓存中保留必要的信息......

所以,我有一些带有 DropDowns 的视图必须显示按用户 ID 过滤的数据......我想知道过滤该结果的最佳方法是什么......

1 - 直接在控制器上?

...   
Model.MyList = repository.GetAll().Where(x => x.User.Id == userId);
return View(Model);

2 - 创建一个动作过滤器(如何在不从数据库中查询不必要的数据的情况下做到这一点)

3 - 其他方式?

1 的问题是我有几个具有相同下拉列表的视图,因此我将不得不重复相同的代码。

4

7 回答 7

10

方法 - 1

功能

private void userInfo(ResultExecutingContext filtercontext)
{                                        
    if (filtercontext.Controller.TempData[userId.ToString()] == null)
        filtercontext.Controller.ViewBag.userId =
            filtercontext.Controller.TempData[userId.ToString()] = 
            repository.GetAll().Where(x => x.Id == userId);

    else      //This will load the data from TempData. So, no need to 
              //hit DataBase.
        filtercontext.Controller.ViewBag.userId =
            filtercontext.Controller.TempData[userId.ToString()];

    TempData.Keep();  // This will save your Database hit.
}

过滤方法

public class MyActionFilter : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filtercontext)
    {
        //Call the Action Method before executing the View and after 
        //executing the Action.
        userInfo(filtercontext);
        base.OnResultExecuting(filtercontext);
    }
}

控制器动作方法

[MyActionFilter] 
//Whenever Action Method will execute. We will check TempData contains 
//Data or not.
public ActionResult Index()
{
    return View();
}

TempData关于和的关键点TempData.Keep()

  1. 中的项目TempData只会在阅读后标记为删除。
  2. 中的项目TempData可以通过调用取消标记TempData.Keep(key)
  3. RedirectResult并且RedirectToRouteResult总是要求TempData.Keep()保留TempData.

您也可以使用Session变量,唯一的主要问题是Session变量与TempData. 最后,您还可以跨控制器/区域保留数据。

TempData也可以在新选项卡/窗口中使用,就像Session变量一样。

方法 - 2

您可以Cache将数据放在某个变量中,并且可以再次以相同的方式重用TempData.

于 2013-08-11T07:48:12.733 回答
1

我将创建一个操作过滤器,将您需要的值放入 ViewBag 并将其发送到视图。这样您就不必一遍又一遍地重写相同的代码,您可以只专注于视图以根据需要显示数据。请参阅下面的示例代码:

using System.Web.Mvc;
namespace CustomActionFilter.CustomActionFilters
{
    public class MyResultFilterAttribute : FilterAttribute, IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            //The action filter logic - before 
            filterContext.Controller.ViewBag.userInfo = GetNeccUserInfo(filterContext.HttpContext.User.Identity.Name);
        }

        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            //The action filter logic - after
        }
    }
    private UserInfo GetNeccUserInfo(string userName)
    {
        using (var repo = new UserRepository(new UniteOfWorkUsers()))
        {
            var userInfo = repo.GetUserInfo(userName);
            return userInfo;
        }
    }
}

希望这会有所帮助:)

于 2013-08-07T17:22:47.547 回答
1

免责声明:我是 Entity REST SDK 的作者。

我采用了不同的方法并创建了安全上下文,其中包含在查询任何内容之前应该应用的所有必要的 lambda 表达式。

    public class DefaultSecurityContext : BaseSecurityContext {

      public static DefaultSecurityContext Instance = new DefaultSecurityContext();

      // UserID for currently logged in User
      public static long UserID{
           get{
                 return long.Parse( HttpContext.Current.User.Identity.Name );
           }
      }

      public DefaultSecurityContext(){
      }

      protected override void OnCreate(){

            // User can access his own Account only
            var acc = CreateRules<Account>();

            acc.SetRead( y => x=> x.AccountID == UserID ) ;
            acc.SetWrite( y => x=> x.AccountID == UserID );

            // User can only modify AccountName and EmailAddress fields
            acc.SetProperties( SecurityRules.ReadWrite, 
                  x => x.AccountName,
                  x => x.EmailAddress);

            // User can read AccountType field
            acc.SetProperties<Account>( SecurityRules.Read, 
                  x => x.AccountType);

            // User can access his own Orders only
            var order = CreateRules<Order>();
            order.SetRead( y => x => x.CustomerID == UserID );

            // User can modify Order only if OrderStatus is not complete
            order.SetWrite( y => x => x.CustomerID == UserID && x.OrderStatus != "Complete" );

            // User can only modify OrderNotes and OrderStatus
            order.SetProperties( SecurityRules.ReadWrite, 
                  x => x.OrderNotes,
                  x => x.OrderStatus );

            // User can not delete orders
            order.SetDelete(order.NotSupportedRule);
      }
}

如您所见,我们也可以过滤对属性的访问。

如果您的大部分重复代码被安全上下文替换,您可以根据不同的用户角色创建不同的安全上下文并仍然保持相同的控制器。

public class OrdersController : WebAtomsController <MyEntities> {
        protected override BaseSecurityContext CreateSecurityContext(){
             return DefaultSecurityContext.Instance;
        }

        public ActionResult SearchOrders( 
               string productName, 
               string orderBy = "OrderID DESC", 
               int start = 0, 
               int size = 10)
        {
               // Where method automatically applies
               // filter based on current SecurityContext

               var aq = Where<Order>();
               if(!string.IsNullOrEmpty(productName)){
                      aq = aq.Where( 
                                  x=> x.OrderItems.Any( 
                                     y=> y.Product.ProductName.StartsWith(productName)));
               }

               // OrderBy accepts string as a parameter
               aq = aq.OrderBy(orderBy);
               return aq.Page(start,size).Select( 
                     y=> new {
                            y.OrderID,
                            y.OrderDate,
                            y.OrderStatus,
                     });
        }
}

更多详情,请访问 https://entityrestsdk.codeplex.com

于 2013-08-13T20:44:44.507 回答
1

这是一个非常默认的场景,您希望用户只看到与他相关的数据。

就个人而言,我从来没有在控制器中进行 DB-Calls,我总是有一个额外的 DataLayer,我用 IoC-Container 连接它。

这个 DataLayer 应该只知道 DataBase 和数据是如何存储的,并正确过滤这些数据。您可以争论 DataLayer 是否可以使用 HttpContext 自动检索用户 ID,或者应该将其作为参数获取。

所以你不必总是写那个表达式,你也可以创建一个函数,它会给你正确的 Where-Lambda-Expression,你可以简单地使用它:

public Expression<TModel> GetUserFilter<TModel>()
{
    var userId = GetUserId();
    var itemParameter = Expression.Parameter(typeof(TModel), "item");
    var whereExpression = Expression.Lambda<Func<TModel, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "Id"
                ),
            Expression.Constant(userId)
            ),
        new[] { itemParameter }
        );
    return whereExpression;
}

现在您可以在 Controller 或 DataLayer 中调用此函数:

Model.MyList = repository.GetAll().Where(GetUserFilter<Repository>());

您当然可以更改名称并使其更短,以便实际上更少编写:)

于 2013-08-11T10:55:50.917 回答
0

最好的方法:获取所有用户的缓存列表。+:数据库高效。-:如果大表使用大量内存。-:结果不是最新的(调整缓存时间)。

在 OData 中有一个数据库请求过滤器可以执行此过滤器,但它并不打算以您想要的方式使用。它在这里是为了防止存储过程和查询中的错误,这些错误返回未经此用户授权的行。这是针对数据“泄漏”的第二级保护。

var model = new Model(userId)

别处:

Model(Guid userID)
{
    MyList = CacheStore.Get("allUsers", () => repository.GetAll())
                .Where(x => x.Id == userId).ToList();
}
于 2013-08-11T13:45:14.443 回答
0

为什么不创建一个带有 userId 参数的方法,或者创建一个用户的扩展方法使用它像这样“User.GetList();” ? 我不确定存储库模式是否合理。:)

于 2013-08-13T02:51:42.797 回答
0

例如,您可以创建 MembershipLogic 类并在那里定义您现在使用或将来可能使用的所有方法。类将通过用户 ID 返回任何数据

所以在你的控制器中它看起来像:

var db = new DbEntities();
List<a> newList = MembershipLogic.UserList(db, userid);

在 MembershipLogic 中,您需要以下方法:

public static List<a> UserList(DbEntities db, int UserID)
{
var list = db.GetAll().Where(x => x.Id == userId);
return list;
}

我在我的项目中使用这样的逻辑。积累方法并在我需要的任何地方使用它们。

于 2013-08-08T16:23:20.573 回答