我在 MVC 应用程序中为 IoC 使用 Unity 框架,并使用 LoadConfiguration 方法(从配置文件中读取接口/类型映射)填充 UnityContainer。
在我的应用程序中的不同控制器之间共享此容器的最佳方法是什么?
如果我在应用程序(Global.asax)上使用公共属性,我应该担心线程同步吗?
我在 MVC 应用程序中为 IoC 使用 Unity 框架,并使用 LoadConfiguration 方法(从配置文件中读取接口/类型映射)填充 UnityContainer。
在我的应用程序中的不同控制器之间共享此容器的最佳方法是什么?
如果我在应用程序(Global.asax)上使用公共属性,我应该担心线程同步吗?
我同意 Zabavsky 关于注入服务的观点。这是我所做的:
我将我的 MVC 应用程序分成几个不同的项目。
我还有一组打包到 NuGet 包中的类,如果需要,我可以将它们添加到我的应用程序中,即(对于本示例):
我的控制器除了从服务层获取视图模型以发送到视图,然后在从视图发布时接收数据并将其发送到服务层进行验证并保存回存储库之外什么都不做。
这是一个基本示例:
这是将在此示例中使用的视图模型:
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; }
}
带有 GET 和 POST 操作方法的示例控制器:
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();
}
}
仓库界面:
public interface IAppNameRepository : IRepository
{
CareerPlanningForm GetCareerPlanningFormByID(int careerPlanningFormID);
}
CompanyName.Data 公共库中的类:
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 中的 Unity 引导
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 中的 Unity 引导
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 中的 Unity 引导
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);
}
}