web-dev-qa-db-fra.com

Automapper - Erreur du mappeur déjà initialisée

J'utilise AutoMapper 6.2.0 dans mon application ASP.NET MVC 5.

Lorsque j'appelle ma vue via le contrôleur, tout se passe bien. Mais, lorsque j'actualise cette vue, Visual Studio affiche une erreur: 

System.InvalidOperationException: 'Le mappeur est déjà initialisé. Vous devez appeler Initialize une fois par domaine d'application/processus. '

J'utilise AutoMapper dans un seul contrôleur. Aucune configuration n'a encore été effectuée à un endroit quelconque, ni AutoMapper utilisé dans un autre service ou contrôleur.

Mon contrôleur:

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

Erreur dans le contrôleur:

 enter image description here

Ma classe de modèle:

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

Ma classe 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; }
}
22
Rahmat Ali

Lorsque vous actualisez la vue, vous créez une nouvelle instance de StudentsController - et donc vous réinitialisez votre mappeur - ce qui génère le message d'erreur "Le mappeur est déjà initialisé".

Depuis le Guide de démarrage

Où est-ce que je configure AutoMapper?

Si vous utilisez la méthode static Mapper, la configuration ne doit avoir lieu qu'une fois par AppDomain. Cela signifie que le meilleur endroit pour insérer le code de configuration est le démarrage de l'application, tel que le fichier Global.asax pour les applications ASP.NET.

Une façon de configurer cela consiste à placer toutes vos configurations de mappage dans une méthode statique.

App_Start/AutoMapperConfig.cs:

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

Puis appelez cette méthode dans le fichier Global.asax.cs

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

Vous pouvez maintenant (ré) l'utiliser dans les actions de votre contrôleur.

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

Si vous souhaitez/devez vous en tenir à l'implémentation statique dans un scénario de test d'unité, notez que vous pouvez appeler AutoMapper.Mapper.Reset() avant d'appeler initialize. Notez que cela ne doit pas être utilisé dans le code de production, comme indiqué dans la documentation. 

Source: http://docs.automapper.org/fr/stable/Configuration.html#resetting-static-mapping-configuration

27
dasch88

J'ai déjà utilisé cette méthode et cela a fonctionné jusqu'à la version 6.1.1

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

Depuis la version 6.2, cela ne fonctionne plus. Pour utiliser correctement Automapper, créez un nouveau mappeur et utilisez-le comme ceci:

 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);
18
Michael Mittermair

Au cas où vous auriez vraiment besoin de "réinitialiser" AutoMapper, vous devriez basculer vers l'API basée sur l'instance pour éviter System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.

Par exemple, lorsque vous créez les tests TestServer pour xUnit, vous pouvez simplement définir ServiceCollectionExtensions.UseStaticRegistration dans le constructeur de classe fixure sur false pour résoudre le problème:

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

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

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

Vous pouvez utiliser automapper comme API statique et API d'instance , Le mappeur déjà initialisé est un problème courant dans l'API statique, vous pouvez utiliser mapper.Reset () où vous avez initialisé le mappeur mais ceci n’est pas une réponse du tout.

Essayez simplement avec l'API d'instance

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);
5
Thisara Subath

Pour le test unitaire, vous pouvez ajouter Mapper.Reset () à votre classe de test unitaire.

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

Version Automapper 8.0.0

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

Vous pouvez simplement utiliser Mapper.Reset().

Exemple: 

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

Si vous utilisez Mapper dans UnitTest et que vos tests sont supérieurs à un, vous pouvez utiliser Mapper.Reset().

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

//Your test classes.

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

Si vous utilisez MsTest, vous pouvez utiliser l'attribut AssemblyInitialize pour que le mappage ne soit configuré qu'une seule fois pour cet Assembly (ici, test Assembly). Ceci est généralement ajouté à la classe de base des tests d'unité de contrôleur.

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

J'espère que cette réponse vous aidera

0
Darshan Sudhakar