我正在使用asp.net mvc开发一个多租户应用。我必须为每个请求确定租户,所以我在下面创建了一个类:
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,我正在创建一个TenantRepository实例。 TenantRepository对数据库上下文有依赖性,我在创建实例时传递它。
现在当我必须对当前Tenant进行其他数据库操作时,我必须在其他地方创建一个新的Repository实例并且必须传递新的Context,所以我提取租户和新Context的实际上下文我认为可能会产生问题。
所以我的问题是如何处理这种情况,以便使用相同的上下文实例?
答案 0 :(得分: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
也可以使用相同的上下文做一些工作。
答案 1 :(得分: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可能有所帮助。