2012-07-26 22 views
5

Bối cảnh:Làm cách nào để đơn giản hóa việc mở rộng phương thức mở rộng LINQ bằng cách sử dụng sự phản chiếu?

Tôi có một dịch vụ web mà trả về các hàng trong một bảng (tên bảng được cung cấp như một tham số) với Id lớn hơn một Id nhất định (cũng được cung cấp như một tham số). Chúng tôi giả sử các Id là tuần tự.

Tôi đang sử dụng LINQ to SQL cho sự tương tác cơ sở dữ liệu vì vậy tôi muốn trả lại hàng mới như:

List<WhateverObject> 

Bởi vì chúng tôi chỉ biết tên bảng trong thời gian chạy, tôi không thể sử dụng LINQ một cách bình thường điều này khiến mọi việc trở nên phức tạp hơn nhiều.

Câu hỏi:

Mã này là dưới đây (và nó hoạt động). Làm thế nào tôi có thể đơn giản hóa nó? Có vẻ quá phức tạp.

private object GetUpdateList(string tableName, int Id, DataClassesDataContext db) 
{ 
    PropertyInfo pi = db.GetType().GetProperty(tableName); 

    var table = pi.GetValue(db, null); 

    // Get type of object within the table. 
    Type genericType = table.GetType().GetGenericArguments()[0]; 

    // The Where method lives on the Enumerable type in System.Linq 
    var whereMethods = typeof(System.Linq.Enumerable) 
     .GetMethods(BindingFlags.Static | BindingFlags.Public) 
     .Where(mi => mi.Name == "Where"); 

    // There are actually 2 where methods - we want the one with 2 parameters 
    MethodInfo whereMethod = null; 
    foreach (var methodInfo in whereMethods) 
    { 
     var paramType = methodInfo.GetParameters()[1].ParameterType; 
     if (paramType.GetGenericArguments().Count() == 2) 
     { 
      // we are looking for Func<TSource, bool>, the other has 3 
      whereMethod = methodInfo; 
      break; 
     } 
    } 

    Func<object, bool> IdEquals = BuildEqFuncFor("Id", Id); 

    whereMethod = whereMethod.MakeGenericMethod(genericType); 
    var result = whereMethod.Invoke(table, new object[] { table, IdEquals }); 

    MethodInfo toListMethod = typeof(System.Linq.Enumerable).GetMethod("ToList").MakeGenericMethod(genericType); 
    return toListMethod.Invoke(result, new object[] { result }); 
} 

// Build lambda expression for use in Linq 
private static Func<object, bool> BuildEqFuncFor(string prop, object val) 
{ 
    // We know we are comparing integers here so cast them. 
    // There is probably a more general solution. 
    return t => (int)t.GetType().InvokeMember(prop, BindingFlags.GetProperty, null, t, null) > (int)val; 
} 

Để đưa ra giải pháp này tôi đã phải tham khảo các câu hỏi sau:

+0

Không bỏ qua câu hỏi của bạn, nhưng bạn đang ở trong tình huống không thể tái cấu trúc? Nó có vẻ như rất nhiều công việc cho một cái gì đó rất đơn giản. Nó có cần phải chung chung không? –

+0

Bạn sẽ đề xuất điều gì? –

+0

Chỉ cần đánh máy mạnh mẽ? Vượt qua tên bảng và id. Chọn nó từ cơ sở dữ liệu và trả về các hàng được yêu cầu. Trả về một 'IEnumerable ' và gọi nó là một ngày. –

Trả lời

4

Hãy thử một cái gì đó như thế này:

private IList GetUpdateList(string tableName, int id, DataClassesDataContext db) 
{ 
    System.Reflection.PropertyInfo pi = db.GetType().GetProperty(tableName); 

    var table = pi.GetValue(db, null); 

    // Get type of object within the table. 
    Type genericType = table.GetType().GetGenericArguments()[0]; 

    var param = Expression.Parameter(genericType, "x"); 
    var predicateExpr = Expression.Lambda(
     Expression.GreaterThan(
      Expression.Property(param, "Id"), 
      Expression.Constant(id)), 
     param); 

    return this 
     .GetType() 
     .GetMethod("GetUpdateListGeneric") 
     .MakeGenericMethod(genericType) 
     .Invoke(this, new[] { table, predicateExpr }) as IList; 
} 

private IList<T> GetUpdateListGeneric<T>(
    Table<T> table, 
    Expression<Func<T, bool>> predicate) where T : class 
{ 
    return table.Where(predicate).ToList(); 
} 
+0

Tuyệt vời! Tôi chỉ cần chỉnh sửa một vài thứ (kiểu trả về) và kéo Expression.Parameter vào một biến đầu tiên và nó hoạt động. –

+0

Rất vui khi được trợ giúp và cảm ơn các bản sửa lỗi. – Jacob

Các vấn đề liên quan