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子句中。
答案 0 :(得分:17)
简短的回答是否定的,在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中命名元组项