假设我正在创建一个具有以下功能的"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和代码优先的方法完成的,但现在这并不重要)
这个存储库将被注入到控制器中,允许用户列出、添加、删除、编辑待办事项。这是通过我创建的自定义控制器工厂完成的,该工厂使用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,以便控制器不必在每个动作方法中都这样做。这是一个很好的方法,如果是,我该如何实施它?如果不是,有什么更好的选择?
我会用以下两种方法之一来解决这个问题:
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
。(你可以决定注入一个IPrincipal
是更有用的,因为你也可以得到关于用户角色的信息)
kernel.Bind<IIdentity>()
.ToMethod(ctx => HttpContext.Current.User.Identity)
.InRequestScope();
现在,假设Ninject也在为你构造你的控制器,并且你已经为你的应用程序所需的IRepository<T>
服务注册了组件,当前用户IIdentity
将被Ninject注入到Repository<ToDoItem>
中。
2。为IRepository<ToDoItem>
(或者IRepository<T>
,如果合适的话)创建一个扩展方法,将添加的Where()
表达式限制为只返回与当前用户相关的TODO项。
使用Windsor Castle的用户:
container.Register(
...
Component.For<IIdentity>()
.UsingFactoryMethod(() => { return HttpContext.Current.User.Identity; })
.LifeStyle.PerWebRequest,
...);
注意:
Component.For<ICustomer>().Instance(HttpContext.Current.User.Identity)
不起作用,因为"当你注册一个现有的实例时,即使你指定了一个生活方式,它也会被忽略",参见温莎城堡文档