Automapper - Mapper already initialized error
Asked Answered
P

11

31

I am using AutoMapper 6.2.0 in my ASP.NET MVC 5 application.

When I call my view through controller it shows all things right. But, when I refresh that view, Visual Studio shows an error:

System.InvalidOperationException: 'Mapper already initialized. You must call Initialize once per application domain/process.'

I am using AutoMapper only in one controller. Not made any configuration in any place yet nor used AutoMapper in any other service or controller.

My controller:

public class StudentsController : Controller
{
    private DataContext db = new DataContext();

    // GET: Students
    public ActionResult Index([Form] QueryOptions queryOptions)
    {
        var students = db.Students.Include(s => s.Father);

        AutoMapper.Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<Student, StudentViewModel>();
        });
            return View(new ResulList<StudentViewModel> {
            QueryOptions = queryOptions,
            Model = AutoMapper.Mapper.Map<List<Student>,List<StudentViewModel>>(students.ToList())
        });
    }

    // Other Methods are deleted for ease...

Error within controller:

enter image description here

My Model class:

public class Student
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public string CNIC { get; set; }
    public string FormNo { get; set; }
    public string PreviousEducaton { get; set; }
    public string DOB { get; set; }
    public int AdmissionYear { get; set; }

    public virtual Father Father { get; set; }
    public virtual Sarparast Sarparast { get; set; }
    public virtual Zamin Zamin { get; set; }
    public virtual ICollection<MulaqatiMehram> MulaqatiMehram { get; set; }
    public virtual ICollection<Result> Results { get; set; }
}

My ViewModel Class:

public class StudentViewModel
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }
    public string CNIC { get; set; }
    public string FormNo { get; set; }
    public string PreviousEducaton { get; set; }
    public string DOB { get; set; }
    public int AdmissionYear { get; set; }

    public virtual FatherViewModel Father { get; set; }
    public virtual SarparastViewModel Sarparast { get; set; }
    public virtual ZaminViewModel Zamin { get; set; }
}
Parental answered 11/11, 2017 at 19:1 Comment(0)
C
24

When you refresh the view you are creating a new instance of the StudentsController -- and therefore reinitializing your Mapper -- resulting in the error message "Mapper already initialized".

From the Getting Started Guide

Where do I configure AutoMapper?

If you're using the static Mapper method, configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.asax file for ASP.NET applications.

One way to set this up is to place all of your mapping configurations into a static method.

App_Start/AutoMapperConfig.cs:

public class AutoMapperConfig
{
    public static void Initialize()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<Student, StudentViewModel>();
            ...
        });
    }
}

Then call this method in the Global.asax.cs

protected void Application_Start()
{
    App_Start.AutoMapperConfig.Initialize();
}

Now you can (re)use it in your controller actions.

public class StudentsController : Controller
{
    public ActionResult Index(int id)
    {
        var query = db.Students.Where(...);

        var students = AutoMapper.Mapper.Map<List<StudentViewModel>>(query.ToList());

        return View(students);
    }
}
Curet answered 11/11, 2017 at 20:26 Comment(1)
Thanks for your answer, It solved my issue. But a new question rose in my mind, before updating AutoMapper my code was working very vice without any problem. Then, why this rule of Initialization not made any sense in previous version which was maybe 6.1.0. Can you have any Idea about...???Parental
H
46

If you want/need to stick with the static implementation in a unit testing scenario, note that you can call AutoMapper.Mapper.Reset() before calling initialize. Do note that this should not be used in production code as noted in the documentation.

Source: AutoMapper documentation.

Heterotaxis answered 5/12, 2017 at 17:0 Comment(1)
Great answer, this helped me out immensely.Firebreak
C
24

When you refresh the view you are creating a new instance of the StudentsController -- and therefore reinitializing your Mapper -- resulting in the error message "Mapper already initialized".

From the Getting Started Guide

Where do I configure AutoMapper?

If you're using the static Mapper method, configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.asax file for ASP.NET applications.

One way to set this up is to place all of your mapping configurations into a static method.

App_Start/AutoMapperConfig.cs:

public class AutoMapperConfig
{
    public static void Initialize()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<Student, StudentViewModel>();
            ...
        });
    }
}

Then call this method in the Global.asax.cs

protected void Application_Start()
{
    App_Start.AutoMapperConfig.Initialize();
}

Now you can (re)use it in your controller actions.

public class StudentsController : Controller
{
    public ActionResult Index(int id)
    {
        var query = db.Students.Where(...);

        var students = AutoMapper.Mapper.Map<List<StudentViewModel>>(query.ToList());

        return View(students);
    }
}
Curet answered 11/11, 2017 at 20:26 Comment(1)
Thanks for your answer, It solved my issue. But a new question rose in my mind, before updating AutoMapper my code was working very vice without any problem. Then, why this rule of Initialization not made any sense in previous version which was maybe 6.1.0. Can you have any Idea about...???Parental
I
20

I've used this method before and it worked till version 6.1.1

 Mapper.Initialize(cfg => cfg.CreateMap<ContactModel, ContactModel>()
            .ConstructUsing(x => new ContactModel(LoggingDelegate))
            .ForMember(x => x.EntityReference, opt => opt.Ignore())
        );

Since version 6.2, this doesn't work any more. To correctly use Automapper create a new Mapper and us this one like this:

 var mapper = new MapperConfiguration(cfg => cfg.CreateMap<ContactModel, ContactModel>()
            .ConstructUsing(x => new ContactModel(LoggingDelegate))
            .ForMember(x => x.EntityReference, opt => opt.Ignore())).CreateMapper();

        var model = mapper.Map<ContactModel>(this);
Institutionalize answered 13/11, 2017 at 13:41 Comment(4)
That was the situation I faced. My application was running fine before updating it to 6.2 then after update to 6.2 it started to show that error. Thanks for your answer Man!Parental
Glad that this post helped you. An upvote would be greate. ThxInstitutionalize
Great solution. But why Auto mapper change its core configuration.Amon
Best solution in Febrary 2021. Thanks. here your point goesPolyploid
B
17

In case you really need to "re-initialize" AutoMapper you should switch to the instance based API to avoid System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.

For example, when you are creating the TestServer for xUnit tests you can just set ServiceCollectionExtensions.UseStaticRegistration inside fixure class constructor to false to make the trick:

public TestServerFixture()
{
    ServiceCollectionExtensions.UseStaticRegistration = false; // <-- HERE

    var hostBuilder = new WebHostBuilder()
        .UseEnvironment("Testing")
        .UseStartup<Startup>();

    Server = new TestServer(hostBuilder);
    Client = Server.CreateClient();
}
Beane answered 29/11, 2017 at 12:4 Comment(0)
A
9

You can use automapper as Static API and Instance API , Mapper already initialized is common issue in Static API , you can use mapper.Reset() where you initialized mapper but this this not an answer at all.

Just try with instance API

var students = db.Students.Include(s => s.Father);

var config = new MapperConfiguration(cfg => {
               cfg.CreateMap<Student, StudentViewModel>();        
             });

IMapper iMapper = config.CreateMapper();          
return iMapper.Map<List<Student>, List<StudentViewModel>>(students);
Anacrusis answered 10/8, 2018 at 6:59 Comment(0)
B
8

For Unit Testing, you can add Mapper.Reset() to your unit test class

[TearDown]
public void TearDown()
{
    Mapper.Reset();
}
Babs answered 16/8, 2018 at 12:29 Comment(1)
I suggest to do it in [ClassInitialize] instead of TearDownAulic
R
8

Automapper 8.0.0 version

    AutoMapper.Mapper.Reset();
    Mapper.Initialize(
     cfg => {
         cfg.CreateMap<sourceModel,targetModel>();
       }
    );
Rosenblast answered 5/12, 2018 at 13:10 Comment(1)
Very helpful, thanks - my NUnit tests were failing because they AutoMapper was complaining about being already initialised; I had multiple test suites which would initialise just the profiles they needed, so resetting first made sure previous profiles were unloaded.Unaccomplished
B
1

You can simply use Mapper.Reset().

Example:

public static TDestination MapToObject<TSource, TDestination>(TSource Obj)
{
    Mapper.Initialize(cfg => cfg.CreateMap<TSource, TDestination>());
    TDestination tDestination = Mapper.Map<TDestination>(Obj);
    Mapper.Reset();
    return tDestination;
}
Brunhilde answered 3/9, 2018 at 9:56 Comment(1)
Great answer to the question, But, it should not be used in the production code as stated in @Heterotaxis answer...Parental
C
1

If you are using Mapper in UnitTest and your tests more then one, You may use Mapper.Reset()

`

//Your mapping.
 public static void Initialize()
 {
   Mapper.Reset();                    
   Mapper.Initialize(cfg =>
   {  
       cfg.CreateMap<***>    
   }

//Your test classes.

 [TestInitialize()]
 public void Initialize()
 {
      AutoMapping.Initialize();
 }`
Cornwallis answered 16/11, 2018 at 9:21 Comment(0)
C
1

If you are using MsTest you can use the AssemblyInitialize attribute so that mapping gets configured only once for that assembly (here test assembly). This is generally added into to the base class of controller unit tests.

[TestClass]
public class BaseUnitTest
{
    [AssemblyInitialize]
    public static void AssemblyInit(TestContext context)
    {
        AutoMapper.Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<Source, Destination>()
                .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.EmailAddress));
        });
    }
}

I hope this answer helps

Comely answered 25/2, 2019 at 17:17 Comment(0)
S
0
private static bool _mapperIsInitialized = false;
        public InventoryController()
        {

            if (!_mapperIsInitialized)
            {

                _mapperIsInitialized = true;
                Mapper.Initialize(
                    cfg =>
                    {
                        cfg.CreateMap<Inventory, Inventory>()
                        .ForMember(x => x.Orders, opt => opt.Ignore());
                    }
                    );
            }
        }
Steep answered 20/5, 2019 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.