Automapper + EF4 + ASP.NET MVC - 让“设置上下文”的错误(我知道为

2019-07-30 17:28发布

我有一个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()当前的实例DbContext保存在()给CreateMap关闭。

在第二次我打的页面,静态地图已经到位,仍然使用参考最初的,现在布置, DbContext

确切的错误是:

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

现在的问题是:怎样才能让AutoMapper总是使用当前背景下,而不是最初的一个吗?

有没有办法使用的automapper的“实例”,而不是静态的方式Mapper类? 如果这是可能的,但它建议每次都重新创建映射? 我很担心反射缓慢起伏。

我读了一些关于自定义冲突解决,但我得到了类似的问题 - 如何获得自定义解析器使用当前的背景下?

Answer 1:

这是可能的,但设置有点复杂。 我在我的项目使用与Ninject的帮助依赖注入。

AutoMapper有类型转换器的概念。 转换器提供了一种方法来实现某些类型转换成一个单独的类需要复杂的操作。 如果转换到类需要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的实例,而不是在构造函数中注入它的。

让我知道如果您有任何问题。



Answer 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完成...



Answer 3:

从@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));
};


文章来源: Automapper + EF4 + ASP.NET MVC - getting 'context disposed' error (I know why, but how to fix it?)