首页 > 解决方案 > 为什么 IQueryable.Select 每次迭代都使用相同的引用

问题描述

我有一个IQueryable我想在其上执行一个Select. 在该选择中,我创建一个对象的新实例并运行一个函数,该函数将对象的值从 IQueryable(b) 复制到新创建的对象(新 DTO),然后返回此实例。

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(); 

标签: c#entity-frameworklinq.net-coreiqueryable

解决方案


当您与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();

推荐阅读