为什么 IQueryable.Select 每次迭代都使用相同的引用

Why does IQueryable.Select use the same reference for each iteration

我有一个 IQueryable,我想对其执行 Select。在那个 select 我创建了一个对象的新实例和 运行 一个函数,该函数将对象的值从 IQueryable(b) 复制到新创建的对象(新 DTO),然后 returns 这个实例。

IQueryable.Select:

businessLayer.GetAll().Select( b => new DTO().InitInhertedProperties(b)).ToList();

DTO 中的函数:

public DTO InitInhertedProperties(Base baseInstance)
{
    return Utilities.InitInhertedProperties(this, baseInstance);
}

复制功能:

public static T InitInhertedProperties<T,K>(T instance, K baseClassInstance)  where T : K
{
    foreach (PropertyInfo propertyInfo in baseClassInstance.GetType().GetProperties())
    {
        object value = propertyInfo.GetValue(baseClassInstance, null);
        if (null != value) propertyInfo.SetValue(instance, value, null);
    }

    return instance;
}

第一次调用 InitInhertedProperties 方法时 instance 是一个空对象,baseClassInstance 具有它应该具有的值:

第一次迭代的结果如下所示:

如您所见:一切都在第一次迭代中进行。现在是第二次迭代。

第二次调用 InitInhertedProperties 方法 insatnce 不是新实例,而是第一次迭代的实例。 baseClassInstance 正是它应该的样子:

第二次迭代的结果如下所示:

结果列表如下所示:

只有在使用 IQueryable.Select 时才会发生这种情况。使用 List.Select 时,结果看起来与预期的一样。

这意味着这样做可以解决问题。但这只是解决方法而不是解决方案。

businessLayer.GetAll().ToList().Select( b => new DTO().InitInhertedProperties(b)).ToList(); 

当您使用 IQueryable 时,您将绑定到表达式。 Entity Framework 将检查您放入 SelectOrderBy 和其他方法中的每个表达式,并尝试将其转换为 SQL。所以你不能在你的 lambda 中调用任意方法,只有 EF

知道

如果你想做一些没有SQL引擎直接支持的事情,你可以调用AsEnumerable:

businessLayer.GetAll().AsEnumerable().Select( ... 

(请注意,AsEnumerableToList 好,因为它保持懒惰)


可能(或可能不会,取决于查询提供程序版本)的另一个选项是手动构建表达式:

public static Expression<Func<TEntity, TDto>> InitInhertedProperties<TEntity, TDto>() where TDto : TEntity
{
    var entity = Expression.Parameter(typeof(BusinessObject), "b");
    var newDto = Expression.New(typeof(Dto).GetConstructors().First());
    var body = Expression.MemberInit(newDto,
        typeof(TDto).GetProperties()
            .Select(p => Expression.Bind(p, Expression.Property(entity, p.Name)))
      );

    return Expression.Lambda<Func<TEntity, TDto>>(body, entity);
}

用法:

var myExp = InitInhertedProperties<BusinessObject, Dto>();
var result = businessLayer.GetAll().Select(myExp).ToList();