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