12

I've upgraded to the latest version of AutoMapper (9.0) and I've changed the static configuration to:

public static IMapper RegisterAutoMapper()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<MyModel MyDto>;
        //etc...
   });
  
   var mapper = config.CreateMapper();
   return mapper;
}

Using the previous static API I used to do the following in Global.asax:

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
    AutoMapping.Map();
}

WebApiConfig.Register registers the routes and also Autofac

How do I register AutoMapper with Autofac because currently I'm getting compiler errors on such lines:

var myDto = Mapper.Map<MyModel>(model);

And the compiler error:

An object reference is required for the non-static field, method, or property 'Mapper.Map(object)'

Igor Popov
  • 9,795
  • 7
  • 55
  • 68
Ivan-Mark Debono
  • 15,500
  • 29
  • 132
  • 263
  • 2
    You probably need to include [a minimal, reproducible example here](https://stackoverflow.com/help/minimal-reproducible-example). There's a lot of dots not connected for the reader - `AutoMapping.Map` isn't shown; `RegisterAutoMapper` exists but no one knows when it's called; the code around `myDto` isn't shown so we don't know where `Mapper` comes from. – Travis Illig Aug 16 '19 at 16:57

4 Answers4

13

Here's one I made earlier:

public class YourAutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        //Also register any custom type converter/value resolvers
        builder.RegisterType<CustomValueResolver>().AsSelf();
        builder.RegisterType<CustomTypeConverter>().AsSelf();

        builder.Register(context => new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<MyModel MyDto>;
            //etc...
        })).AsSelf().SingleInstance();

        builder.Register(c =>
        {
            //This resolves a new context that can be used later.
            var context = c.Resolve<IComponentContext>();
            var config = context.Resolve<MapperConfiguration>();
            return config.CreateMapper(context.Resolve);
        })
        .As<IMapper>()
        .InstancePerLifetimeScope();
    }
}

In the global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        var builder = new ContainerBuilder();

        builder.RegisterModule<MyAutofacModule>();
        // Register anything else needed

        var container = builder.Build();

        // MVC resolver
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

        // API Resolver
        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }
}

Then all you need to do is inject IMapper

andyb952
  • 1,931
  • 11
  • 25
7

There is also a nuget-package that does all of that for you.

All you need to do is to call an extension method on the ContainerBuilder and pass in the assemblies, that should be scanned for AutoMapper types.

var containerBuilder = new ContainerBuilder();
containerBuilder.AddAutoMapper(typeof(MvcApplication).Assembly);
// more registrations here

You can find it here. You can find an official example in the AutoMapper docs as well.

Edit: There are samples for ASP.NET Core and Console-Applications here.

alsami
  • 8,996
  • 3
  • 25
  • 36
  • What does argument in `AddAutoMapper` mean? Why is it `typeof(MvcApplication)`? I have project where I have DTOs, and map profiles. But when I set this project name to `typeof(...)`, there is error because this is namespace, not assembly. How is it possible to figure out what should be the assemble name please? – kosist Jun 14 '20 at 21:03
  • 2
    The type passed into `typeof(...).Assembly` is the assembly that contains your profiles and other `AutoMapper` types. So it's specific to your project and must be a type available in the project. – alsami Jun 15 '20 at 04:59
  • Thank you for the answer. But let me ask a bit more. I have project, which contains 2 classes, which are profiles for automapper. When I check project's properties, I see there assembly name. When I build entire solution, and use that name in `typeof(...)` parameter, Visual Studio takes that name as namespace, not assembly. But when I've tried to pass there name of one of the profile classes, both 2 profiles were mapped properly (I use assert to test profile's configuration). Should I put those 2 profile classes into 3rd class, or smth like that please? – kosist Jun 15 '20 at 08:06
  • 3
    If the project contains both profiles, you can choose what-ever type you want to get the Assembly of the project. The project produces the assembly (dll, or .exe file). If you have several projects containing profiles, you can pass them in comma separated. For instance like this `builder.AddAutoMapper(typeof(ProfileA).Assembly, typeof(ProfileB).Assembly)`. What's happening here is called assembly scanning. Basically this line adds all profiles and other automapper types using the assemblies you have passed in. – alsami Jun 15 '20 at 12:47
  • `AddAutoMapper` is now obsolete. Use `RegisterAutoMapper` – kiafiore Jul 21 '23 at 09:16
1

This is an implmentation I applied to a Web API 2 project, in NET Framework 4.7.2, but despite how old it may look, you can use it too.

Initialization

Notice second line. That's were you'll initialize the Autofact static class.

// Global.asax
protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  AutofacConfig.RegisterComponents(); // IoC, AUTOFAC
  GlobalConfiguration.Configure(WebApiConfig.Register);
  FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
  RouteConfig.RegisterRoutes(RouteTable.Routes);
  BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Autofact Configuration

  • This is the whole configuration file for Autofac. It is a static class.
  • Notice the #region MAPPING
// AutofacConfig.cs
public static class AutofacConfig
{
  public static void RegisterComponents()
  {
    var builder = new ContainerBuilder();

    // Get your HttpConfiguration.
    var config = GlobalConfiguration.Configuration;

    // Register your Web API controllers.
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // OPTIONAL: Register the Autofac filter provider.
    builder.RegisterWebApiFilterProvider(config);

    // OPTIONAL: Register the Autofac model binder provider.
    builder.RegisterWebApiModelBinderProvider();

    #region MAPPING

    // 1 - Mapper configuration is done here
    // Notice the AutoMapperProfile instantiation
    var mapperConfiguration = new MapperConfiguration(cfg => { cfg.AddProfile(new AutoMapperProfile()); });

    // 2 - After configuring the mapper, you create it
    var mapper = mapperConfiguration.CreateMapper();

    // 3 - Register that instance of the mapper, which implements IMapper interface
    builder.RegisterInstance<IMapper>(mapper);

    // 4 - Now you register the TestMapper that you'll inject somewehre in your code, let's say, in a controller
    // Register as many mappers as you need
        
    builder.Register(c => new TestMapper(c.Resolve<IMapper>())).As<ITestMapper>();

    #endregion

    // Set the dependency resolver to be Autofac.
    var container = builder.Build();
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
  }
}

Automapper Profile

  • As you can see, this class extends Profile from AutoMapper namespace.
  • Here you will create all the mapping you may need. This is the actual configuration for AutoMapperProfile we did above.
  • This is the file you call from AutofacConfig.cs
// AutoMapperProfile.cs
public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Test, TestDto>()
            .ForMember(destination => destination.Floating, options => options.MapFrom(source => source.flotante))
            .ForMember(dto => dto.Integer, opt => opt.MapFrom(src => src.entero))
            ;
        
        // Test is a model/entity from the DB
        // TestDto is a class I use as a Data Transfer Object
    }
}

TestMapper

  • Here's where you inject the mapper into TestMapper.
  • ITestMapper is your custom definition.
// TestMapper.cs
public class TestMapper : ITestMapper
{
    IMapper _mapper;

    public TestMapper(IMapper mapper)
    {
        _mapper = mapper;
    }

    public TestDto GetTestItem(Test test)
    {
        return _mapper.Map<TestDto>(test);
    }

    // You may add additional implementation here.
}

Use Example

  • Now you can inject TestMapper as ITestMapper wherever you may need it.
  • Notice this is an ApiController inheritor.
// TestController.cs
[RoutePrefix("api/test")]
public class TestController : ApiController
{
    private ITestMapper _testMapper;

    public TestController(, ITestMapper testMapper)
    {
        _testMapper = testMapper;
    }

    [HttpGet]
    [Route("GetTestItem/{id:int}")]
    // GET api/<controller>/GetTestItem/5
    public Prueba GetTestItem(int id)
    {
        var itemDb = _testRepository.GetTestItem(id); // it could be a service
        var itemDto = _testMapper.GetTestItem(itemDb)

        // Return the mapped object
        return itemDto;
    }
}
carloswm85
  • 1,396
  • 13
  • 23
0

While I'm not familiar with Autofac myself, here is a recent article that descibes how to set up injection for automapper with Autofac.

Goodluck!