2009-09-23 18 views
19

Powiedz, że mam DataTable z czterema kolumnami, Company (string), Fund (string)), Stan (string), Value (podwójne):System.LINQ.Dynamic: Wybierz ("nowy (...)") do listy <T> (lub innej kolekcji zliczającej <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)); 

chcę użyć System.LINQ.Dynamic zbudować dynamiczny zapytanie, które grupy po obu Spółki, Fundusz, lub państwa, a następnie wybiera przez moją grupę kryteria jako pierwsza kolumna i suma (wartość):

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

W powyższym zapytaniu wybrana grupaby wartość (grupa) w chory zawsze będzie ciągiem znaków, a suma będzie zawsze podwójna, więc chcę móc wrzucić coś w rodzaju Listy, gdzie Wynik jest obiektem z właściwościami Grupa (łańcuch) i TotalValue (podwójne).

Mam z tym wiele problemów, czy ktoś może rzucić trochę światła?

Odpowiedz

40

Najpierw uzyskać dostęp do aktualnej wartości jako Key pogrupowany w klauzuli SELECT:

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

To powinno uczynić swoją pracę zapytania. Najtrudniejsze pytanie brzmi: jak odwrócić zwrócone obiekty, które będą miały typ generowany dynamicznie, który dziedziczy z DynamicClass, do typu statycznego.

Opcja 1: Użyj odbicia, aby uzyskać dostęp do właściwości obiektu dynamicznego: Group i TotalValue.

Opcja 2: Używaj skompilowanych drzewek wyrażeń do generowania lekkich kodów, aby uzyskać dostęp do właściwości Group i TotalValue.

Opcja 3: Zmodyfikuj bibliotekę dynamiczną, aby obsługiwała mocno napisany wynik. Ten okazuje się być dość prosta:

  1. W ExpressionParser.Parse(), przechwytywanie argument typu w prywatnym polu:

    private Type newResultType; 
    public Expression Parse(Type resultType) 
    { 
        newResultType = resultType; 
        int exprPos = token.pos; 
        // ... 
    
  2. Pod koniec ExpressionParser.ParseNew(), postaramy się wykorzystać newResultType przed zalegających do typu dynamicznego:

    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. Wreszcie, musimy silnie typami wersję 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))); 
    } 
    

    Jedyne zmiany w stosunku do pierwotnego Select() są miejsca my odwołujące TResult.

Teraz musimy tylko nazwany typ powrotu:

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

I zaktualizowaną kwerenda będzie wyglądać następująco:

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

Inną możliwością jest rozszerzenie języka zapytań o DLinq z możliwość określenia nazwy typu w klauzuli new w ciągu zapytania.

Opisałem zmiany potrzebne w Dynamic.cs w innym stackoverflow answer.

Pozwoli to stwarzać zapytań jak następujących:

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

I lanego powrócił danych do listy <IExampleInterface> przez następujący sposób:

1 Extend Wybierz metodę dynamicznego LINQ do wsparcia generic typy. Zobacz pierwsza odpowiedź here

2 Zastosowanie metody Select (bardzo podobny do pierwszej linii odpowiedzi dahlbyk'S)

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

Jeśli potrzebujesz wiele kolumn, można użyć następujących:

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

3 Konwersja dane do listy.

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

4 Zastosowanie Impromptu przekonwertować List do listy <IExampleInterface>

var result = new List<IExampleInterface>(); 

foreach (var d in data) 
{ 
    dynamic r = Impromptu.ActLike<IExampleInterface>(d); 
    result.Add(r); 
} 
Powiązane problemy