Asp.net mvc IoC的存储库模式-应该如何正确使用?
我是一个孤独的开发人员,Pluralsight是我的救星,它帮助我理解存储库和IoC之类的东西,我只是在学习。 然而,我发现这种模式很难使用 因此,我使用Unity IoC,并遵循Asp.net mvc IoC的存储库模式-应该如何正确使用?,asp.net-mvc,repository,unity-container,ioc-container,Asp.net Mvc,Repository,Unity Container,Ioc Container,我是一个孤独的开发人员,Pluralsight是我的救星,它帮助我理解存储库和IoC之类的东西,我只是在学习。 然而,我发现这种模式很难使用 因此,我使用Unity IoC,并遵循ModelContainer类 public static class ModelContainer { private static IUnityContainer instance; static ModelContainer() { instance = new Unit
ModelContainer
类
public static class ModelContainer
{
private static IUnityContainer instance;
static ModelContainer()
{
instance = new UnityContainer();
}
public static IUnityContainer Instance
{
get
{
instance.RegisterType<ISCD_CallDiaryRepository, SCD_CallDiaryRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<ICompanyRepository, CompanyRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<ISubcontractorRepository, SubcontractorRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<ITradeRepository, TradeRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<IEmployeeRepository, EmployeeRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<ISubcontractorTradeRepository, SubcontractorTradeRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<ICountyRepository, CountyRepository>(new HierarchicalLifetimeManager());
instance.RegisterType<IAddressLineRepository, AddressLineRepository>(new HierarchicalLifetimeManager());
return instance;
}
}
}
这种设置让我感到尴尬的是,如果我想在这些存储库类中使用CRUD功能,我必须在控制器中执行该功能,或者将我创建的存储库实例作为参数传递到模型类方法中。我选择后者,控制器并不意味着要做很多事情,但我认为必须不断传递repository参数是不对的
我错过了什么?我在职业生涯早期也尝试过后者。结局并不好。最终会出现意外的性能影响和大型模型对象。我认为控制器是你提到的逻辑的完美场所。如果您担心同一类中的ui和业务问题,请将业务转移到服务类。在提供的示例中,有
分包商控制器
控制器具有六个依赖项,这通常意味着控制器试图一次做太多事情,可能违反单一责任原则。至少有三种方法可以改善这种情况:
- 与将存储库注入控制器,并将存储库实例作为参数传递到模型类方法中不同,您可以将模型方法和依赖项组合到单个实体中。这也将解决构造函数过度注入的问题
- 将存储库工厂注入控制器,然后使用此工厂解决依赖关系
- 注入模型本身,并使存储库成为它们的依赖项
- AppName.Configuration:处理应用程序的任何配置(即拉入web.config/app设置等)
- 数据:这是执行所有数据库访问的数据层(无业务逻辑)。DBML/EDMX住在这里,我的存储库类也住在这里
- 模型:这是为MVC定义所有视图模型以及整个应用程序所需的其他模型对象的地方李>
- 服务:这是我的业务层,所有东西都必须经过这里才能到达数据层或表示层。ViewModels是从数据库对象构建的,数据验证在这里进行,等等
- Web:这将是MVC应用程序。AppName.Data.Test:数据应用程序的单元测试
- AppName.Services.Test:服务的单元测试
- AppName.Web.Test:MVC控制器的单元测试
- AppName.Web.UI.Test:Web用户界面的单元测试(使用WATIN)
- 数据:数据层逻辑的公共库
- CompanyName.MVC:用于ASP.NET MVC集成的公共库
- 实用程序:用于各种实用程序的公共库
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; }
}
来自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);
}
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>();
}
}
谢谢,这是非常有用的。我有个问题要问你。我有一个专门的DataServiceController,它只处理AJAX调用,因此将有许多存储库接口注入其中。为了维护SRP,我会设想我需要很多这样的数据服务控制器,而不是一个来处理所有这些控制器?仔细想想,您给出的解决方案也会处理这些问题。工厂和聚合服务都旨在解决这一问题。第一个更易于实现和通用,但第二个将帮助您以更优雅的方式将控制器、存储库和模型解耦——当然要考虑很多!谢谢你。我知道这很重要——我只是想彻底地告诉你全部情况。谢谢非常感谢。现在我应该勾选谁?服务被注入构造函数并在该过程中设置。决不能将服务分配给/重新实例化,因为依赖项注入容器指定的ICareerPlanningService始终是ICareerPlanningService。服务应该简单地被使用(即,根据需要调用它们的方法)。仅仅因为它对您不好就意味着这样做不是件好事?DI帮助我们应用SOLID principles。在我提到的“在业务对象中添加存储库类”的实例中,DI违反了单一责任原则。DI不应被用作应用可靠原则的总括规则。
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);
}
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);
}
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);
}
}
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;
}
}
public interface IUnityBootstrap
{
IUnityContainer Configure(IUnityContainer container);
}
public class UnityBootstrap : IUnityBootstrap
{
public IUnityContainer Configure(IUnityContainer container)
{
var config = container.Resolve<IAppNameConfiguration>();
return container.RegisterLinqToSqlClasses<AppNameDataContext>(config.AppNameConnectionString)
.RegisterType<IAppNameRepository, AppNameRepository>();
}
}
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>();
}
}
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);
}
}