2009-09-23 22 views
19

Giả sử tôi có một DataTable với bốn cột, Công ty (chuỗi), Quỹ (chuỗi), Nhà nước (string), giá trị gia tăng (double):System.LINQ.Dynamic: Chọn ("new (...)") vào Danh sách <T> (hoặc bất kỳ bộ sưu tập số đếm nào khác của <T>)

table1.Rows.Add("Company 1","Fund 1","NY",100)); 
    table1.Rows.Add("Company 2","Fund 1","CA",200)); 
    table1.Rows.Add("Company 3","Fund 1","FL",300)); 
    table1.Rows.Add("Company 4","Fund 2","CA",400)); 
    table1.Rows.Add("Company 5","Fund 1","NY",500)); 
    table1.Rows.Add("Company 6","Fund 2","CA",600)); 
    table1.Rows.Add("Company 7","Fund 3","FL",700)); 

tôi muốn sử dụng System.LINQ.Dynamic để xây dựng một truy vấn năng động mà nhóm ở hai công ty, Quỹ, hoặc Nhà nước, và sau đó chọn nhóm của tôi bằng cách tiêu chí làm cột đầu tiên và tổng (giá trị):

string groupbyvalue="Fund"; 
var q1= table1.AsEnumerable().AsQueryable() 
       .GroupBy(groupbyvalue,"it") 
       .Select("new ("+groupbyvalue+" as Group, Sum(Value) as TotalValue)"); 

Trong truy vấn trên, nhóm đã chọnbyvalue (Nhóm) bệnh luôn luôn là một chuỗi, và tổng số sẽ luôn luôn là một đôi, vì vậy tôi muốn để có thể đúc thành một cái gì đó giống như một danh sách, nơi kết quả là một đối tượng với thuộc tính Group (string) và TotalValue (double).

Tôi đang gặp nhiều rắc rối với điều này, bất kỳ ai cũng có thể làm sáng tỏ một chút?

Trả lời

40

Trước tiên, bạn sẽ truy cập vào giá trị nhóm hiện tại như Key trong Chọn khoản của bạn:

.Select("new (Key as Group, Sum(Value) as TotalValue)"); 

Điều đó sẽ làm cho công việc truy vấn của bạn. Câu hỏi khó hơn là làm thế nào để biến các đối tượng trả về, mà sẽ có một kiểu được tạo động kế thừa từ DynamicClass, thành một kiểu tĩnh.

Tùy chọn 1: Sử dụng phản chiếu để truy cập các thuộc tính GroupTotalValue của đối tượng động.

Tùy chọn 2: Sử dụng cây biểu thức đã biên dịch để tạo mã nhẹ để truy cập các thuộc tính GroupTotalValue.

Tùy chọn 3: Sửa đổi thư viện động để hỗ trợ kết quả được nhập mạnh mẽ. Đây hóa ra là khá đơn giản:

  1. Trong ExpressionParser.Parse(), nắm bắt những đối số kiểu trong một lĩnh vực riêng:

    private Type newResultType; 
    public Expression Parse(Type resultType) 
    { 
        newResultType = resultType; 
        int exprPos = token.pos; 
        // ... 
    
  2. Gần cuối ExpressionParser.ParseNew(), chúng tôi sẽ cố gắng sử dụng newResultType trước mặc định thành loại động:

    Expression ParseNew() 
    { 
        // ... 
        NextToken(); 
        Type type = newResultType ?? DynamicExpression.CreateClass(properties); 
        MemberBinding[] bindings = new MemberBinding[properties.Count]; 
        for (int i = 0; i < bindings.Length; i++) 
         bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); 
        return Expression.MemberInit(Expression.New(type), bindings); 
    } 
    
  3. Cuối cùng, chúng tôi cần phiên bản được đánh máy mạnh Select():

    public static IQueryable<TResult> Select<TResult>(this IQueryable source, string selector, params object[] values) 
    { 
        if (source == null) throw new ArgumentNullException("source"); 
        if (selector == null) throw new ArgumentNullException("selector"); 
        LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(TResult), selector, values); 
        return source.Provider.CreateQuery<TResult>(
         Expression.Call(
          typeof(Queryable), "Select", 
          new Type[] { source.ElementType, typeof(TResult) }, 
          source.Expression, Expression.Quote(lambda))); 
    } 
    

    Chỉ những thay đổi từ Select() ban đầu là những nơi chúng tôi tham chiếu TResult.

Bây giờ chúng ta chỉ cần một loại tên trở lại:

public class Result 
    { 
     public string Group { get; set; } 
     public double TotalValue { get; set; } 
    } 

Và truy vấn cập nhật của bạn sẽ trông như thế này:

IQueryable<Result> res = table1.AsQueryable() 
     .GroupBy(groupbyvalue, "it") 
     .Select<Result>("new (Key as Group, Sum(Value) as TotalValue)"); 
1

Một khả năng khác là mở rộng ngôn ngữ truy vấn của DLINQ với khả năng xác định tên loại trong mệnh đề new trong chuỗi truy vấn.

Tôi đã mô tả các thay đổi cần trong Dynamic.cs trong một số khác stackoverflow answer.

Nó sẽ cho phép bạn đặt ra câu hỏi như sau: Dữ liệu

IQueryable<Result> res 
    = table1.AsQueryable() 
    .GroupBy(groupbyvalue, "it") 
    .Select("new Result(Key as Group, Sum(Value) as TotalValue)") 
    as IQueryable<Result>; 
3

tôi đúc trở lại Liệt kê <IExampleInterface> bằng cách sau:

1 Mở rộng Chọn phương pháp LINQ năng động để hỗ trợ chung loại. Xem câu trả lời đầu tiên here

2 Sử dụng phương thức Select (rất giống với dòng đầu tiên của câu trả lời dahlbyk của)

Select<dynamic>("new (Key as Group, Sum(Value) as TotalValue)") 

Nếu bạn cần nhiều cột, bạn có thể sử dụng như sau:

GroupBy(@"new (Fund, Column1, Column2)", "it"). 
Select<dynamic>("new (Key.Fund, Key.Column1, Key.Column2, Sum(Value) as TotalValue)") 

3 Chuyển đổi dữ liệu vào danh sách.

var data = ... 
    GroupBy("...").Select<dynamic>("..."). 
    Cast<dynamic>().AsEnumerable().ToList(); 

4 Sử dụng Impromptu để chuyển đổi Danh sách vào danh sách <IExampleInterface>

var result = new List<IExampleInterface>(); 

foreach (var d in data) 
{ 
    dynamic r = Impromptu.ActLike<IExampleInterface>(d); 
    result.Add(r); 
} 
Các vấn đề liên quan