Question

Je configure Automapper dans Bootstrapper et j'appelle Bootstrap () dans Application_Start () , et on m'a dit que c'était faux parce que j'avais modifier ma classe Bootstrapper chaque fois que je dois ajouter un nouveau mappage, de sorte que je viole 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>();
    }    
}
Était-ce utile?

La solution

Je soutiens que vous enfreignez deux principes: le principe de responsabilité unique (SRP) et le principe d'ouverture / fermeture (OCP).

Vous violez le SRP car la classe d'amorçage a plus d'une raison de changer: si vous modifiez la liaison de modèle ou la configuration du mappeur automatique.

Si vous ajoutiez du code d'amorçage supplémentaire pour configurer un autre sous-composant du système, vous violeriez le code OCP.

Comme je le fais habituellement, je définis l'interface suivante.

public interface IGlobalConfiguration
{
    void Configure();
}

Pour chaque composant du système nécessitant un amorçage, je créerais une classe qui implémenterait 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 pour injecter les dépendances. IConfiguration est l'implémentation sous-jacente de la classe statique AutoMapper et ModelBinderDictionary est l'objet ModelBinders.Binder . Je définirais ensuite un NinjectModule qui analyserait l'assembly spécifié pour toute classe implémentant l'interface IGlobalConfiguration et ajouterait 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 au fichier Global.asax.

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 à OCP. Je peux facilement ajouter du code d'amorçage supplémentaire en créant une classe qui implémente l'interface IGlobalConfiguration et mes classes de configuration globales n'ont qu'un seul motif à changer.

Autres conseils

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

Cependant, il est utile de centraliser certaines choses du point de vue de la possibilité de procéder à une ingénierie inverse.

Dans NInject, il existe la notion d'avoir un module par projet ou sous-système (ensemble de projets), ce qui semble un compromis judicieux.

Je sais que c’est un ancien logiciel, 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 cette question. 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

Si vous enfreignez le principe de responsabilité unique que vous violez, la classe a plus d'une raison de changer.

Personnellement, j'aurais une classe ConfigureAutoMapper avec laquelle toute ma configuration pour AutoMapper a été effectuée. Mais on pourrait soutenir qu’il s’agit d’un choix personnel.

Omu, je suis aux prises avec des questions similaires pour amorcer un conteneur IoC dans la routine de démarrage de mon application. Pour IoC, les conseils qui m'ont été donnés montrent qu'il est avantageux de centraliser votre configuration plutôt que de l'éparpiller dans votre application lorsque vous ajoutez des modifications. Pour configurer AutoMapper, je pense que l’avantage de la centralisation est beaucoup moins important. Si vous pouvez obtenir votre conteneur AutoMapper dans votre conteneur IoC ou dans votre service de recherche de service, je suis d'accord avec la suggestion de Ruben Bartelink de configurer les mappages une fois par assemblage ou dans des constructeurs statiques ou quelque chose de décentralisé.

En gros, j’ai le sentiment qu’il s’agit de décider si vous souhaitez centraliser le démarrage ou le décentraliser. Si vous êtes vraiment préoccupé par le principe d'ouverture / fermeture de votre routine de démarrage, commencez par le décentraliser. Mais votre adhésion à OCP peut être diminuée en échange de la valeur de tous vos bootstrap effectués au même endroit. Une autre option serait de demander au programme de démarrage d'analyser certains assemblys à la recherche de registres, en supposant qu'AutoMapper dispose d'un tel concept.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top