2013-04-03 65 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); 
    } 
} 
相关问题