2013-05-09 47 views
1

比方说,我创建具有以下特征的“待办事项列表”的Web应用程序:ASP.NET MVC4库注入到控制器,取决于当前登录用户

  • 有能力注册/登录用户每
  • 用户有自己的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

我会在以下两种方法之一解决这个:

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将被注入到Repository<ToDoItem>为您Ninject。

2. 创建IRepository<ToDoItem>(或者甚至IRepository<T>如果合适的话)的扩展方法,它包装添加Where()表达用于限制返回TODO项只限于那些相关的当前用户。

+0

由于存储库注入到控制器中,使其生命周期与Web请求权绑定?但是,我想避免控制器的代码依赖于当前记录的用户和使用已经返回当前用户数据的存储库。例如,假设我没有多用户支持就首先创建了这个应用程序。现在我将如何扩展它以支持多用户最简单的方式?例如。控制器应保持不变,只更改存储库以返回当前登录用户的数据。希望这会更清楚我的问题... – matori82 2013-05-09 15:38:32

+0

存储库的生活方式与Web请求无关;它可以例如具有单身生活方式,这意味着将相同的存储库实例注入到每个新创建的控制器实例中。我现在更了解你想做什么,但我不确定希望避免修改控制器代码是实现它的最好方法。话虽如此,“HttpContext.User”和“Thread.CurrentPrincipal”都应设置为代表当前用户的“IPrincipal”,以便您可以在存储库方法中使用这些... – 2013-05-09 15:48:00

+0

注入“IPrincipal”或“IIdentity”可能会使存储库更易于测试,但这确实意味着存储库的生活方式需要根据每个Web请求(或更短)进行。 – 2013-05-09 15:49:38

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