如何在mvc c#中处理这种情况?

时间:2014-02-10 07:47:40

标签: c# asp.net-mvc entity-framework

我正在使用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的实际上下文我认为可能会产生问题。

所以我的问题是如何处理这种情况,以便使用相同的上下文实例?

2 个答案:

答案 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可能有所帮助。