Automapper + EF4 + ASP.NET MVC - 获取"上下文处理"错误(我知道原因,但是如何修复它?)

Cri*_*scu 6 asp.net-mvc closures entity-framework automapper

我在MVC控制器动作中有这个非常基本的代码.它将Operation模型类映射到非常基本的OperationVM视图模型类.

public class OperationVM: Operation 
{
    public CategoryVM CategoryVM { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我需要加载完整的类别列表才能创建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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我第一次点击页面时一切都很棒.问题是,映射器对象是静态的.因此,在调用时Mapper.CreateMap(),当前的实例将DbContext保存在给予CreateMap()的闭包中.

我第二次点击页面时,静态地图已经到位,仍然使用对初始的引用,现在处理了DbContext.

确切的错误是:

The operation cannot be completed because the DbContext has been disposed.
Run Code Online (Sandbox Code Playgroud)

问题是:如何使AutoMapper始终使用当前上下文而不是最初的上下文?

有没有办法使用automapper的"实例"而不是静态Mapper类?如果可以,建议每次都重新创建映射吗?我担心反思减速.

我读了一些关于自定义解析器的内容,但是我遇到了类似的问题 - 如何让自定义解析器使用当前上下文?

Lef*_*une 8

这是可能的,但设置有点复杂.我在Ninject的帮助下在我的项目中使用它来进行依赖注入.

AutoMapper具有TypeConverters的概念.转换器提供了一种实现在单独的类中转换某些类型所需的复杂操作的方法.如果将Category转换为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以配置AutoMapper以使用您的转换器:

Mapper.CreateMap<Category, CategoryVM>().ConvertUsing<CategoryToCategoryVMConverter>();
Run Code Online (Sandbox Code Playgroud)

这是棘手的部分.每次映射值时,AutoMapper都需要创建转换器的新实例,并且需要为构造函数提供DbContext实例.在我的项目中,我使用Ninject进行依赖注入,并将其配置为在处理请求时使用相同的DbContext实例.这样,在控制器和AutoMapper转换器中都会注入相同的DbContext实例.简单的Ninject配置如下所示:

Bind<DbContext>().To<SomeContext>().InRequestScope();
Run Code Online (Sandbox Code Playgroud)

您当然可以使用某种工厂模式来获取DbContext的实例,而不是在构造函数中注入它.

如果您有任何疑问,请告诉我.