web-dev-qa-db-ja.com

文字列を連結するLINQアグリゲートを備えたEntityFramework?

これはTSQLで実行するのは簡単ですが、EF4で機能させるために、ここに座って机に頭をぶつけています。

私はテーブルを持っています、それをTestDataと呼びましょう。たとえば、DataTypeID、Name、DataValueなどのフィールドがあります。

DataTypeID, Name, DataValue
1,"Data 1","Value1"
1,"Data 1","Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"

DataID/Nameでグループ化し、DataValueをCSV文字列に連結したいと思います。目的の結果には-が含まれている必要があります

DataTypeID, Name, DataValues
1,"Data 1","Value1,Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"

さて、これが私がそれをやろうとしている方法です-

var query = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = (string)g.Aggregate("", (a, b) => (a != "" ? "," : "") + b.DataValue),
 }).ToList()

問題は、LINQ toEntitiesがこれをSQLに変換する方法を知らないことです。これは、3つのLINQクエリの和集合の一部であり、そのままにしておくことを強く望んでいます。データを取得して、後で集計を実行できると思います。パフォーマンス上の理由から、それは私のアプリでは機能しません。 SQLサーバー関数の使用も検討しました。しかし、それはEF4の世界では「正しい」とは思えません。

誰かがこれをクラックする気がありますか?

14
Phil Figgins

答えてくれたmoi_memeに感謝します。私がやりたかったことは、LINQ toEntitiesでは不可能です。他の人が示唆しているように、文字列操作メソッドにアクセスするには、LINQ toObjectsを使用する必要があります。

詳細については、moi_memeによって投稿されたリンクを参照してください。

2018年8月27日更新-リンクを更新(再度)- https://web.archive.org/web/20141106094131/http ://www.mythos-rini.com/blog/archives/451

そして、私は8年前からリンクのみの回答に失敗しているので、アーカイブされたコピーがいつか消えてしまった場合に備えて明確にします。その基本的な要点は、EFクエリでstring.joinにアクセスできないことです。 dbに対してクエリを実行するには、LINQクエリを作成してからToList()を呼び出す必要があります。次に、メモリにデータ(別名LINQ to Objects)があるので、string.joinにアクセスできます。

上記の参照リンクから提案されたコードは次のとおりです-

var result1 = (from a in users
                b in roles
           where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
           select new 
           {
              UserName = a.UserName,
              RoleNames = b.RoleName)                 
           });

var result2 = (from a in result1.ToList()
           group a by a.UserName into userGroup
           select new 
           {
             UserName = userGroup.FirstOrDefault().UserName,
             RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
           });

著者はさらに、パフォーマンスを向上させるためにstring.joinをaggregateに置き換えることを提案しています-

RoleNames = (userGroup.Select(x => x.RoleNames)).Aggregate((a,b) => (a + ", " + b))
3
Phil Figgins

ToList()が元のクエリの一部であり、この例に追加されただけではない場合は、結果のリストのオブジェクトへのLINQを使用して集計を行います。

var query = (from t in context.TestData
            group t by new { DataTypeID = t.DataTypeID, Name = t.Name } into g 
            select new { DataTypeID = g.Key.DataTypeID, Name = g.Key.Name, Data = g.AsEnumerable()})
            .ToList()
            .Select (q => new { DataTypeID = q.DataTypeID, Name = q.Name, DataValues = q.Data.Aggregate ("", (acc, t) => (acc == "" ? "" : acc + ",") + t.DataValue) });

LINQPadでテストすると、次の結果が得られます。

alt text

12
Jeff Ogata

一部の回答では、ToList()を呼び出してから、LINQ toOBJECTとして計算を実行することを提案しています。少量のデータの場合は問題ありませんが、メモリに早くロードしたくない大量のデータがある場合は、ToList()を選択できない可能性があります。

同様の要件がありました。私の問題は、エンティティの子アイテムのリストを取得し、その子アイテムの最初の文字を使用してコンマ区切りの値文字列を作成することでした。

  1. ビューモデルに、リポジトリからの生データを保持するプロパティを作成しました。

    public class MyViewModel
    {
        public string AnotherRegularProperty { get; set; }
    
        public IEnumerable<string> RawChildItems { get; set; }
    
        public string FormattedData
        {
            get
            {
                if (this.RawChildItems == null)
                    return string.Empty;
    
                string[] theItems = this.RawChildItems.ToArray();
    
                return theItems.Length > 0
                    ? string.Format("{0} ( {1} )", this.AnotherRegularProperty, String.Join(", ", theItems.Select(z => z.Substring(0, 1))))
                    : string.Empty;
            }
        }
    }
    

さて、そのようにして、私のViewModelは準備ができていました。その後、すべてのデータをメモリにロードする.ToList()を呼び出さなくても、LINQからエンティティ、このビューモデルにデータを簡単にロードしました。データベースに数千のレコードがある場合、.ToList()を呼び出すことはありません。

例:

IQueryable<MyEntity> myEntities = _myRepository.GetData();

IQueryable<MyViewModel> viewModels = myEntities.Select(x => new MyViewModel() { AnotherRegularProperty = x.AProperty, RawChildItems = x.MyChildren })

これで、必要なときにいつでもMyViewModelのFormattedDataプロパティを呼び出すことができ、プロパティが呼び出されたときにのみGetterが実行されます。

5
Emran Hussain

あなたはすでにとても近くにいます。これを試して:

var query = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = String.Join(",", g),
 }).ToList()

または、EFがString.Join(Linq-to-SQLで許可)を許可しない場合は、これを行うことができます。

var qs = (from t in context.TestData
  group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
  select new
 {
   DataTypeID = g.Key.DataTypeID,
   Name = g.Key.Name,
   DataValues = g
 }).ToArray();

var query = (from q in qs
            select new
            {
                q.DataTypeID,
                q.Name,
                DataValues = String.Join(",", q.DataValues),
            }).ToList();
0
Enigmativity

データベース上にこのビューを作成し(フィールドを連結します)、EFに元のテーブルの代わりにこのビューを使用させることをお勧めしますか?

LINQステートメントまたはマッピングの詳細では不可能であると確信しています。

0
Gerrie Schenck