2012-02-17 17 views
5

LINQ-to-SQL là PITA cho tôi. Chúng tôi đang sử dụng nó để giao tiếp với cơ sở dữ liệu, và sau đó gửi các thực thể thông qua WCF đến một ứng dụng Silverlight. Tất cả mọi thứ đã được làm việc tốt, cho đến khi nó đến thời gian để bắt đầu chỉnh sửa (CUD) các thực thể, và dữ liệu liên quan của họ.LINQ-to-SQL: Chuyển đổi Func <T, T, bool> thành Biểu thức <Func <T, T, bool>>

Tôi cuối cùng đã có thể đưa ra hai vòng cho phép CUD. Tôi đã cố gắng tái cấu trúc chúng, và tôi đã rất gần gũi, cho đến khi tôi biết rằng tôi không thể luôn làm Lambda với L2S.

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

gọi theo:

ctx.CudOperation<MyEntity>(myVar.MyEntities, newHeader.MyEntities, 
    (x, y) => x.PkID == y.PkID && x.Fk1ID == y.Fk1ID && x.Fk2ID == y.FK2ID); 

này gần như làm việc. Tuy nhiên, Func của tôi cần phải là một biểu thức>, và đó là nơi tôi bị mắc kẹt.

Có ai có thể cho tôi biết nếu điều này là có thể không? Chúng ta phải trong .NET 3.5 do SharePoint 2010.

Trả lời

10

Chỉ cần thay đổi các tham số từ:

Func<T, T, bool> predicate 

Để:

Expression<Func<T, T, bool>> predicate 

Expression được tạo ra bởi trình biên dịch.

Bây giờ, vấn đề là làm thế nào để sử dụng điều này.

Trong trường hợp của bạn, bạn cần cả một Func một Expression, kể từ khi bạn đang sử dụng nó trong Enumerable truy vấn LINQ (Func dựa) cũng như các truy vấn SQL LINQ (biểu hiện dựa).

Trong:

.Where(o => predicate(old, o)) 

Tham số old là cố định. Vì vậy, chúng tôi có thể thay đổi thông số thành:

Func<T, Expression<Func<T, bool>>> predicate 

Điều này có nghĩa là chúng tôi có thể cung cấp một đối số (một 'cố định') và lấy lại biểu thức.

foreach (var old in oldCollection) 
{ 
    var condition = predicate(old); 
    // ... 
    { 
     ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition)); 
    } 
} 

Chúng tôi cũng cần sử dụng điều này trong Any. Để nhận được Func từ Biểu thức, chúng tôi có thể gọi Compile():

foreach (var old in oldCollection) 
{ 
    var condition = predicate(old); 
    if (!newCollection.Any(condition.Compile())) 
    { 
     ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition)); 
    } 
} 

Bạn có thể làm điều tương tự với phần tiếp theo.

Có hai vấn đề:

  1. Việc thực hiện có thể bị ảnh hưởng bằng cách sử dụng Compile() lô. Tôi không chắc chắn nó sẽ thực sự có hiệu quả như thế nào, nhưng tôi sẽ hồ sơ nó để kiểm tra.
  2. Cách sử dụng hiện có chút lạ, vì đây là một lambda được cuộn. Thay vì vượt qua (x,y) => ..., bạn sẽ được chuyển qua x => y => .... Tôi không chắc đây có phải là vấn đề lớn đối với bạn không.

Có thể có một cách tốt hơn để làm điều này :)

Dưới đây là một phương pháp thay thế, cần được nhanh hơn một chút, vì các biểu thức chỉ có được biên dịch một lần. Tạo một Ổ ghi rằng sẽ 'áp dụng' một đối số, như thế này:

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

Sau đó sử dụng nó như thế này:

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); 
     } 
    } 
} 
+0

Porges, tôi đánh giá cao sự trả lời nhanh chóng. Tuy nhiên, khi bạn làm điều đó, và thay đổi điều khoản của tôi .Where để ctx.GetTable () .Where (predicate), tôi không thể biên dịch. Tôi cần phải vượt qua hai biến (mà cần phải có tài sản của họ so sánh) với Expression/Func. – DaleyKD

+0

Ah, tôi hiểu, vấn đề thực tế của bạn nằm trong phương thức. Tôi đã bỏ lỡ điều đó - tôi sẽ cập nhật câu trả lời. – porges

+0

Hút thuốc lá! Điều đó hoàn toàn hiệu quả. Có lẽ chúng tôi sẽ xem lại điều này cho hiệu suất trong phiên bản 1.1. ;) Cảm ơn bạn! – DaleyKD