Convert anonymous type to new C# 7 tuple type

前端 未结 5 1963
情书的邮戳
情书的邮戳 2021-01-07 16:19

The new version of C# is there, with the useful new feature Tuple Types:

public IQueryable Query();

public (int id, string name) GetSomeIn         


        
相关标签:
5条回答
  • 2021-01-07 16:56

    Note for anyone on a lower version of .NET: If you are on a lower version of .NET than 4.7.2 or .NET Core, you should use Nuget Package Manager to install System.ValueTuple to your project.

    Then, here's an example of getting a tuple from a Linq to SQL query:

    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()
    

    That ran for me, however, after check-in, I got a build failure...read on.

    For even more fun and games, I unfortunately had an earlier version of C# on my build server for some reason. So I had to go back because it didn't recognize the new tuple format on the .select(o => (o.record1, o.record2)) line (specifically that it would be a tuple because of the parenthesis around o.record1 and o.record2). So, I had to go back and kind of finagle it a bit more:

    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;
    
    0 讨论(0)
  • 2021-01-07 16:58

    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();
    
    0 讨论(0)
  • 2021-01-07 17:01

    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);
    
    0 讨论(0)
  • 2021-01-07 17:09
        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();
        }
    

    This method you can named your tuple item in a linq select

    0 讨论(0)
  • 2021-01-07 17:10

    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;
    }
    
    0 讨论(0)
提交回复
热议问题