2009-09-23 7 views
19

Angenommen, ich habe eine DataTable mit vier Spalten, Company (string), Fund (string), Staat (string), Wert (double):System.LINQ.Dynamic: Wählen Sie ("new (...)") in eine Liste <T> (oder eine andere aufzählbare Sammlung von <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)); 

ich möchte System.LINQ.Dynamic verwenden, um eine dynamische Abfrage, die Gruppen auf beiden Unternehmen, Fonds oder Staat, zu bauen und dann meine Gruppe auswählt, indem Kriterien als erste Spalte und sUM (Wert):

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

In der obigen Abfrage, die ausgewählte groupbyvalue (Group) w Es ist immer ein String, und die Summe wird immer ein Double sein, also möchte ich in der Lage sein, in etwas wie eine Liste zu wirken, wobei Ergebnis ein Objekt mit den Eigenschaften Group (string) und TotalValue (double) ist.

Ich habe eine Menge Probleme damit, kann jemand etwas Licht vergießen?

Antwort

40

Zunächst werden Sie den aktuellen Wert gruppiert als Key in Ihrer Select-Klausel zuzugreifen:

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

, dass Ihre Abfrage Arbeit machen sollte. Die schwierigere Frage ist, wie die zurückgegebenen Objekte, die einen dynamisch generierten Typ haben, der von DynamicClass erbt, in einen statischen Typ umgewandelt werden.

Option 1: Verwenden Sie reflection, um auf die Eigenschaften des dynamischen Objekts Group und zuzugreifen.

Option 2: Verwenden Sie kompilierte Ausdrucksbäume für die Generierung von Lightweight-Code, um auf die Eigenschaften Group und TotalValue zuzugreifen.

Option 3: Ändern Sie die dynamische Bibliothek, um ein stark typisiertes Ergebnis zu unterstützen. Dies erweist sich als ziemlich einfach zu sein:

  1. In ExpressionParser.Parse(), die Art Argument in einem privaten Feld erfassen:

    private Type newResultType; 
    public Expression Parse(Type resultType) 
    { 
        newResultType = resultType; 
        int exprPos = token.pos; 
        // ... 
    
  2. Nahe dem Ende ExpressionParser.ParseNew(), werden wir versuchen, newResultType zu verwenden, bevor säumige zu einem dynamischen Typ:

    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. Schließlich müssen wir eine stark typisierte Version von 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))); 
    } 
    

    Die einzigen Änderungen von der ursprünglichen Select() sind Orte, die wir TResult verweisen.

Jetzt brauchen wir nur einen benannten Typ zurückzukehren:

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

Ihre aktualisierte Abfrage wird wie folgt aussehen:

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

Eine andere Möglichkeit ist Abfragesprache von DLINQ zu verlängern mit die Möglichkeit, den Typnamen in der new-Klausel in der Abfragezeichenfolge anzugeben.

Ich habe die erforderlichen Änderungen in Dynamic.cs in einem anderen stackoverflow answer beschrieben.

Es ermöglichen Ihnen, Abfragen wie die folgenden stellen:

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

I-Daten Casted zurück <IExampleInterface> durch die folgende Weise zur Liste:

1 Wählen Sie Methode des dynamischen Linq Extend Generika zu unterstützen Arten. Siehe erste Antwort here

2 Verwenden Sie die Select-Methode (sehr ähnlich erste Linie der dahlbyk Antwort)

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

Wenn Sie mehrere Spalten benötigen, können Sie folgendes:

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

3 Convert Daten zur Liste.

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

4 Verwenden Impromptu Liste konvertieren <IExampleInterface>

var result = new List<IExampleInterface>(); 

foreach (var d in data) 
{ 
    dynamic r = Impromptu.ActLike<IExampleInterface>(d); 
    result.Add(r); 
} 
zur Liste
Verwandte Themen