35 votes

Configurer Automapper dans Bootstrapper viole le principe Open-Closed?

Je configure Automapper dans le Bootstrapper et j'appelle le Bootstrap() dans le Application_Start() , et on m'a dit que c'était faux parce que je devais modifier mon Bootstrapper class à chaque fois que je dois ajouter un nouveau mappage, je viole donc le principe Open-Closed.

Comment pensez-vous, est-ce que je viole vraiment ce principe?

 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>();
    }    
}
 

39voto

mrydengren Points 3319

Je dirais que vous êtes en violation de deux principes: le principe de responsabilité unique (SRP) et de l'open/closed principle (OCP).

Vous êtes à la violation de la SRP en raison de la phase de classe ont plus d'une raison de changer: si vous modifiez le modèle de la liaison ou de l'auto mappeur de configuration.

Vous serait violer l'OCP si vous étiez à ajouter de l'amorçage de code pour la configuration d'un sous-composant du système.

Comment j'ai l'habitude de gérer ce que je définissent l'interface suivante.

public interface IGlobalConfiguration
{
    void Configure();
}

Pour chaque composant dans le système d'amorçage je voudrais créer une classe qui implémente cette interface.

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.
    }
}

J'utilise Ninject à injecter les dépendances. IConfiguration est à la base de la mise en œuvre de la statique AutoMapper de la classe et de l' ModelBinderDictionary est le ModelBinders.Binder objet. Je ne puis définir un NinjectModule qui pourrait balayer l'assembly spécifié pour toute la classe qui implémente l' IGlobalConfiguration interface et ajouter ces classes à un composite.

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);
    }
}

Je voudrais ensuite ajouter le code suivant à la Mondiale.asax fichier.

public class MvcApplication : HttpApplication
{
    public void Application_Start()
    {
        IKernel kernel = new StandardKernel(
            new AutoMapperModule(),
            new MvcModule(),
            new GlobalConfigurationModule()
        );

        Kernel.Get<IGlobalConfiguration>().Configure();
    }
}

Maintenant mon code d'amorçage adhère à la fois SRP et de l'OCP. Je peux facilement ajouter d'autres amorçage code par la création d'une classe qui implémente l' IGlobalConfiguration interface et ma configuration globale classes seulement ont une raison d'en changer.

3voto

Ruben Bartelink Points 23945

Pour le fermer complètement, vous pourriez avoir un initialiseur statique par enregistrement de mappage, mais ce serait une perte de temps.

Certaines choses sont réellement utiles pour avoir centralisé dans une certaine mesure du point de vue de la possibilité de faire de l'ingénierie inverse.

Dans NInject, il existe la notion de Module par projet ou sous-système (ensemble de projets), ce qui semble un compromis sensé.

3voto

boca Points 1414

Je sais que c’est un vieux projet, mais vous serez peut-être intéressé de savoir que j’ai créé une bibliothèque open source appelée Bootstrapper qui traite précisément de ce problème. Vous voudrez peut-être y jeter un coup d'œil. Pour ne pas enfreindre le principe d'OC, vous devez définir vos mappeurs dans des classes distinctes qui implémentent IMapCreater. Boostrapper trouvera ces classes en utilisant la réflexion et initialisera tous les mappeurs au démarrage

2voto

Kev Hunter Points 989

Si quelque chose est le principe de responsabilité unique que vous violez, en ce sens que la classe a plus d'une raison de changer.

Personnellement, j'aurais une classe ConfigureAutoMapper avec laquelle toute ma configuration pour AutoMapper a été faite. Mais on pourrait affirmer que c'est une question de choix personnel.

2voto

flipdoubt Points 4140

Omu, je me débats avec des questions similaires quand il s'agit de l'amorçage d'un conteneur IoC dans mon application routine de démarrage. Pour les Cio, les conseils que j'ai été donné de points à l'avantage de la centralisation de votre configuration plutôt que de saupoudrer tout au cours de votre application que vous ajoutez des changements. Pour la configuration de AutoMapper, je pense que l'avantage de la centralisation est beaucoup moins important. Si vous pouvez obtenir votre AutoMapper récipient dans votre conteneur IoC ou Service Locator, je suis d'accord avec Ruben Bartelink la suggestion de configuration des mappages d'une fois par l'assemblée ou dans les constructeurs statiques ou quelque chose décentralisée.

En gros, je vois cela comme une question de décider si vous souhaitez centraliser l'amorçage ou la décentralisation. Si vous êtes préoccupé par les Ouvert/Fermé Principe sur votre routine de démarrage, accédez à la décentralisation de lui. Mais votre adhésion à l'OCP peut être composé vers le bas en échange de la valeur de tous vos amorçage fait en un seul endroit. Une autre option serait d'avoir le programme d'amorçage analyse certaines assemblées pour les registres, en supposant AutoMapper a un tel concept.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X