我在Bootstrapper中配置Automapper,我在 Application_Start()中调用 Bootstrap(),我被告知这是错的,因为我有每次我必须添加一个新映射时修改我的 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>();
    }    
}
有帮助吗?

解决方案

我认为你违反了两个原则:单一责任原则(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 接口的类来轻松添加其他引导代码,而我的全局配置类只有一个改变的原因。

其他提示

要完全关闭它,你可以为每个映射注册设置一个静态初始值设定项,但这样就太过分了。

从能够进行逆向工程的角度来看,某些事情对于集中到某种程度实际上是有用的。

在NInject中,每个项目或子系统(一组项目)都有一个 Module 的概念,这似乎是一个合理的妥协。

我知道这是旧版本,但您可能有兴趣知道我创建了一个名为 Bootstrapper的开源库正是处理这个问题。你可能想看一下。 为了避免违反OC原则,您需要在实现IMapCreater的单独类中定义映射器。 Boostrapper将使用反射找到这些类,并将在启动时初始化所有映射器

如果您违反了单一责任原则,那么该课程有多个理由需要改变。

我个人会有一个ConfigureAutoMapper类,我的所有AutoMapper配置都已完成。但可以说,这取决于个人选择。

Omu,在我的应用程序启动例程中引导IoC容器时,我遇到了类似的问题。对于IoC,我给出的指导指出了集中配置的优势,而不是在添加更改时将其全部洒在应用程序上。对于配置AutoMapper,我认为集中化的优势不那么重要。如果您可以将AutoMapper容器放入IoC容器或服务定位器,我同意Ruben Bartelink的建议,即每个程序集或静态构造函数或分散的东西配置映射一次。

基本上,我认为这是决定是否要集中引导或分散它的问题。如果您担心启动例程中的开放/封闭原则,请将其分散。但是,您可以拨打您对OCP的遵守情况,以换取在一个地方完成的所有引导的价值。假设AutoMapper有这样的概念,另一种选择是让引导程序扫描注册表的某些程序集。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top