Automapper - уже инициализированная ошибка Mapper

Я использую AutoMapper 6.2.0 в моем приложении ASP.NET MVC 5.

Когда я вызываю свое мнение через контроллер, он показывает все правильно. Но когда я обновляю это представление, Visual Studio показывает ошибку:

System.InvalidOperationException: 'Mapper уже инициализирован. Вы должны вызывать Инициализировать один раз на домен/процесс приложения. '

Я использую AutoMapper только в одном контроллере. Не была выполнена какая-либо конфигурация в любом месте и не использовалась AutoMapper в любой другой службе или контроллере.

Мой контроллер:

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

Ошибка в контроллере:

enter image description here

Мой класс модели:

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

Мой класс ViewModel:

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

Ответ 1

Когда вы обновляете представление, вы создаете новый экземпляр StudentsController - и, следовательно, повторно инициализируете свой Mapper, в результате появляется сообщение об ошибке "Mapper уже инициализирован".

Из руководства по началу работы

Где я могу настроить AutoMapper?

Если вы используете статический метод Mapper, конфигурация должна выполняться только один раз для AppDomain. Это означает, что лучше всего разместить код конфигурации в запуске приложения, например, файл Global.asax для приложений ASP.NET.

Один из способов установить это - поместить все ваши конфигурации сопоставлений в статический метод.

App_Start/AutoMapperConfig.cs:

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

Затем вызовите этот метод в файле Global.asax.cs

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

Теперь вы можете (повторно) использовать его в своих действиях контроллера.

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

Ответ 2

Если вы хотите/должны придерживаться статической реализации в сценарии модульного тестирования, обратите внимание, что вы можете вызвать AutoMapper.Mapper.Reset() перед вызовом initialize. Обратите внимание, что это не должно использоваться в рабочем коде, как указано в документации.

Источник: документация AutoMapper.

Ответ 3

Я использовал этот метод раньше, и он работал до версии 6.1.1

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

Начиная с версии 6.2, это больше не работает. Чтобы правильно использовать Automapper, создайте новый Mapper, и нам это будет выглядеть следующим образом:

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

Ответ 4

Если вам действительно нужно "повторно инициализировать" AutoMapper вы должны переключиться на API на основе экземпляра, чтобы избежать System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process. Mapper already initialized. You must call Initialize once per application domain/process.

Например, когда вы создаете TestServer для тестов xUnit вы можете просто установить ServiceCollectionExtensions.UseStaticRegistration внутри fixure класса fixure false чтобы сделать трюк:

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

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

    Server = new TestServer(hostBuilder);
    Client = Server.CreateClient();
}

Ответ 5

Для модульного тестирования вы можете добавить Mapper.Reset() в класс тестирования устройства

[TearDown]
public void TearDown()
{
    Mapper.Reset();
}

Ответ 6

Вы можете использовать automapper как Static API и Instance API, уже инициализированный Mapper является общей проблемой в Static API, вы можете использовать mapper.Reset(), где вы инициализировали mapper, но это не совсем ответ.

Просто попробуйте использовать 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);

Ответ 7

Версия Automapper 8.0.0

    AutoMapper.Mapper.Reset();
    Mapper.Initialize(
     cfg => {
         cfg.CreateMap<sourceModel,targetModel>();
       }
    );

Ответ 8

Вы можете просто использовать Mapper.Reset().

Пример:

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

Ответ 9

Если вы используете Mapper в UnitTest и ваши тесты больше одного, вы можете использовать Mapper.Reset()

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

//Your test classes.

 [TestInitialize()]
 public void Initialize()
 {
      AutoMapping.Initialize();
 }

Ответ 10

Если вы используете MsTest, вы можете использовать атрибут AssemblyInitialize, чтобы сопоставление настраивалось только один раз для этой сборки (здесь тестовая сборка). Это обычно добавляется в базовый класс тестов модулей контроллера.

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

Надеюсь этот ответ поможет

Ответ 11

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