ASP.NET MVC4存储库注入到依赖于当前登录用户的控制器

时间:2013-05-09 14:57:35

标签: asp.net-mvc repository code-injection authorize

假设我正在创建一个具有以下功能的“TODO list”Web应用程序:

  • 有能力注册/登录用户
  • 每个用户都有自己独立于其他用户的TODO列表

所以我创建了一个包含ToDoItem类的简单模型。

我想使用好的做法,所以我创建了一个应该从数据库中获取TODO项的通用存储库:

public interface IRepository<T>
{
    IQueryable<T> FindAll();
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);

    void Add(T newEntity);
    void Remove(T entity);

    T FindById(long id);
}

(使用EF和代码优先方法完成实现,但现在这并不重要)

此存储库将注入控制器,允许用户列出,添加,删除,编辑TODO项目。这是通过我创建的自定义控制器工厂完成的,它使用Ninject DI容器将存储库接口解析为具体实现。 所以控制器看起来像这样:

public class HomeController : Controller
{
    IRepository<ToDoItem> _repository;
    public HomeController(IRepository<ToDoItem> repository)
    {
        _repository = repository;
    }

    // list all TODO items for this user
    [Authorize]
    public ActionResult ListItems()
    {
        var todoItems = _repository.FindAll();
        return View(todoItems);
    }

}

我的问题是什么是让控制器仅为当前登录用户返回TODO列表的最佳方法?理想情况下,我希望控制器能够使用已注入并预先设置当前登录用户的存储库。换句话说,我想在动作方法中避免使用这种代码:

    // list all TODO items for this user
    [Authorize]
    public ActionResult ListItems()
    {
        var todoItems = _repository.FindAll(User.Identity);
        return View(todoItems);
    }

我在想可能的解决方案是让控制器工厂以某种方式知道哪个用户被记录,因此它会初始化具体的存储库并设置用户ID,以便控制器不必在每个操作方法中都这样做。这是一个好方法,如果是这样,我该如何实现它?如果没有,有什么更好的选择?

2 个答案:

答案 0 :(得分:2)

我会用两种方法解决这个问题:

1。 根据Web请求使存储库的生活方式成为可能,并依赖于User.Identity,以便可以在存储库方法中使用它。 e.g。

public class Repository<ToDoItem> : IRepository<ToDoItem>
{
    private IIdentity _identity;

    // let the container inject the IIdentity into the repository
    // (you will need to register a service with 
    //  the container for IIdentity for this)
    public Repository(IIdentity identity)
    {
        _identity = identity;
    }

    IQueryable<ToDoItem> FindAll()
    {
        return FromSomeContext().Where(x => x.Username == _identity.Name);
    }

    // ....
}

然后使用Ninject注册一个方法,它可以调用解析IIdentity任何需要它的组件。 (您可以决定注入IPrincipal更有用,因为您也可以通过它获取有关用户角色的信息。)

kernel.Bind<IIdentity>()
      .ToMethod(ctx => HttpContext.Current.User.Identity)
      .InRequestScope();

现在,假设Ninject还在为您构建控制器,并且您已为应用程序所需的IRepository<T>服务注册了组件,则当前用户IIdentity将被注入Repository<ToDoItem> Ninject给你的。

2。 为IRepository<ToDoItem>(如果合适,甚至是IRepository<T>)创建一个扩展方法,该方法包含添加Where()表达式,以便仅将返回的TODO项限制为与当前用户相关的项。

答案 1 :(得分:0)

对于那些使用温莎城堡的人:

container.Register(
...
Component.For<IIdentity>()
    .UsingFactoryMethod(() => { return HttpContext.Current.User.Identity; })
    .LifeStyle.PerWebRequest,
...);

注意:

Component.For<ICustomer>().Instance(HttpContext.Current.User.Identity)

不起作用,因为“当您注册现有实例时,即使您指定了生活方式,也会被忽略。”,请参阅Windsor Castle Documentation