我有几个实现ISortable接口的类:
public interface ISortable
{
int Id { get; set; }
int? Idx { get; set; }
}
在我的DbContext类中,我有一个更新方法,应该为实现ISortable的实体做一些额外的事情:
public void UpdateSingle<T>(T item) where T : class
{
// If entity is Sortable, update the indexes of the records between the new and the old index of the updated entity
var sortable = item as ISortable;
if (sortable != null)
{
Detach(item); // need to detach the entity from the context in order to retrieve the old values from DB
var oldItem = Find<T>(sortable.Id) as ISortable;
if (oldItem != null && sortable.Idx != oldItem.Idx)
{
var entities = FindAll<T>().ToList().Cast<ISortable>();
var oldIdx = oldItem.Idx;
var newIdx = sortable.Idx;
if (newIdx > oldIdx)
{
var toUpdate = entities.Where(a => a.Idx <= newIdx && a.Idx > oldIdx).Select(a => a);
foreach (var toUpdateEntity in toUpdate)
{
toUpdateEntity.Idx = toUpdateEntity.Idx - 1;
}
}
else
{
var toUpdate = entities.Where(a => a.Idx >= newIdx && a.Idx < oldIdx).Select(a => a);
foreach (var toUpdateEntity in toUpdate)
{
toUpdateEntity.Idx = toUpdateEntity.Idx + 1;
}
}
}
Detach(oldItem);
Attach(item); // re-attach to enable saving
}
Entry(item).State = EntityState.Modified;
Commit();
}
我想知道的是这一行:
var entities = FindAll<T>().ToList().Cast<ISortable>();
我必须将LINQ to SQL表达式转换为列表才能将实体强制转换为ISortable。我需要将其转换为ISortable才能执行以下操作:
var toUpdate = entities.Where(a => a.Idx <= newIdx && a.Idx > oldIdx).Select(a => a);
接口公开了Idx属性。
问题是在FindAll()上调用ToList()会将整个表加载到内存中。
有没有办法在不首先加载整个表的情况下执行Where,而不会丢失通用实现?
这里的想法是我想对所有“可排序”的实体执行更新的一些常见操作。为了使这个工作,更新方法需要是通用的,以便处理各种类,但后来我需要接口来公开必要的字段......如果有更好的方法这样做(可能有),请让我知道。 : - )
答案 0 :(得分:0)
问题是在FindAll()上调用ToList()会将整个表加载到内存中。
使用AsEnumerable
代替ToList
;它只是将编译时类型更改为IEnumerable<T>
而不是IQueryable<T>
,因此后续操作在内存中执行而不是在数据库中执行,但一次只处理一个项目(从中获取项目)根据后续操作的需要逐个DB。
答案 1 :(得分:0)
再次尝试,这次使用表达式。我认为这应该有效:
public void UpdateSingle<T>(T item) where T : class
{
// If entity is Sortable, update the indexes of the records between the new and the old index of the updated entity
var sortable = item as ISortable;
if (sortable != null)
{
Detach(item); // need to detach the entity from the context in order to retrieve the old values from DB
var oldItem = Find<T>(sortable.Id);
if (oldItem != null && sortable.Idx != oldItem.Idx)
{
UpdateSingleSortable(oldItem, sortable);
}
Detach(oldItem);
Attach(item); // re-attach to enable saving
}
Entry(item).State = EntityState.Modified;
Commit();
}
public void UpdateSingleSortable<T>(T oldItem, ISortable sortable)
where T : class
{
var entities = FindAll<T>();
var oldIdx = oldItem.Idx;
var newIdx = sortable.Idx;
if (newIdx > oldIdx)
{
var expression = GenerateExpressionA(oldItem, newIdx, oldIdx);
var typedExpression = expression as Expression<Func<T, bool>>;
var toUpdate = entities.Where(typedExpression).Select(a => a);
foreach (var toUpdateEntity in toUpdate)
{
toUpdateEntity.Idx = toUpdateEntity.Idx - 1;
}
}
else
{
var expression = GenerateExpressionB(oldItem, newIdx, oldIdx);
var typedExpression = expression as Expression<Func<T, bool>>;
var toUpdate = entities.Where(typedExpression).Select(a => a);
foreach (var toUpdateEntity in toUpdate)
{
toUpdateEntity.Idx = toUpdateEntity.Idx + 1;
}
}
}
Expression GenerateExpressionB<T>(T t, int? newIdx, int? oldIdx)
{
// a => a.Idx >= newIdx && a.Idx < oldIdx
var underlyingType = t.GetType();
var idxGetter = underlyingType.GetProperty("Idx");
Type genericFunc = typeof(Func<,>);
Type[] typeArgs = { underlyingType, typeof(bool) };
Type returnType = genericFunc.MakeGenericType(typeArgs);
var param = Expression.Parameter(underlyingType);
var toReturn = Expression.Lambda(
returnType,
Expression.And
(
Expression.GreaterThanOrEqual(
Expression.MakeMemberAccess(param, idxGetter),
Expression.Constant(newIdx, typeof(int?))
),
Expression.LessThan(
Expression.MakeMemberAccess(param, idxGetter),
Expression.Constant(oldIdx, typeof(int?))
)
),
param);
return toReturn;
}
Expression GenerateExpressionA<T>(T t, int? newIdx, int? oldIdx)
{
// a => a.Idx <= newIdx && a.Idx > oldIdx
var underlyingType = t.GetType();
var idxGetter = underlyingType.GetProperty("Idx");
Type genericFunc = typeof(Func<,>);
Type[] typeArgs = { underlyingType, typeof(bool) };
Type returnType = genericFunc.MakeGenericType(typeArgs);
var param = Expression.Parameter(underlyingType);
var toReturn = Expression.Lambda(
returnType,
Expression.And
(
Expression.LessThanOrEqual(
Expression.MakeMemberAccess(param, idxGetter),
Expression.Constant(newIdx, typeof(int?))
),
Expression.GreaterThan(
Expression.MakeMemberAccess(param, idxGetter),
Expression.Constant(oldIdx, typeof(int?))
)
),
param);
toReturn.Dump();
return toReturn;
}
答案 2 :(得分:0)
只需将方法的签名更改为以下内容:
public void UpdateSingle<T>(T item)
where T : class, ISortable
然后,您不仅可以在数据库端执行查询(您不需要将集合拉入内存以使项目满足给定条件),您也不会在运行时进行检查;您将检查以确保T
在编译时实现ISortable
。