首页 文章

Automapper - Mapper已初始化错误

提问于
浏览
23

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

当我通过控制器调用我的视图时,它显示所有正确的事情 . 但是,当我刷新该视图时,Visual Studio显示错误:

System.InvalidOperationException:'Mapper已初始化 . 您必须为每个应用程序域/进程调用一次Initialize .

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

我的Model类:

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

10 回答

  • 18

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

    来自Getting Started Guide

    我在哪里配置AutoMapper?如果您使用静态Mapper方法,则每个AppDomain只应进行一次配置 . 这意味着放置配置代码的最佳位置是应用程序启动,例如ASP.NET应用程序的Global.asax文件 .

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

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

    如果您希望/需要在单元测试场景中坚持使用静态实现,请注意您可以在调用initialize之前调用 AutoMapper.Mapper.Reset() . 请注意,这不应该在文档中提到的 生产环境 代码中使用 .

    资料来源:http://docs.automapper.org/en/stable/Configuration.html#resetting-static-mapping-configuration

  • 22

    我之前使用过这种方法,直到版本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

    万一你真的需要"re-initialize" AutoMapper 你应该switch to the instance based API以避免 System.InvalidOperationExceptionMapper already initialized. You must call Initialize once per application domain/process.

    例如,当您为 xUnit 测试创建 TestServer 时,您可以将 fixure 类构造函数中的 ServiceCollectionExtensions.UseStaticRegistration 设置为 false 来制作技巧:

    public TestServerFixture()
    {
        ServiceCollectionExtensions.UseStaticRegistration = false; // <-- HERE
    
        var hostBuilder = new WebHostBuilder()
            .UseEnvironment("Testing")
            .UseStartup<Startup>();
    
        Server = new TestServer(hostBuilder);
        Client = Server.CreateClient();
    }
    
  • 0

    您可以使用automapper作为 Static APIInstance API ,已经初始化的Mapper是静态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);
    
  • 0

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

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

    Automapper 8.0.0 version

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

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

    如果你在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();
     }
    
  • 4

    如果您正在使用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));
            });
        }
    }
    

    我希望这个答案有所帮助

相关问题