Convert anonymous type to new C# 7 tuple type
The short answer is no, in the current form of C#7 there is no in-framework way to accomplish your goals verbatim, since you want to accomplish:
- Linq-to-entities
- Mapping to a subset of columns
- Avoiding property by property mapping from a custom or anonymous type to a C#7 tuple by mapping directly to a C#7 tuple.
Because Query<SomeType>
exposes an IQueryable
, any sort of projection must be made to an expression tree .Select(x => new {})
.
There is an open roslyn issue for adding this support, but it doesn't exist yet.
As a result, until this support is added, you can either manually map from an anonymous type to a tuple, or return the entire record and map the result to a tuple directly to avoid two mappings, but this is obviously inefficient.
While this restriction is currently baked into Linq-to-Entities due to a lack of support and the inability to use parametered constructors in a .Select()
projection, both Linq-to-NHibernate and Linq-to-Sql allow for a hack in the form of creating a new System.Tuple
in the .Select()
projection, and then returning a ValueTuple with the .ToValueTuple() extension method:
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();
}
Since System.Tuple can be mapped to an expression, you can return a subset of data from your table and allow the framework to handle mapping to your C#7 tuple. You can then deconstruct the arguments with any naming convention you choose:
(int id, string customName) info = GetSomeInfo();
Console.Write(info.customName);
Of course, by creating the tuple from your LINQ expression:
public (int id, string name) GetSomeInfo() {
var obj = Query<SomeType>()
.Select(o => (o.Id,o.Name))
.First();
return obj;
}
According to another answer regarding pre-C# 7 tuples, you can use AsEnumerable()
to prevent EF to mix things up. (I have not much experience with EF, but this should do:)
public (int id, string name) GetSomeInfo() {
var obj = Query<SomeType>()
.AsEnumerable()
.Select(o => (o.Id,o.Name))
.First();
return obj;
}
While tuple literals are not currently supported in expression trees, it doesn't mean the ValueTuple
type isn't. Just create it explicitly.
public (int id, string name) GetSomeInfo() =>
Query<SomeType>()
.Select(o => ValueTuple.Create(o.Id, o.Name))
.First();