1
\$\begingroup\$

I have the following code to register the dependency injection for automapper:

        // Register Automapper
        services.AddAutoMapper(
            typeof(ApiProfile),
            typeof(DomainToViewModelProfile),
            typeof(DomainToDomainProfile));

        // register lazy load automapper

        var mapperConfig = new MapperConfiguration(mc =>
        {
            // Register AutoMapper Profiles
            mc.AddProfile(new ApiProfile());
            mc.AddProfile(new DomainToViewModelProfile());
            mc.AddProfile(new DomainToDomainProfile());
        });

        IMapper mapper = mapperConfig.CreateMapper();

        services.AddScoped(provider => new Lazy<IMapper>(mapper));

This all works fine but I was wondering if there was a better way to register this as it seems daft that I have to list them profiles in both places - is there a way of adding a scoped IMapper using the config / mapper created for the lazy loading (or make this code a bit cleaner in any way)?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

How about having a Type[] where you can list all the profiles?

var profileTypes = new []
{
   typeof(ApiProfile),
   typeof(DomainToViewModelProfile),
   typeof(DomainToDomainProfile))
};

According to this you can simply pass the collection as it is to the AddAutoMapper

services.AddAutoMapper(profileTypes);

Then you can use Activator.CreateInstance method to create instances from the above types:

var mapperConfig = new MapperConfiguration(mc =>
{
    foreach(var profileType in profileTypes)
    {
       var profile = (Profile)Activator.CreateInstance(profileType);
       mc.AddProfile(profile);
    }
});

Disclaimer: I haven't used AutoMapper for awhile and I haven't tested the above proposed solution.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks this works well, I'm just going to leave this open in case anyone knows of a way to use the mapper created for the lazy scoped \$\endgroup\$
    – Pete
    Apr 22, 2022 at 12:05
1
\$\begingroup\$

Quick & dirty

Use the IServiceProvider available to resolve the IMapper instance and put it inside the Lazy:

services.AddScoped(provider =>
{
    var mapper = provider.GetService<IMapper>();

    return new Lazy<IMapper>(mapper);
});

Future-proof

If you want a more future-proof solution, you can create a generic lazy service provider, that will allow you to inject any from your registered services wrapped in Lazy<T> like Lazy<IMapper>. But, be warned that open generic types are not validated during startup services validation.

services.AddTransient(typeof(Lazy<>), typeof(LazyServiceProvider<>));

The lazy service provider:

public class LazyServiceProvider<T> : Lazy<T>
    where T : class
{
    public LazyServiceProvider(IServiceProvider provider)
        : base(() => provider.GetRequiredService<T>())
    {
    }
}

Final note

AutoMapper's extension method accepts an array of types being assembly marker types, not mapping profile types. An assembly marker type is a type used for scanning the type's origin assembly for all mapping profiles that are contained in that assembly. All types matching AutoMapper's criteria are later on registered in order to create the mapper. So if those three types:

services.AddAutoMapper(
            typeof(ApiProfile),
            typeof(DomainToViewModelProfile),
            typeof(DomainToDomainProfile));

are in the same assembly (project), then it is redundant. Luckily for you, the authors of AutoMapper are selecting only distinct assemblies, so no harm done, but it is still a code smell. If you had a large solution with dozens of mapping profiles, this marker type approach becomes handy, as you only need to pass one type for an assembly containing mapping profiles. It doesn't even have to be a mapping profile, it can be just any type representing the assembly. So, if those three types are in the same assembly, you can pass only one of them, and it will work just as before:

services.AddAutoMapper(typeof(ApiProfile));

Assembly type scanning is also used in, for example, another great library called Scrutor, so it's not that weird or uncommon thing to do.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.