Automapper - 映射器已初始化错误

Automapper - Mapper already initialized error

我在我的 ASP.NET MVC 5 应用程序中使用 AutoMapper 6.2.0。

当我通过控制器调用我的视图时,它显示一切正常。但是,当我刷新该视图时,Visual Studio 显示错误:

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

我只在一个控制器中使用 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...

控制器内错误:

我的模型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; }
}

我的视图模型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; }
}

当您刷新视图时,您正在创建 StudentsController 的新实例——因此会重新初始化您的 Mapper——导致错误消息 "Mapper already initialized".

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

设置它的一种方法是将所有映射配置放入静态方法中。

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

我以前用过这个方法,一直用到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);

如果你真的需要 "re-initialize" AutoMapper 你应该 switch to the instance based API 避免 System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.

例如,当您为 xUnit 测试创建 TestServer 时,您只需将 fixure class 构造函数中的 ServiceCollectionExtensions.UseStaticRegistration 设置为 false 使技巧:

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

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

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

如果您 want/need 在单元测试场景中坚持使用静态实现,请注意您可以在调用初始化之前调用 AutoMapper.Mapper.Reset()。请注意,如文档中所述,不应在生产代码中使用它。

来源:AutoMapper documentation.

您可以将自动映射器用作 静态 API实例 APIMapper already initialized 是 Static API 中的常见问题,您可以使用 mapper.Reset() 您在哪里初始化映射器,但这根本不是答案。

用实例试试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);

对于单元测试,您可以将 Mapper.Reset() 添加到单元测试中 class

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

您可以简单地使用 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;
}

如果您在 UnitTest 中使用 Mapper 并且您的测试不止一个,您可以使用 Mapper.Reset()

`

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

//Your test classes.

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

Automapper 8.0.0 版本

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

如果您使用的是 MsTest,则可以使用 AssemblyInitialize 属性,以便为该程序集(此处为测试程序集)仅配置一次映射。这通常添加到控制器单元测试的基础 class 中。

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

希望这个回答对您有所帮助

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