LINQ-to-SQL對我來說是一個PITA。我們使用它與數據庫進行通信,然後通過WCF將實體發送到Silverlight應用程序。一切都運行正常,直到開始編輯(CUD)實體及其相關數據。
我終於能夠設計兩個允許CUD的循環。我試著重構它們,而且我非常接近,直到我得知我不能總是用L2S做Lambda。
public static void CudOperation<T>(this DataContext ctx, IEnumerable<T> oldCollection, IEnumerable<T> newCollection, Func<T, T, bool> predicate)
where T : class
{
foreach (var old in oldCollection)
{
if (!newCollection.Any(o => predicate(old, o)))
{
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(o => predicate(old, o)));
}
}
foreach (var newItem in newCollection)
{
var existingItem = oldCollection.SingleOrDefault(o => predicate(o, newItem));
if (existingItem != null)
{
ctx.GetTable<T>().Attach(newItem, existingItem);
}
else
{
ctx.GetTable<T>().InsertOnSubmit(newItem);
}
}
}
被稱為:
ctx.CudOperation<MyEntity>(myVar.MyEntities, newHeader.MyEntities,
(x, y) => x.PkID == y.PkID && x.Fk1ID == y.Fk1ID && x.Fk2ID == y.FK2ID);
這幾乎奏效了。但是,我的Func需要是一個Expression>,這就是我被困住的地方。
有沒有人可以告訴我這是否可能?由於SharePoint 2010,我們必須使用.NET 3.5。
只需更改參數:
Func<T, T, bool> predicate
至:
Expression<Func<T, T, bool>> predicate
表達式由編譯器生成。
現在,問題是如何使用它。
在您的情況下,您需要一個Func
和一個Expression
,因為您在Enumerable
LINQ查詢(基於func)以及SQL LINQ查詢(基於表達式)中使用它。
在:
.Where(o => predicate(old, o))
old
參數是固定的。所以我們可以將參數更改為:
Func<T, Expression<Func<T, bool>>> predicate
這意味著我們可以提供一個參數('固定'一個)並返回一個表達式。
foreach (var old in oldCollection)
{
var condition = predicate(old);
// ...
{
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
}
}
我們還需要在Any
使用它。要從表達式獲取Func,我們可以調用Compile()
:
foreach (var old in oldCollection)
{
var condition = predicate(old);
if (!newCollection.Any(condition.Compile()))
{
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
}
}
你可以用下一部分做同樣的事情。
有兩個問題:
Compile()
批次可能會影響性能。我不確定它實際會產生多大的影響,但我會對其進行分析以進行檢查。 (x,y) => ...
你將傳遞x => y => ...
我不確定這對你來說是不是很重要。 可能有更好的方法來做到這一點:)
這是另一種方法 ,它應該更快一些,因為Expression只需要編譯一次。創建一個“應用”一個參數的重寫器,如下所示:
class PartialApplier : ExpressionVisitor
{
private readonly ConstantExpression value;
private readonly ParameterExpression replace;
private PartialApplier(ParameterExpression replace, object value)
{
this.replace = replace;
this.value = Expression.Constant(value, value.GetType());
}
public override Expression Visit(Expression node)
{
var parameter = node as ParameterExpression;
if (parameter != null && parameter.Equals(replace))
{
return value;
}
else return base.Visit(node);
}
public static Expression<Func<T2,TResult>> PartialApply<T,T2,TResult>(Expression<Func<T,T2,TResult>> expression, T value)
{
var result = new PartialApplier(expression.Parameters.First(), value).Visit(expression.Body);
return (Expression<Func<T2,TResult>>)Expression.Lambda(result, expression.Parameters.Skip(1));
}
}
然後像這樣使用它:
public static void CudOperation<T>(this DataContext ctx,
IEnumerable<T> oldCollection,
IEnumerable<T> newCollection,
Expression<Func<T, T, bool>> predicate)
where T : class
{
var compiled = predicate.Compile();
foreach (var old in oldCollection)
{
if (!newCollection.Any(o => compiled(o, old)))
{
var applied = PartialApplier.PartialApply(predicate, old);
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(applied));
}
}
foreach (var newItem in newCollection)
{
var existingItem = oldCollection.SingleOrDefault(o => compiled(o, newItem));
if (existingItem != null)
{
ctx.GetTable<T>().Attach(newItem, existingItem);
}
else
{
ctx.GetTable<T>().InsertOnSubmit(newItem);
}
}
}