35

我在 Bootstrapper 中配置 AutomapperBootstrap()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>();
    }    
}
4

5 回答 5

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类的底层实现,ModelBinderDictionaryModelBinders.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接口的类轻松添加额外的引导代码,而我的全局配置类只有一个更改的理由。

于 2009-11-27T21:46:24.377 回答
3

要让它完全关闭,您可以为每个映射注册设置一个静态初始化程序,但这会有点矫枉过正。

从能够进行逆向工程的角度来看,有些事情实际上在一定程度上集中是有用的。

在 NInject 中,有一个Module每个项目或子系统(一组项目)的概念,这似乎是一个明智的折衷方案。

于 2009-11-27T08:35:26.167 回答
3

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

于 2011-11-25T17:23:46.373 回答
2

如果有的话,它是您违反的单一责任原则,因为该类有多个改变的理由。

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

于 2009-11-27T14:39:18.320 回答
2

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

基本上,我认为这是一个决定是要集中引导还是分散引导的问题。如果您对启动程序中的开放/封闭原则感到担忧,那就去中心化它。但是,您对 OCP 的遵守可能会被调低,以换取您在一个地方完成的所有引导的价值。另一种选择是让引导程序扫描注册表的某些程序集,假设 AutoMapper 有这样的概念。

于 2009-11-27T15:05:28.050 回答