6

私はMVCコントローラーアクションにこの本当に基本的なコードを持っています。Operationモデルクラスを非常に基本的なOperationVMビューモデルクラスにマップします。

public class OperationVM: Operation 
{
    public CategoryVM CategoryVM { get; set; }
}

CategoryVMインスタンスを作成するには、カテゴリの完全なリストをロードする必要があります。ビューに表示するを
作成する方法は次のとおりです。List<OperationVM>

public class OperationsController : Controller {

    private SomeContext context = new SomeContext ();

    public ViewResult Index()
    {
        var ops = context.Operations.Include("blah...").ToList();
        Mapper.CreateMap<Operation, OperationVM>()
            .ForMember(
                dest => dest.CategoryVM, 
                opt => opt.MapFrom(
                    src => CreateCatVM(src.Category, context.Categories)
                    //  trouble here ----------------^^^^^^^
                )
            );
        var opVMs = ops.Select(op => Mapper.Map<Operation, OperationVM>(op))
                       .ToList();

        return View(opVMs);
    }
}

初めてページにアクセスしたときは、すべてうまくいきました。問題は、マッパーオブジェクトが静的であるということです。したがって、を呼び出すMapper.CreateMap()と、currentのインスタンスがDbContextCreateMap()に指定されたクロージャに保存されます。

2回目にページにアクセスしたとき、静的マップはすでに配置されており、現在は破棄されている最初のへの参照を使用していますDbContext

正確なエラーは次のとおりです。

The operation cannot be completed because the DbContext has been disposed.

問題は、AutoMapperが最初のコンテキストではなく常に現在のコンテキストを使用するようにするにはどうすればよいですか?

Mapper静的クラスの代わりにオートマッパーの「インスタンス」を使用する方法はありますか?これが可能な場合は、毎回マッピングを再作成することをお勧めしますか?反射が遅くなるのが心配です。

カスタムリゾルバーについて少し読みましたが、同様の問題が発生します-カスタムリゾルバーに現在のコンテキストを使用させるにはどうすればよいですか?

4

3 に答える 3

8

可能ですが、設定が少し複雑です。依存性注入のために Ninject の助けを借りて、プロジェクトでこれを使用しています。

AutoMapper には TypeConverters の概念があります。コンバーターは、特定の型を別のクラスに変換するために必要な複雑な操作を実装する方法を提供します。CategoryVM への変換にデータベース ルックアップが必要な場合は、次のようなカスタム TypeConverter クラスにそのロジックを実装できます。

using System;
using AutoMapper;

public class CategoryToCategoryVMConverter : 
        TypeConverter<Category, CategoryVM>
{
    public CategoryToCategoryVMConverter(DbContext context)
    {
        this.Context = context;
    }

    private DbContext Context { get; set; }

    protected override CategoryVM ConvertCore(Category source)
    {
        // use this.Context to lookup whatever you need
        return CreateCatVM(source, this.Context.Categories);
    }
}

次に、コンバーターを使用するように AutoMapper を構成します。

Mapper.CreateMap<Category, CategoryVM>().ConvertUsing<CategoryToCategoryVMConverter>();

ここがトリッキーな部分です。AutoMapper は、値をマップするたびにコンバーターの新しいインスタンスを作成する必要があり、コンストラクターに DbContext インスタンスを提供する必要があります。私のプロジェクトでは、依存性注入に Ninject を使用しており、リクエストの処理中に DbContext の同じインスタンスを使用するように構成されています。このようにして、DbContext の同じインスタンスがコントローラーと AutoMapper コンバーターの両方に挿入されます。簡単な Ninject 構成は次のようになります。

Bind<DbContext>().To<SomeContext>().InRequestScope();

もちろん、ある種のファクトリ パターンを使用して、コンストラクターに挿入する代わりに DbContext のインスタンスを取得できます。

ご不明な点がございましたら、お知らせください。

于 2012-09-07T18:29:19.497 に答える
2

完全にハックではない回避策を見つけました。基本的に、AutoMapper にトリッキーなフィールドを無視するように指示し、自分で更新します。

更新されたコントローラーは次のようになります。

public class OperationsController : Controller {

    private SomeContext context = new SomeContext ();

    public ViewResult Index()
    {
        var ops = context.Operations.Include("blah...").ToList();
        Mapper.CreateMap<Operation, OperationVM>()
            .ForMember(dest => dest.CategoryVM, opt => opt.Ignore());

        var opVMs = ops.Select(
            op => {
                var opVM = Mapper.Map<Operation, OperationVM>(op);
                opVM.CategoryVM = CreateCatVM(op.Category, context.Categories);
                return opVM;
            })
            .ToList();

        return View(opVMs);
    }
}

これが AutoMapper 内からどのように行われるのか、まだ興味があります...

于 2012-08-01T02:14:41.603 に答える
0

@LeffeBruneからの答えは完璧です。ただし、同じ動作をしたいのですが、すべてのプロパティを自分でマップしたくありません。基本的に、「ConstructUsing」をオーバーライドしたかっただけです。

これが私が思いついたものです。

public static class AutoMapperExtension
{
    public static void ConstructUsingService<TSource, TDestination>(this IMappingExpression<TSource, TDestination> mappingExression, Type typeConverterType)
    {
        mappingExression.ConstructUsing((ResolutionContext ctx) =>
        {
            var constructor = (IConstructorWithService<TSource, TDestination>)ctx.Options.ServiceCtor.Invoke(typeConverterType);
            return constructor.Construct((TSource)ctx.SourceValue);
        });
    }
}

public class CategoryToCategoryVMConstructor : IConstructorWithService<Category, CategoryVM>
{
    private DbContext dbContext;

    public DTOSiteToHBTISiteConverter(DbContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public CategoryVM Construct(Category category)
    {
        // Some commands here
        if (category.Id > 0)
        {
            var vmCategory = dbContext.Categories.FirstOrDefault(m => m.Id == category.Id);
            if (vmCategory == null)
            {
                throw new NotAllowedException();
            }

            return vmCategory;
        }

        return new CategoryVM();
    }
}

// Initialization
Mapper.Initialize(cfg =>
{
    cfg.ConstructServicesUsing(type => nInjectKernelForInstance.Get(type));
    cfg.CreateMap<Category, CategoryVM>().ConstructUsingService(typeof(CategoryToCategoryVMConstructor));
};
于 2015-03-05T21:44:04.640 に答える