将匿名类型转换为新的C#7元组类型

时间:2017-04-13 19:16:07

标签: linq-to-entities tuples anonymous-types linq-to-nhibernate c#-7.0

C#的新版本就在那里,有了新的功能元组类型:

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 Provider知道如何理解元组并将属性相关列放在SQL select子句中。

5 个答案:

答案 0 :(得分:17)

简短的回答是否定的,在C#7的当前形式中,没有框架内的方式来逐字完成你的目标,因为你想要完成:

  • LINQ到实体
  • 映射到列的子集
  • 通过直接映射到C#7元组,通过属性映射从自定义或匿名类型到C#7元组来避免属性。

由于Query<SomeType>公开IQueryable,因此必须对表达式树.Select(x => new {})进行任何类型的投影。

有一个open roslyn issue用于添加此支持,但它尚不存在。

因此,在添加此支持之前,您可以手动从匿名类型映射到元组,或者返回整个记录并将结果直接映射到元组以避免两个映射,但这显然效率低下。

虽然由于缺乏支持以及无法在.Select()投影中使用参数化构造函数,此限制目前已纳入Linq-to-Entities,但Linq-to-NHibernate和Linq-to-Sql都允许对于在System.Tuple投影中创建新.Select()形式的黑客攻击,然后使用.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元组。然后,您可以使用您选择的任何命名约定来解构参数:

(int id, string customName) info = GetSomeInfo();
Console.Write(info.customName);

答案 1 :(得分:15)

当然,通过从LINQ表达式创建元组:

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

    return obj;
}

根据to another answer关于前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;
}

答案 2 :(得分:4)

尽管表达式树当前不支持元组文字,但这并不意味着不支持ValueTuple类型。只需显式创建它即可。

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

答案 3 :(得分:1)

.NET较低版本上的任何人的注意事项: 如果您使用的是低于4.7.2或.NET Core的.NET版本,则应使用Nuget软件包管理器将System.ValueTuple安装到您的项目中。

然后,下面是从Linq到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;

答案 4 :(得分:0)

    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中命名元组项