web-dev-qa-db-ja.com

Automapper-マッパーはすでに初期化済みのエラーです

ASP.NET MVC 5アプリケーションでAutoMapper 6.2.0を使用しています。

コントローラを介してビューを呼び出すと、すべてのものが正しく表示されます。しかし、そのビューを更新すると、Visual Studioにエラーが表示されます。

System.InvalidOperationException: 'マッパーは既に初期化されています。初期化は、アプリケーションドメイン/プロセスごとに1回呼び出す必要があります。

AutoMapperは1つのコントローラーでのみ使用しています。まだどこでも設定を行っておらず、他のサービスやコントローラーで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; }
}
26
Rahmat Ali

ビューを更新すると、StudentsControllerの新しいインスタンスが作成されるため、マッパーが再初期化され、エラーメッセージ "マッパーは既に初期化されています"が表示されます。

スタートガイド から

AutoMapperはどこで設定しますか?

静的なマッパーメソッドを使用している場合、構成はAppDomainごとに1回だけ行う必要があります。つまり、構成コードを配置する最適な場所は、ASP.NETアプリケーションのGlobal.asaxファイルなどのアプリケーションの起動時です。

これを設定する1つの方法は、すべてのマッピング構成を静的メソッドに配置することです。

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

単体テストのシナリオで静的実装に固執したい/必要な場合は、初期化を呼び出す前にAutoMapper.Mapper.Reset()を呼び出すことができることに注意してください。ドキュメントに記載されているように、これは本番コードでは使用しないでください。

ソース: AutoMapperドキュメント

37
dasch88

以前にこの方法を使用したことがあり、バージョン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を正しく使用するには、新しいマッパーを作成します。このマッパーは次のようになります。

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

AutoMapperを本当に「再初期化」する必要がある場合は、 インスタンスベースのAPIに切り替える を使用して、System.InvalidOperationExceptionを回避する必要があります:Mapper already initialized. You must call Initialize once per application domain/process.

たとえば、TestServerテスト用にxUnitを作成する場合、fixureクラスコンストラクター内でServiceCollectionExtensions.UseStaticRegistrationfalseに設定するだけで、トリックを作成できます。

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

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

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

単体テストの場合、Mapper.Reset()を単体テストクラスに追加できます。

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

Automapper 8.0.0バージョン

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

静的APIおよびインスタンスAPIとしてオートマッパーを使用できます。マッパーは既に初期化されていますは、静的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);
5
Thisara Subath

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

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();
 }
0
adaniak
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());
                    }
                    );
            }
        }
0
Nikola Nikolov

MsTestを使用している場合、AssemblyInitialize属性を使用して、マッピングがそのアセンブリ(ここではテストアセンブリ)に対して1回だけ構成されるようにすることができます。これは通常、コントローラーユニットテストの基本クラスに追加されます。

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

この答えがお役に立てば幸いです

0