1

假设我正在创建一个具有以下功能的“TODO 列表”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,这样控制器就不必在每个操作方法中都这样做。这会是一个好方法吗?如果是这样,我该如何实施?如果没有,有什么更好的选择?

4

2 回答 2

2

我将通过以下两种方式之一解决这个问题:

1. 使存储库的生活方式符合 Web 请求,并依赖于User.Identity可以在存储库方法中使用的方式。例如

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任何需要它的组件。(您可能会认为注入 anIPrincipal更有用,因为您也可以使用它获取有关用户角色的信息)。

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

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

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

于 2013-05-09T15:04:24.963 回答
0

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

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

笔记:

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

不起作用,因为“当您注册现有实例时,即使您指定一种生活方式,它也会被忽略。”,请参阅温莎城堡文档

于 2014-04-22T10:59:05.270 回答