2014-02-10 46 views
1

我正在使用asp.net mvc开发多租户应用程序。我必须为每个请求确定承租人,所以我创建了以下类别:如何在mvc c#中处理这种情况?

public class TenantProvider 
    { 
     public static Tenant Tenant 
     { 
      get 
      { 
       Tenant tenant = HttpContext.Current.Items["Tenant"] as Tenant; 

       if (tenant == null) 
       { 
        var tenantUsername = HelperUtility.GetCurrentRequestHost(); 
        //The below line of code is my problem 
        TenantRepository tenantRepository = new TenantRepository(new AppointContext()); 
        tenant = tenantRepository.GetByUsername(tenantUsername); 
        HttpContext.Current.Items.Add("Tenant", tenant); 
       } 

       return tenant; 
      } 
     } 
    } 

此类静态属性返回当前请求的承租人。它将首先检查高速缓存中的Tenant,如果没有找到,将从数据库中获取租户,初始化缓存并返回Tenant。

为了得到租客形成数据库我创建一个TenantRepository实例。 TenantRepository对数据库上下文具有依赖关系,我在创建实例时传递它。

现在,当我必须对当前Tenant执行其他数据库操作时,我必须在其他某个位置创建新的Repository实例,并且必须通过新的上下文,因此我提取了租户和新的实际上下文上下文不同,我认为可能会造成问题。

所以我的问题是我怎么能处理这种情况,因此在相同情况下例如将使用?

+0

为什么不将租户存储在索赔中? – Swell

+0

@Swell您能否用一些链接或示例代码来详细说明您的建议? – user1740381

+0

这是一个关于索赔http://vimeo.com/43549130的迷你速成课程,这是一部来自Dominick Baier的精彩视频。你也应该看看这个项目https://github.com/brockallen/BrockAllen.MembershipReboot来自Brock Allen。 – Swell

回答

2

您正在寻找的解决方案是工作单元设计模式。来自Martin Fowler:

维护受业务事务影响的对象列表,并协调写出更改和并发问题的解决方案。

裁判:http://martinfowler.com/eaaCatalog/unitOfWork.html

这种模式允许你注册多个事务组合成一个单一的环境。这是一个非常常见的模式,这里有一个可能的实现。首先,创建工作对象的单位将举行到你的中央上下文的引用,并且将与该上下文初始化的仓库(该实现使用实体框架):

public class UnitOfWork : IUnitOfWork 
{ 
    internal EntitiesContext _context = new EntitiesContext(); 
    private ITenantRepository _tenantRepository; 
    private IOtherRepository _otherRepository; 

    public ITenantRepository TenantRepository 
    { 
     get 
     { 
      if (_tenantRepository== null) 
      { 
       _tenantRepository= new TenantRepository(_context); 
      } 
      return _tenantRepository; 
     } 
    } 

    public IOtherRepository OtherRepository 
    { 
     get 
     { 
      if (_otherRepository== null) 
      { 
       _otherRepository= new OtherRepository(_context); 
      } 
      return _otherRepository; 
     } 
    } 

    public void Save() 
    { 
     _context.SaveChanges(); 
    } 

    private bool disposed = false; 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
      { 
       _context.Dispose(); 
      } 
     } 
     this.disposed = true; 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

需要注意的是,如果你使用任何存储库在这种模式下,他们都将使用相同的上下文。

你的控制器应要么初始化的工作单元,甚至更好,把它注入到它的构造:

public TenantController(IUnitOfWork unitOfWork) 
    { 
     _unitOfWork = unitOfWork; 
     _tenantRepository = unitOfWork.TenantRepository; 
     _otherRepository = unitOfWork.OtherRepository; 
    } 

如果您需要使用的UnitOfWork到另一层,你通常把它作为一个参数传递给另一个对象的构造函数:

public ActionResult Index() 
{ 
    TenantProvider provider = new TenantProvider(_unitOfWork); 
    _otherRepository.DoWork(); 
    _unitOfWork.Save(); 
} 

现在你TenantProvider可以做各自的仓库了一些工作,但工作的OtherRepository的单位也可以使用相同的情况下做一些工作。

+0

感谢您的回答以及真正有用的+1,我想知道为什么您在示例代码中使用ITenantRepository等接口。其实我知道界面的理论(合同B/W类和所有),但我真的从来不明白它的真正用途,这意味着我从来没有像'是的,这是接口的要求'这样的东西。那么你能否向我解释你的示例代码中接口的真正用途? – user1740381

+0

代码到接口,而不是实现。在上面的代码中,我的控制器不知道存储库如何保存其数据。虽然我的示例使用实体框架,因为它在一个接口后面,所以我理论上可以将我的存储库换成保存到XML文档,MySQL,文本文件的存储库......解耦您的代码可确保您的代码更易于维护,适应变化。 –

+0

感谢您的解释,它非常有帮助 – user1740381

0

除了@ChrisHardie,我想添加一些MVC的细节:我认为这是注入工作单元到控制器的一个非常好的做法。为了做到这一点,你可以创建自DefaultControllerFactory衍生和应用程序启动时注册一个自定义的ControllerFactory:

public class CustomControllerFactory : DefaultControllerFactory 
{ 
    protected override IController GetControllerInstance(Type controllerType) 
    { 
     // Analyze whether instance of controllerType should be created directly or 
     // whether the Unit of Work should be injected 
     if (needsUnitOfWork) 
      return (IController)Activator.CreateInstance(controllerType, unitOfWork); 
     else 
      return (IController)Activator.CreateInstance(controllerType); 
    } 
} 

为了需要一个工作单元控制器和那些不之间的辨别,你可以使用反射(自定义属性或检查构造函数参数)。也许你也可以假设每个控制器目前需要一个工作单元。

如果您已经使用Inversion of Control容器,它可以支持您创建实例(IoC容器通常插入MVC中的ControllerFactory中)。如果没有,你可能会考虑开始使用一个。

你可以这样注册的ControllerFactory:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // ... 
     RegisterCustomControllerFactory(); 
     // ... 
    } 

    private void RegisterCustomControllerFactory() 
    { 
     IControllerFactory factory = new CustomControllerFactory(); 
     ControllerBuilder.Current.SetControllerFactory(factory); 
    } 
} 

由于这个答案是建立在对MVC依赖注入的扩展点,这link可能的帮助。

相关问题