3

Linq式を使用してクエリを作成しようとしていますが、複数の列でグループ化しようとしてスタックしています。基本的なコレクションがあるとしましょう。

IEnumerable<Row> collection = new Row[]
{
    new Row() { Col1 = "a", Col2="x" },
    new Row() { Col1 = "a", Col2="x" },
    new Row() { Col1 = "a", Col2="y" },
};

ラムダ式を使用してこれらをグループ化できることを私は知っています:

foreach (var grp in collection.GroupBy(item => new { item.Col1, item.Col2 }))
{
    Debug.Write("Grouping by " + grp.Key.Col1 + " and " + grp.Key.Col2 + ": ");
    Debug.WriteLine(grp.Count() + " rows");
}

ご覧のとおり、これは正しくグループ化されます。

Grouping by a and x: 2 rows
Grouping by a and y: 1 rows

しかし、ここで、グループ化するセレクターのコレクションを受け取り、それがメソッドのパラメーターとして渡され、エンティティータイプがジェネリックであるとします。

void doLinq<T>(params Expression<Func<T,object>>[] selectors)
{
    // linq stuff
}

メソッドを呼び出す人は誰でも次のように呼び出します。

doLinq<Row>(entity=>entity.Col1, entity=>entity.Col2);

group-by式を作成するにはどうすればよいですか?

foreach (var grp in collection.GroupBy(
      item => new { 
          // selectors??
      }))
{
    // grp.Key. ??
}

編集

セレクターのセットが必要な理由を明確にするために、上記を更新しました。

編集#2

doLinqのエンティティタイプを汎用にしました。

4

4 に答える 4

1

Dynamic Linqを確認する必要があります:http://blogs.msdn.com/b/mitsu/archive/2008/02/07/linq-groupbymany-dynamically.aspx

于 2012-04-13T18:44:27.100 に答える
1

さて、linq-to-sqlなどを使用していると仮定するので、式ツリーが必要です。そうでない場合は、他の可能性があるかもしれません。

私が見ることができる可能な解決策:

  • 動的リンク

ウラジミール・ペレバロフの答えを参照してください。

  • groupby式ツリー全体を手動で構築する

http://msdn.microsoft.com/en-us/library/bb882637.aspxを参照して ください

  • 醜い回避策

まあ、それは私の部門です:)

テストされていないコード:

 void doLinq(params string[] selectors) // checking two expressions for equality is messy, so I used strings
     foreach (var grp in collection.GroupBy(
          item => new { 
              Col1 = (selectors.Contains("Col1") ? item.Col1 : String.Empty),
              Col2 = (selectors.Contains("Col2") ? item.Col2 : String.Empty)
              // need to add a line for each column :(
          }))
     {
          string[] grouping = (new string[]{grp.Key.Col1, grp.Key.Col2 /*, ...*/ }).Where(s=>!s.IsNullOrEmpty()).ToArray();
          Debug.Write("Grouping by " + String.Join(" and ", grouping)+ ": ");
          Debug.WriteLine(grp.Count() + " rows");
     }
 }
于 2012-04-15T09:17:07.927 に答える
1

linq-to-sqlの知識は非常に限られていますが、GroupByの内部に何があるかは本当に重要ですか?そうでない場合は、独自のkeySelectorを展開できるためです。とにかく、私はSqlServerCEとSqlServerExpressの両方で試してみましたが、これは機能しているようです。

using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq.Expressions;

namespace ConsoleApplication1 {
    class Props {
        public List<object> list = new List<object>();
        public override bool Equals(object obj) {
            return Enumerable.SequenceEqual(list, (obj as Props).list);
        }
        public override int GetHashCode() {
            return list.Select(o => o.GetHashCode()).Aggregate((i1, i2) => i1 ^ i2);
        }
    }
    class Program {
        static void Main(string[] args) {
            Lol db = new Lol(@"Data Source=.\SQLExpress;Initial Catalog=Lol;Integrated Security=true");
            db.Log = Console.Out;
            doLinq(db.Test, row => row.Col1, row => row.Col2);
            Console.ReadLine();
        }
        static void doLinq<T>(Table<T> table, params Func<T, object>[] selectors) where T : class {
            Func<T, Props> selector = item => {
                var props = new Props();
                foreach (var sel in selectors) props.list.Add(sel(item));
                return props;
            };
            foreach (var grp in table.GroupBy(selector)) {
                Console.Write("Grouping by " + string.Join(", ", grp.Key.list) + ": ");
                Console.WriteLine(grp.Count() + " rows");
            }
        }
    }
}

Lolデータベースには、3行のテーブル「Test」が1つあります。出力は次のとおりです。

SELECT [t0].[Col1], [t0].[Col2]
FROM [dbo].[Test] AS [t0]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

Grouping by a, x: 2 rows
Grouping by a, y: 1 rows

クエリを確認しましたが、linq-to-sqlは、groupByのSQLを生成できない場合は生成しないほど賢いようです。そのため、テーブルのすべての行を反復処理して、クライアントでグループ化します。

編集:完了のためのマイナーな追加。接続文字列はSQLServerExpressを想定しています。

于 2012-04-15T21:21:36.067 に答える
0

解決策は私のために働いた。これには2つの部分が含まれます。

  • 行の値とセレクターのセットを指定して、グループ化オブジェクト(object []としてエレガントに実装しました)を作成します。これには、行アイテムの各セレクターをコンパイルして呼び出すラムダ式が含まれます。
  • グループ化オブジェクトタイプのIEqualityを実装します(私の場合はIEqualityComparerです)。

最初の部分

foreach (System.Linq.IGrouping<object[], T> g in collection.GroupBy(
    new Func<T, object[]>(
        item => selectors.Select(sel => sel.Compile().Invoke(item)).ToArray()
    ),
    new ColumnComparer()
)
{ ... }

第二部

public class ColumnComparer : IEqualityComparer<object[]>
{
    public bool Equals(object[] x, object[] y)
    {
        return Enumerable.SequenceEqual(x, y);
    }

    public int GetHashCode(object[] obj)
    {
        return (string.Join("", obj.ToArray())).GetHashCode();
    }
}

これは、基本的なLinqで機能し、MySqlコネクタではLinqで機能します。他のどのLinqプロバイダー、およびこれが機能する式の種類は、まったく別の質問です...

于 2012-04-21T20:33:33.607 に答える