请考虑以下代码:
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<ContactListModel>
,但第一次返回没有。如果我将.AsEnumerable()
,ToArray()
或.ToList()
追加到第一次回归并不重要,我只是继续获得例外。因此,必须有一些关于Project()
返回的东西。无论它返回什么,可能是一个IEnumerable<ContactListModel>
因此传递方法的返回要求,但它的具体对象是在它进一步处理管道时导致异常。
具体来说,此方法返回的集合将传递到执行最终处理的ListWrapper<TEntity>
类。当我在集合上调用.Count()
时,总会抛出异常。
我绝望地寻求关于通过预测解决问题的指导,因为第二次回报非常低效。从MiniProfiler告诉关于这个特定代码使用的页面,我正在制作1204个SQL查询,带有重复项,我想我们所有人都同意这是一个问题...提前感谢任何建议!
这是Query()
和IQueryModel2<TEntity>
的样子:
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<T>
,第二个返回默认返回IList<T>
。具有讽刺意味的是,当我在LinqPad中调用完全相同的代码序列时,它不会抛出异常,我得到了我期待的ListWrapper。我真的不确定为什么它在ASP.NET MVC中不起作用。虽然我甚至不确定它是ASP.NET的错,因为它在代码中崩溃,这是一个不同程序集中的库项目......
由于LinqPad没有出现问题,我仍然对如何修复此异常感到茫然。不确定它是否重要,但我在解决方案的所有项目中都以.NET 4.5.2为目标,所以我不确定它是否与此相关?
将Automapper升级到v3.2.0。
这解决了我遇到的确切问题。
对于使用VSTest平台(mstests运行良好)在AzureDevOps的构建管道中运行的测试,回到同样的异常,从Automapper 7.0.1开始。支持这些测试的最后一个版本的Automapper是6.2.2。
从另一个答案和their github issue来看,在我看来这是一个回归错误。