2014-02-10 2 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; 
      } 
     } 
    } 

Этого класс статического свойство, возвращающее Арендатор для текущего запроса. Сначала он проверяет наличие в кэше арендатора, если не найден, чем если он получит Арендатора из базы данных, инициализирует кеш и возвращает Арендатора.

Для получения Арендатора из базы данных я создаю экземпляр TenantRepository. TenantRepository имеет зависимость от контекста базы данных, которую я передаю при создании своего экземпляра.

Теперь, когда мне нужно выполнить другую операцию с базой данных в текущем Арендаторе, я должен создать новый экземпляр Репозитория в другом месте и должен передать новый Контекст, поэтому фактический контекст, с которым я извлек Арендатора, и новый Контекст отличается тем, что, я думаю, может создать проблему.

Так что мой вопрос: как я могу справиться с этим сценарием, чтобы использовать тот же экземпляр контекста?

+0

Почему не хранить арендатора в претензии? – Swell

+0

@ Хорошо, пожалуйста, уточните свое предложение с помощью ссылки или образца кода? – user1740381

+0

Вот мини-краш-курс по претензиям http://vimeo.com/43549130 Это отличное видео от Доминика Байера. Вы также должны посмотреть на этот проект https://github.com/brockallen/BrockAllen.MembershipReboot из Brock Allen. – Swell

ответ

2

Решение, которое вы ищете, представляет собой образец дизайна единицы работы. От Martin Fowler:

Поддерживает список объектов, подверженных деловой транзакции, и координирует выписывание изменений и разрешение проблем параллелизма.

исх: http://martinfowler.com/eaaCatalog/unitOfWork.html

Эта модель позволяет зарегистрировать несколько транзакций в одном контексте. Это очень распространенная картина, и здесь есть одна возможная реализация. Во-первых, создать блок объекта работы, который будет содержать ссылку на ваш центральный контекст, и который будет инициализировать репозиториев с этим контекстом (эта реализация использует Entity Framework):

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

Код для интерфейса, а не для реализации. В приведенном выше коде мой контроллер ничего не знает о том, как хранилище сохраняет свои данные. Хотя мой пример использует Entity Framework, потому что он находится за интерфейсом, я мог бы в принципе заменить мой репозиторий тем, который сохраняет XML-документ, MySQL, текстовый файл ... Развязка кода гарантирует, что ваш код более удобен в обслуживании и устойчив к изменениям. –

+0

спасибо за объяснение, его действительно полезно – user1740381

0

В дополнение к @ChrisHardie, я хочу добавить некоторые особенности MVC: Я считаю, что очень хорошая практика - ввести Unit of Work в контроллеры. Для того, чтобы сделать это, вы можете создать собственный ControllerFactory, который является производным от DefaultControllerFactory и регистрируется при запуске приложения:

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); 
    } 
} 

Для того, чтобы различать между контроллерами, которые нуждаются в единице работы и те, которые этого не сделать, может использовать отражение (пользовательские атрибуты или проверку аргументов конструктора).Возможно, вы также можете предположить, что каждый контроллер нуждается в Единице работы на данный момент.

Если вы уже используете контейнер с инверсией управления, он может поддерживать вас при создании экземпляров (контейнеры IoC обычно подключаются к ControllerFactory в MVC). Если нет, вы можете начать использовать его.

Вы можете зарегистрировать ControllerFactory так:

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

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

Как это ответы опирается на точки расширения для Dependency Injection в MVC, это link может помочь.

Смежные вопросы