web-dev-qa-db-ja.com

匿名型を新しいC#7タプル型に変換します

C#の新しいバージョンがあり、便利な新機能Tuple Typesがあります。

public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new {
            id = o.Id,
            name = o.Name,
        })
        .First();

    return (id: obj.id, name: obj.name);
}

匿名型オブジェクトobjを、プロパティごとにプロパティをマッピングせずに返したいタプルに変換する方法はありますか(プロパティの名前が一致すると仮定)?

コンテキストはORMにあり、SomeTypeオブジェクトには他の多くのプロパティがあり、多くの列を持つテーブルにマップされます。 IDとNAMEだけを取得するクエリを実行したいので、匿名型をタプルに変換するか、ORM Linqプロバイダーがタプルを理解し、SQLのselect句にプロパティ関連の列を配置する方法を知っている必要があります。

29
lmcarreiro

簡単な答えはノーです。C#7の現在の形式では、目標を逐語的に達成するインフレームワークの方法はありません。

  • Linq-to-Entity
  • 列のサブセットへのマッピング
  • C#7タプルに直接マッピングすることにより、カスタムまたは匿名タイプからC#7タプルへのプロパティマッピングによるプロパティを回避します。

_Query<SomeType>_はIQueryableを公開するため、式ツリー.Select(x => new {})に対して何らかの投影を行う必要があります。

このサポートを追加するための roslynの問題を開く がありますが、まだ存在していません。

その結果、このサポートが追加されるまで、手動で匿名型からタプルにマッピングするか、レコード全体を返して結果をタプルに直接マッピングして2つのマッピングを回避できますが、これは明らかに非効率的です。


現在、この制限は、サポートの欠如と.Select()プロジェクションでパラメーター化されたコンストラクターを使用できないためにLinq-to-Entitiesに組み込まれていますが、Linq-to-NHibernateとLinq-to-Sqlの両方で可能です.Select()プロジェクションで新しい_System.Tuple_を作成し、 。ToValueTuple() 拡張メソッドでValueTupleを返すという形式のハック:

_public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new System.Tuple<int, string>(o.Id, o.Name))
        .First();

    return obj.ToValueTuple();
}
_

System.Tupleは式にマッピングできるため、テーブルからデータのサブセットを返し、フレームワークがC#7 Tupleへのマッピングを処理できるようにすることができます。その後、選択した命名規則で引数を分解できます。

_(int id, string customName) info = GetSomeInfo();
Console.Write(info.customName);
_
18
David L

もちろん、LINQ式からTupleを作成することにより:

_public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}
_

別の答えに C#7より前のタプルに関しては、AsEnumerable()を使用してEFが物事を混同しないようにすることができます。 (私はEFの経験があまりありませんが、これでうまくいくはずです:)

_public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .AsEnumerable()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}
_
17
Patrick Hofman

現在、式ツリーではタプルリテラルはサポートされていませんが、ValueTuple型がサポートされていないという意味ではありません。明示的に作成するだけです。

public (int id, string name) GetSomeInfo() =>
    Query<SomeType>()
        .Select(o => ValueTuple.Create(o.Id, o.Name))
        .First();
5
Jeff Mercado

下位バージョンの.NETを使用しているユーザーへの注意:4.7.2または.NET Coreよりも下位のバージョンの.NETを使用している場合、Nuget Package Managerを使用してSystem.ValueTupleをプロジェクトにインストールする必要があります。

次に、Linq to SQLクエリからタプルを取得する例を示します。

var myListOfTuples = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).AsEnumerable()
                     .select(o => (o.record1, o.record2))
                     .ToList()

それは私のために走りました、しかし、チェックイン後に、私はビルド失敗を得ました...続きを読みます。

さらに面白くてゲームをするために、残念ながらビルドサーバーに以前のバージョンのC#が何らかの理由でありました。ですから、.select(o =>(o.record1、o.record2))行の新しいタプル形式を認識しなかったため、戻って行かなければなりませんでした(具体的には、oの周りの括弧のためにタプルになるということです)。 record1およびo.record2)。だから、私は戻ってもう少し精査しなければなりませんでした:

var myListOfAnonymousObjects = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).ToList()

var myTuples = new List<Tuple<Record1sClass, Record2sClass>>();
foreach (var pair in myListOfAnonymousObjects)
{
    myTuples.Add(pair.record1, pair.record2);
}
return myTuples;
1
JakeJ
    public IList<(int DictionaryId, string CompanyId)> All(string DictionaryType)
    {
        var result = testEntities.dictionary.Where(p => p.Catalog == DictionaryType).ToList();
        var resultTuple = result.Select(p => (DictionaryId: p.ID, CompanyId: p.CompanyId));
        return resultTuple.ToList();
    }

この方法では、linq selectでTupleアイテムに名前を付けることができます

0
Jackdon Wang