为什么 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 将检查您放入 Select
、OrderBy
和其他方法中的每个表达式,并尝试将其转换为 SQL。所以你不能在你的 lambda 中调用任意方法,只有 EF
知道
如果你想做一些没有SQL引擎直接支持的事情,你可以调用AsEnumerable
:
businessLayer.GetAll().AsEnumerable().Select( ...
(请注意,AsEnumerable
比 ToList
好,因为它保持懒惰)
可能(或可能不会,取决于查询提供程序版本)的另一个选项是手动构建表达式:
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();
我有一个 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 将检查您放入 Select
、OrderBy
和其他方法中的每个表达式,并尝试将其转换为 SQL。所以你不能在你的 lambda 中调用任意方法,只有 EF
如果你想做一些没有SQL引擎直接支持的事情,你可以调用AsEnumerable
:
businessLayer.GetAll().AsEnumerable().Select( ...
(请注意,AsEnumerable
比 ToList
好,因为它保持懒惰)
可能(或可能不会,取决于查询提供程序版本)的另一个选项是手动构建表达式:
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();