2013-04-03 102 views
1

我在MVC應用程序中使用IoC的Unity框架,並使用LoadConfiguration方法(從配置文件讀取接口/類型映射)填充UnityContainer。在MVC應用程序中的控制器之間共享Unity容器的最佳方式是什麼?

什麼是在我的應用程序中的不同控制器之間共享此容器的最佳方法?

如果我在應用程序(Global.asax)上使用公共屬性,我應該擔心線程同步嗎?

+1

爲什麼你需要在你的控制器UnityContainer?正確的方法是注入你需要的服務。 – Zabavsky

+0

最佳實踐是使用代碼配置(使用'RegisterType'方法)儘可能多地依賴,而不是使用XML。 XML很脆弱,容易出錯,並且在增長時會成爲維護的噩夢。只能將註冊信息放置在部署期間或之後必須可更改的XML中。 – Steven

回答

1

我同意Zabavsky關於注射服務。這是我做的:

我把我的MVC應用程序分成幾個不同的項目。

  • AppName.Configuration:處理應用的任何配置(即,拉web.config中/應用設置等)
  • AppName.Data:這是執行所有DB訪問該數據層(無商業邏輯)。 DBML/EDMX居住在這裏,我的倉庫類也住在這裏。
  • AppName.Models:這是我爲所有MVC定義的ViewModel以及整個應用程序需要的其他模型對象。
  • AppName.Services:這是我的業務層,所有的一切都必須經過這裏才能到達數據層或表示層。 ViewModels是從數據庫對象構建的,數據驗證發生在這裏等。
  • AppName.Web:這將是MVC應用程序。
  • AppName.Data.Test:對於數據應用單元測試
  • AppName.Services.Test:爲MVC控制器
  • AppName.Web單元測試:對於服務
  • AppName.Web.Test單元測試。 UI.Test:單元測試的Web用戶界面(使用華廷)

我也有一組打包成的NuGet包中的類如果需要的話/時,即我可以添加到我的應用程序(在這個例子中):

  • CompanyName.Data:數據層邏輯
  • CompanyName.MVC公共庫:用於ASP.NET MVC集成
  • CompanyName.Utilities公共庫:雜項公用事業

我控制器公共庫做什麼,除了從服務層獲取視圖模型以發送到視圖,然後從視圖中發佈後接收數據,並將其發送到服務層進行驗證並保存回存儲庫。

這裏是一個基本的例子:

這是將在本實施例中所使用的視圖模型:用GET和POST操作方法

public class CreateFocusViewModel 
{ 
    public int CareerPlanningFormID { get; set; } 

    public int PerformanceYear { get; set; } 

    public IList<FocusModel> Focuses { get; set; } 

    public string ResultsMeasuresFocusComments { get; set; } 

    public byte MaximumFocusesAllowed { get; set; } 
} 

public class FocusModel 
{ 
    public int FocusID { get; set; } 

    public string FocusText { get; set; } 

    public bool IsPendingDeletion { get; set; } 
} 

樣品控制器:

public class CPFController : Controller 
{ 
    private readonly ICareerPlanningFormService careerPlanningFormService; 

    public CPFController(ICareerPlanningFormService careerPlanningFormService) 
    { 
     this.careerPlanningFormService = careerPlanningFormService; 
    } 

    [HttpGet] 
    public ViewResult CreateFocus(int careerPlanningFormID) 
    { 
     var model = this.careerPlanningFormService.BuildCreateFocusViewModel(careerPlanningFormID); 
     return this.View(model); 
    } 

    [HttpPost] 
    public ActionResult CreateFocus(int careerPlanningFormID, string button) 
    { 
     var model = this.careerPlanningFormService.BuildCreateFocusViewModel(careerPlanningFormID); 
     this.TryUpdateModel(model); 

     switch (button) 
     { 
      case ButtonSubmitValues.Next: 
      case ButtonSubmitValues.Save: 
      case ButtonSubmitValues.SaveAndClose: 
       { 
        if (this.ModelState.IsValid) 
        { 
         try 
         { 
          this.careerPlanningFormService.SaveFocusData(model); 
         } 
         catch (ModelStateException<CreateFocusViewModel> mse) 
         { 
          mse.ApplyTo(this.ModelState); 
         } 
        } 

        if (!this.ModelState.IsValid) 
        { 
         this.ShowErrorMessage(Resources.ErrorMsg_WEB_ValidationSummaryTitle); 
         return this.View(model); 
        } 

        break; 
       } 

      default: 
       throw new InvalidOperationException(string.Format(Resources.ErrorMsg_WEB_InvalidButton, button)); 
     } 

     switch (button) 
     { 
      case ButtonSubmitValues.Next: 
       return this.RedirectToActionFor<CPFController>(c => c.SelectCompetencies(model.CareerPlanningFormID)); 

      case ButtonSubmitValues.Save: 
       this.ShowSuccessMessage(Resources.Msg_WEB_NotifyBarSuccessGeneral); 
       return this.RedirectToActionFor<CPFController>(c => c.CreateFocus(model.CareerPlanningFormID)); 

      case ButtonSubmitValues.SaveAndClose: 
      default: 
       return this.RedirectToActionFor<UtilityController>(c => c.CloseWindow()); 
     } 
    } 
} 

ViewModel構建和數據驗證/保存的服務層:

public class CareerPlanningFormService : ICareerPlanningFormService 
{ 
    private readonly IAppNameRepository repository; 
    private readonly IPrincipal currentUser; 

    public CareerPlanningFormService(IAppNameRepository repository, IPrincipal currentUser) 
    { 
     this.repository = repository; 
     this.currentUser = currentUser; 
    } 

    public CreateFocusViewModel BuildCreateFocusViewModel(int careerPlanningFormID) 
    { 
     var cpf = this.repository.GetCareerPlanningFormByID(careerPlanningFormID); 

     // create the model using cpf 
     var model = new CreateFocusViewModel 
     { 
      CareerPlanningFormID = cpf.CareerPlanningFormID, 
      PerformanceYear = cpf.PerformanceYearID, 
      ResultsMeasuresFocusComments = cpf.ResultsMeasuresFocusComments, 
      MaximumFocusesAllowed = cpf.PerformanceYear.MaximumCareerPlanningFormFocusesAllowed 
      // etc., etc... 
     }; 

     return model; 
    } 

    public void SaveFocusData(CreateFocusViewModel model) 
    { 
     // validate the model 
     this.ValidateCreateFocusViewModel(model); 

     // get the current state of the CPF 
     var cpf = this.repository.GetCareerPlanningFormByID(model.CareerPlanningFormID); 

     // bunch of code saving focus data here... 

     // update the ResultsMeasuresFocusComments 
     cpf.ResultsMeasuresFocusComments = string.IsNullOrWhiteSpace(model.ResultsMeasuresFocusComments) ? null : model.ResultsMeasuresFocusComments.Trim(); 

     // commit the changes 
     this.repository.Commit(); 
    } 

    private void ValidateCreateFocusViewModel(CreateFocusViewModel model) 
    { 
     var errors = new ModelStateException<CreateFocusViewModel>(); 

     { 
      var focusesNotPendingDeletion = model.Focuses.Where(f => f.IsPendingDeletion == false); 

      // verify that at least one of the focuses (not pending deletion) has a value 
      { 
       var validFocuses = focusesNotPendingDeletion.Where(f => !string.IsNullOrWhiteSpace(f.FocusText)).ToList(); 
       if (!validFocuses.Any()) 
       { 
        var index = model.Focuses.IndexOf(model.Focuses.Where(f => f.IsPendingDeletion == false).First()); 
        errors.AddPropertyError(m => m.Focuses[index].FocusText, Resources.ErrorMsg_CPF_OneFocusRequired); 
       } 
      } 

      // verify that each of the focuses (not pending deletion) length is <= 100 
      { 
       var focusesTooLong = focusesNotPendingDeletion.Where(f => f.FocusText != null && f.FocusText.Length > 100).ToList(); 
       if (focusesTooLong.Any()) 
       { 
        focusesTooLong.ToList().ForEach(f => 
        { 
         var index = model.Focuses.IndexOf(f); 
         errors.AddPropertyError(m => m.Focuses[index].FocusText, Resources.ErrorMsg_CPF_FocusMaxLength); 
        }); 
       } 
      } 
     } 

     errors.CheckAndThrow(); 
    } 
} 

庫類:

public class AppNameRepository : QueryRepository, IAppNameRepository 
{ 
    public AppNameRepository(IGenericRepository repository) 
     : base(repository) 
    { 
    } 

    public CareerPlanningForm GetCareerPlanningFormByID(int careerPlanningFormID) 
    { 
     return this.Repository.Get<CareerPlanningForm>().Where(cpf => cpf.CareerPlanningFormID == careerPlanningFormID).Single(); 
    } 
} 

庫接口:從CompanyName.Data公用庫

public interface IAppNameRepository : IRepository 
{ 
    CareerPlanningForm GetCareerPlanningFormByID(int careerPlanningFormID); 
} 

類:

public abstract class QueryRepository : IRepository 
{ 
    protected readonly IGenericRepository Repository; 

    protected QueryRepository(IGenericRepository repository) 
    { 
     this.Repository = repository; 
    } 

    public void Remove<T>(T item) where T : class 
    { 
     this.Repository.Remove(item); 
    } 

    public void Add<T>(T item) where T : class 
    { 
     this.Repository.Add(item); 
    } 

    public void Commit() 
    { 
     this.Repository.Commit(); 
    } 

    public void Refresh(object entity) 
    { 
     this.Repository.Refresh(entity); 
    } 
} 

public interface IGenericRepository : IRepository 
{ 
    IQueryable<T> Get<T>() where T : class; 
} 

public interface IRepository 
{ 
    void Remove<T>(T item) where T : class; 
    void Add<T>(T item) where T : class; 
    void Commit(); 
    void Refresh(object entity); 
} 

我都LinqToSQL和EF,這裏是設置對於LinqToSQL:

internal sealed class LinqToSqlRepository : IGenericRepository 
{ 
    private readonly DataContext dc; 

    public LinqToSqlRepository(DataContext dc) 
    { 
     this.dc = dc; 
    } 

    public IQueryable<T> Get<T>() where T : class 
    { 
     return this.dc.GetTable<T>(); 
    } 

    public void Remove<T>(T item) where T : class 
    { 
     this.dc.GetTable<T>().DeleteOnSubmit(item); 
    } 

    public void Add<T>(T item) where T : class 
    { 
     this.dc.GetTable<T>().InsertOnSubmit(item); 
    } 

    public void Commit() 
    { 
     this.dc.SubmitChanges(); 
    } 

    public void Refresh(object entity) 
    { 
     this.dc.Refresh(RefreshMode.OverwriteCurrentValues, entity); 
    } 
} 

這也在CompanyName.Data公共庫中。它具有的方法要麼寄存器LinqToSQL或的EntityFramework

public static class UnityContainerExtensions 
{ 
    public static IUnityContainer RegisterEntityFrameworkClasses<TDbContext>(this IUnityContainer container, string nameOrConnectionString) where TDbContext : DbContext 
    { 
     var constructor = typeof(TDbContext).GetConstructor(new Type[] { typeof(string) }); 
     container.RegisterType<DbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(c => constructor.Invoke(new object[] { nameOrConnectionString }))); 
     container.RegisterType<IGenericRepository, EntityFrameworkRepository>(); 
     return container; 
    } 

    public static IUnityContainer RegisterLinqToSqlClasses<TDataContext>(this IUnityContainer container, string connectionString) where TDataContext : DataContext 
    { 
     var constructor = typeof(TDataContext).GetConstructor(new Type[] { typeof(string) }); 
     container.RegisterType<DataContext>(new HierarchicalLifetimeManager(), new InjectionFactory(c => constructor.Invoke(new object[] { connectionString }))); 
     container.RegisterType<IGenericRepository, LinqToSqlRepository>(); 
     return container; 
    } 
} 

在CompanyName.Utilities庫:

public interface IUnityBootstrap 
{ 
    IUnityContainer Configure(IUnityContainer container); 
} 

統一引導在AppName.Data

public class UnityBootstrap : IUnityBootstrap 
{ 
    public IUnityContainer Configure(IUnityContainer container) 
    { 
     var config = container.Resolve<IAppNameConfiguration>(); 

     return container.RegisterLinqToSqlClasses<AppNameDataContext>(config.AppNameConnectionString) 
         .RegisterType<IAppNameRepository, AppNameRepository>(); 
    } 
} 

統一引導在AppName.Services

public class UnityBootstrap : IUnityBootstrap 
{ 
    public IUnityContainer Configure(IUnityContainer container) 
    { 
     new CompanyName.Security.UnityBootstrap().Configure(container); 
     new AppName.Data.UnityBootstrap().Configure(container); 

     container.RegisterSecureServices<AuthorizationRulesEngine>(typeof(UnityBootstrap).Assembly); 

     return container.RegisterType<ICareerPlanningFormService, CareerPlanningFormService>() 
         .RegisterType<IStaffService, StaffService>(); 
    } 
} 

團隊引導AppName.Web

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // Standard MVC setup 
     AreaRegistration.RegisterAllAreas(); 

     WebApiConfig.Register(GlobalConfiguration.Configuration); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 

     // Application configuration 
     var container = new UnityContainer(); 
     new CompanyName.Mvc.UnityBootstrap().Configure(container); 
     new AppName.Configuration.UnityBootstrap().Configure(container); 
     new AppName.Data.UnityBootstrap().Configure(container); 
     new AppName.Services.UnityBootstrap().Configure(container); 

     // Default MVC model binder is pretty weak with collections 
     ModelBinders.Binders.DefaultBinder = new DefaultGraphModelBinder(); 
    } 

    protected void Application_Error() 
    { 
     HttpApplicationEventHandler.OnError(this.Context); 
    } 

    protected void Application_EndRequest() 
    { 
     HttpApplicationEventHandler.OnEndRequest(this.Context); 
    } 
} 
相關問題