【问题标题】:Using AutoMapper QueryableExtensions with Entity Framework in ASP.NET MVC causes an "Operation could destabilize the runtime" exception在 ASP.NET MVC 中将 AutoMapper QueryableExtensions 与 Entity Framework 一起使用会导致“操作可能破坏运行时”异常
【发布时间】:2014-10-03 01:29:40
【问题描述】:

考虑以下代码:

protected override IEnumerable<IListModel> GetListInternal(
    IQueryModel2<Contact> queryModel) {
    /// Causes exception
    return this.Query(queryModel).Project().To<ContactListModel>().AsEnumerable();

    /// Does not cause exception
    return Mapper.Map<IQueryable<Contact>, IEnumerable<ContactListModel>>(this.Query(queryModel)).ToList();
}

当第一行返回时,我得到了异常:操作可能会破坏运行时。当第二行返回时,它工作得很好。据我了解,第二个返回实际上返回一个具体的IEnumerable&lt;ContactListModel&gt;,但第一个返回没有。我是否将.AsEnumerable()ToArray().ToList() 附加到第一个返回并不重要,我只是不断收到异常。因此,Project() 返回的内容肯定有所不同。无论它返回什么都可能是一个IEnumerable&lt;ContactListModel&gt;,从而通过了方法的返回要求,但是当它在管道中进一步处理时,它的具体对象会导致异常。

具体来说,这个方法返回的集合被传递给一个ListWrapper&lt;TEntity&gt; 类,该类进行最后的处理。当我在集合上调用.Count() 时,总是会抛出异常。

我正在拼命寻求解决投影问题的指导,因为第二次返回效率非常低。从 MiniProfiler 告诉有关使用此特定代码的页面的内容来看,我正在进行 1204 个 SQL 查询,有重复,我认为我们所有人都同意这是一个问题......提前感谢您的任何建议!

这是Query()IQueryModel2&lt;TEntity&gt; 的样子:

public IQueryable<TEntity> Query(
    IQueryModel2<TEntity> queryModel = null) {
    return this.QueryInternal(queryModel);
}

private IQueryable<TEntity> QueryInternal(
    IQueryModel2<TEntity> queryModel) {
    IQueryable<TEntity> entities = this.Context.Set<TEntity>();

    if (queryModel != null) {
        if (queryModel.Entities != null) {
            entities = queryModel.Entities.AsQueryable();
        }

        if (queryModel.Predicate != null) {
            entities = entities.AsExpandable().Where(queryModel.Predicate);
        }
    }

    return entities;
}

public interface IQueryModel2<TEntity> {
    IEnumerable<TEntity> Entities { get; set; }

    Expression<Func<TEntity, bool>> Predicate { get; set; }

    SearchPostModel Search { get; set; }
}

更新(1)

public sealed class ContactListModel :
    ListModel {
    [Display(Order = 3)]
    public string Email { get; set; }
    [Display(Order = 1)]
    public string Name { get; set; }
    [Display(Order = 2)]
    public string Phone { get; set; }
    [Display(Order = 4)]
    public ContactType ContactType { get; set; }
}

internal sealed class ContactToContactListModel :
    Profile {
    protected override void Configure() {
        base.Configure();

        base.CreateMap<Contact, ContactListModel>()
            .ForMember(
                d => d.Name,
                o => o.MapFrom(
                    s => (s.FirstName + " " + s.LastName)))
            .IgnoreAllUnmapped();
    }
}

更新(2)

public static class AutoMapperExtensions {
    public static IMappingExpression<TSource, TDestination> IgnoreAllUnmapped<TSource, TDestination>(
        this IMappingExpression<TSource, TDestination> expression) {
        if (expression == null) {
            return null;
        }

        return IgnoreAllUnmappedInternal<TSource, TDestination>(expression, typeof(TSource), typeof(TDestination));
    }

    private static IMappingExpression<TSource, TDestination> IgnoreAllUnmappedInternal<TSource, TDestination>(
        IMappingExpression<TSource, TDestination> expression,
        Type sourceType,
        Type destinationType) {
        IEnumerable<string> unmappedProperties = Mapper.GetAllTypeMaps().First(
            m =>
                m.SourceType.Equals(sourceType)
                && m.DestinationType.Equals(destinationType)).GetUnmappedPropertyNames();

        foreach (string unmappedProperty in unmappedProperties) {
            expression.ForMember(unmappedProperty, o => o.Ignore());
        }

        return expression;
    }
}

更新 3

因此,我将项目的程序集加载到 LinqPad 中,并在其中重新编写了查询代码。感谢来自 LinqPad 的惊人的 .Dump() 方法,我能够观察到返回的对象到底是什么。所以第一个返回默认返回DbQuery&lt;T&gt;,第二个返回默认返回IList&lt;T&gt;。具有讽刺意味的是,当我在 LinqPad 中调用完全相同的代码序列时,异常被 not 抛出,我得到了我期待的 ListWrapper。我真的不确定为什么它在 ASP.NET MVC 中不起作用。虽然我什至不确定这是 ASP.NET 的错,因为它在不同程序集中的库项目的代码中崩溃了......

我仍然不知道如何解决这个异常,因为 LinqPad 没有出现问题。不确定这是否重要,但我在解决方案中的所有项目中都针对 .NET 4.5.2,所以我不确定它是否与此有关?

【问题讨论】:

  • ContactListModel 和 Mapper 的代码在哪里?
  • 嫌疑人夫妇 - IgnoreAllUnmapped 是一个自定义扩展,可能无法处理投影。其次,确保您在启动期间注册了配置文件。第三,我想知道这些 internalsealed 关键字是否会导致任何问题。
  • 我为IgnoreAllUnmapped 的代码添加了另一个更新。删除它并手动忽略目标属性没有效果,它仍然抛出异常。配置文件也在开始时使用WebActivator 注册方式。最后,我不相信这是internalsealed 关键字,但我会在没有它们的情况下试一试。它不起作用,使其 public unsealed 也没有效果。
  • 您也可以尝试使用EFProfiler 来查看生成了哪些查询。这可能会提供一些线索。
  • 我想我已经缩小了一些线索(更新 3),但我仍然没有关于如何解决这个异常的答案......

标签: c# asp.net-mvc automapper


【解决方案1】:

将 Automapper 升级到 v3.2.0。

这解决了我遇到的确切问题。

【讨论】:

    【解决方案2】:

    从 Automapper 版本 7.0.1 开始,在 AzureDevOps 的构建管道中使用 VSTest 平台运行的测试(mstest 运行良好)返回了相同的异常。支持这些测试的最后一个 Automapper 版本是 6.2.2。

    从另一个答案和their github issue 来看,在我看来这是一个回归错误。

    【讨论】:

    • +1 对我来说同样的问题;从 6.x 升级到 8.x 在 Azure DevOps 中导致了这个确切的问题。修复似乎是推出一个新的 VSTest 任务,该任务仍在等待 ATM:github.com/microsoft/vstest/pull/1997
    猜你喜欢
    • 2012-08-22
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 2012-09-15
    相关资源
    最近更新 更多