我正在Bootstrapper中配置Automapper,我在Bootstrap()
中调用Application_Start()
,我被告知这是错误的,因为我必须修改每个Bootstrapper
类我必须添加一个新的映射,所以我违反了开放原则。
您如何看待,我是否真的违反了这一原则?
public static class Bootstrapper
{
public static void BootStrap()
{
ModelBinders.Binders.DefaultBinder = new MyModelBinder();
InputBuilder.BootStrap();
ConfigureAutoMapper();
}
public static void ConfigureAutoMapper()
{
Mapper.CreateMap<User, UserDisplay>()
.ForMember(o => o.UserRolesDescription,
opt => opt.ResolveUsing<RoleValueResolver>());
Mapper.CreateMap<Organisation, OrganisationDisplay>();
Mapper.CreateMap<Organisation, OrganisationOpenDisplay>();
Mapper.CreateMap<OrganisationAddress, OrganisationAddressDisplay>();
}
}
答案 0 :(得分:39)
我认为你违反了两个原则:单一责任原则(SRP)和开放/封闭原则(OCP)。
您违反了SRP,因为引导类有多个原因需要更改:如果您更改模型绑定或自动映射器配置。
如果要添加额外的引导代码以配置系统的另一个子组件,则会违反OCP。
我通常如何处理这个问题是我定义了以下界面。
public interface IGlobalConfiguration
{
void Configure();
}
对于需要引导的系统中的每个组件,我将创建一个实现该接口的类。
public class AutoMapperGlobalConfiguration : IGlobalConfiguration
{
private readonly IConfiguration configuration;
public AutoMapperGlobalConfiguration(IConfiguration configuration)
{
this.configuration = configuration;
}
public void Configure()
{
// Add AutoMapper configuration here.
}
}
public class ModelBindersGlobalConfiguration : IGlobalConfiguration
{
private readonly ModelBinderDictionary binders;
public ModelBindersGlobalConfiguration(ModelBinderDictionary binders)
{
this.binders = binders;
}
public void Configure()
{
// Add model binding configuration here.
}
}
我使用Ninject注入依赖项。 IConfiguration
是静态AutoMapper
类的基础实现,ModelBinderDictionary
是ModelBinders.Binder
对象。然后,我将定义一个NinjectModule
,它将扫描指定程序集以查找实现IGlobalConfiguration
接口的任何类,并将这些类添加到组合中。
public class GlobalConfigurationModule : NinjectModule
{
private readonly Assembly assembly;
public GlobalConfigurationModule()
: this(Assembly.GetExecutingAssembly()) { }
public GlobalConfigurationModule(Assembly assembly)
{
this.assembly = assembly;
}
public override void Load()
{
GlobalConfigurationComposite composite =
new GlobalConfigurationComposite();
IEnumerable<Type> types =
assembly.GetExportedTypes().GetTypeOf<IGlobalConfiguration>()
.SkipAnyTypeOf<IComposite<IGlobalConfiguration>>();
foreach (var type in types)
{
IGlobalConfiguration configuration =
(IGlobalConfiguration)Kernel.Get(type);
composite.Add(configuration);
}
Bind<IGlobalConfiguration>().ToConstant(composite);
}
}
然后我将以下代码添加到Global.asax文件中。
public class MvcApplication : HttpApplication
{
public void Application_Start()
{
IKernel kernel = new StandardKernel(
new AutoMapperModule(),
new MvcModule(),
new GlobalConfigurationModule()
);
Kernel.Get<IGlobalConfiguration>().Configure();
}
}
现在我的自举代码遵循SRP和OCP。我可以通过创建一个实现IGlobalConfiguration
接口的类来轻松添加其他引导代码,而我的全局配置类只有一个改变的原因。
答案 1 :(得分:3)
要完全关闭它,你可以为每个映射注册设置一个静态初始值设定项,但这样就太过分了。
从能够进行逆向工程的角度来看,有些事情对于集中到某种程度实际上是有用的。
在NInject中,每个项目或子系统(一组项目)都有Module
的概念,这似乎是一个明智的妥协。
答案 2 :(得分:3)
我知道这是一个旧版本,但您可能有兴趣知道我创建了一个名为Bootstrapper的开源库,它可以准确处理这个问题。你可能想看一下。 为了避免违反OC原则,您需要在实现IMapCreater的单独类中定义映射器。 Boostrapper将使用反射找到这些类,并将在启动时初始化所有映射器
答案 3 :(得分:2)
如果您违反了单一责任原则,那么该课程有多个理由需要改变。
我个人会有一个ConfigureAutoMapper类,我的所有AutoMapper配置都已完成。但可以说,这取决于个人选择。
答案 4 :(得分:2)
基本上,我认为这是决定是否要集中引导或分散它的问题。如果您担心启动例程中的开放/封闭原则,请将其分散。但是,您可以拨打您对OCP的遵守情况,以换取在一个地方完成的所有引导的价值。假设AutoMapper有这样的概念,另一种选择是让引导程序扫描注册表的某些程序集。