Сложный IEnumerable Результат из LINQ Select - PullRequest
0 голосов
/ 06 октября 2019

Я пытаюсь понять результирующий тип из действия выбора LINQ. У меня проблемы с пониманием того, как использовать результат.

Примите во внимание следующее:

public class ReportContract
{
    public int ContractId { get; set; }
    public string ContractName { get; set; }
    public string Title { get; set; }
    public string[] Details { get; set; }
}

public class ReportContractVM
{
    public int ContactId { get; set; }
    public string ContractName { get; set; }
}

public class SomeController : ControllerBase
{
    public ActionResult<IEnumerable<ReportContractVM>> Get()
    {
        IEnumerable<ReportContract> contracts = CreateConrtacts();

        IEnumerable<ReportContractVM> result = contracts.Select(
            x => new ReportContractVM
                     {
                       ContactId = x.ContractId,
                       ContractName = x.ContractName
                     }
            );

        return Ok(result);
    }

    private IEnumerable<ReportContract> CreateContracts()
    {
        List<ReportContract> contracts = new List<ReportContract>()
        {
            new ReportContract { 
               ContractId = 1234, 
               ContractName= "ABCD", 
               Details= new string[] { 
                 "test", "Best", "rest", "jest"}, 
               Title="First" 
            },
            new ReportContract { 
               ContractId = 1235, 
               ContractName= "EFGH", 
               Details= new string[] { 
                 "fish", "dish", "wish", "pish"}, 
               Title="Second" 
            },
            new ReportContract { 
               ContractId = 1236, 
               ContractName= "IJKL", 
               Details= new string[] { 
                 "hot", "tot", "mot", "jot"}, 
               Title="Third" 
            },
            new ReportContract { 
               ContractId = 1237, 
               ContractName= "MNOP", 
               Details= new string[] { 
                 "ring", "sing", "bing", "ping"}, 
               Title="Fourth" 
            }
        };

        return contracts;
    }
}

Проверка типа, присвоенного параметру 'result' в отладчике, дает:

System.Linq.Enumberable.SelectListIterator
< 
  ComplexIEnumerable.ReportContract, 
  ComplexIEnumerable.ReportContractVM 
>

Я не уверен, как это относится к

IEnumerable<ReportContractVM>.

Как к

IEnumerable<ReportContractVM> 

получен доступ из этого результата?

Пожалуйста, игнорируйте остальную часть этого текста. «Интеллектуальный» редактор требует больше деталей, и я думаю, что этого достаточно.

Ответы [ 2 ]

2 голосов
/ 06 октября 2019

Я рекомендую прочитать эту статью, в которой объясняется, что такое материализация Enumerator (он же yield return методы) - поскольку она является основой для работы Linq и как использовать результаты IEnumerable<T> (обратите внимание, что IEnumerable<T> может относиться к обоим материализованнымколлекции, такие как T[] или List<T>, а также нематериализованные счетчики):

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/intermediate-materialization

В общем случае никогда не возвращает нематериализованных Entity Framework (т. Е. С поддержкой базы данных) IEnumerable<T> или IQueryable<T> из контроллера ASP.NET Web API или передача нематериализованного IEnumerable<T> в качестве члена ViewModel (это потому, что время жизни родительского элемента DbContext может быть короче, чем время жизни объекта IEnumerable<T> / IQueryable<T>). Однако возвращать нематериализованную коллекцию, используя только объекты в памяти, это нормально (например, перечислитель со значениями enum для получения SelectListItem объектов):

В частности, измените это:

IEnumerable<ReportContractVM> result = contracts.Select(
            x => new ReportContractVM
                     {
                       ContactId = x.ContractId,
                       ContractName = x.ContractName
                     }
            );

на это:

List<ReportContractVM> list = contracts
    .Select( x => new ReportContractVM { ContractId = x.ContractId, ContractName = x.ContractName  } )
    .ToList();

Кроме того, измените ActionResult<T> на IReadOnlyList<T> вместо IEnumerable<T>, что поможет вам всегда возвращать материализованный список вместо, возможно, нематериализованного перечислителя. :

public class SomeController : ControllerBase
{
    public ActionResult<IReadOnlyList<ReportContractVM>> Get()
    {
    }
}

Кстати, у меня были проблемы с ActionResult<T>, плохо работающим с генериками и async Task<ActionResult<T>> действиями, поэтому я лично предпочитаю это делать, но YMMV:

public class SomeController : ControllerBase
{
    [Produces( typeof( IReadOnlyList<ReportContractVM> ) )
    public IActionResult Get()
    {
    }

    // if async:

    [Produces( typeof( IReadOnlyList<ReportContractVM> ) )
    public async Task<IActionResult> Get()
    {
    }
}
0 голосов
/ 06 октября 2019

По сути, вывод - это конкретный перечислимый тип, возвращаемый из Select с параметрами типа, ссылающимися как на вход, так и на вывод. Это может быть легче увидеть в упрощенном примере ниже.

namespace ExampleNamespace
{
    class A
    {
        public A(int i) { }
    }
}

Теперь скажите, что у меня где-то есть этот код:

List<int> i = new List<int>() { 1, 2, 3, 4, 5, 6 };
var temp = i.Select(x => new A(x));

При проверке тип temp:

System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>

Итак, чтобы разбить это на части:

  • System.Linq.Enumerable.WhereSelectListIterator - это тип итератора, возвращаемый из Select. Это может немного отличаться от вашего типа из-за разных версий .NET.

  • int - это тип, который ваш начальный IEnumerable содержал

  • ExampleNamespace.A - полное имя типа вывода.

При всем сказанном, вы должны прочитать и понять, что Дай опубликовал в их ответе,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...