C# 来自LINQ Select的复杂IEnumerable结果

C# 来自LINQ Select的复杂IEnumerable结果,c#,linq,asp.net-core-mvc,C#,Linq,Asp.net Core Mvc,我试图理解LINQ select操作的结果类型。我很难理解如何利用结果 考虑以下几点: public class ReportContract { public int ContractId { get; set; } public string ContractName { get; set; } public string Title { get; set; } public string[] Details { get; set; } } public c

我试图理解LINQ select操作的结果类型。我很难理解如何利用结果

考虑以下几点:

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> 
是否从此结果中访问


请忽略本文的其余部分。聪明的编辑要求更多的细节,我认为这就足够了

我建议阅读这篇文章,解释什么是枚举器,又称收益返回方法物化——因为它是Linq如何工作以及如何使用IEnumerable结果的核心。请注意,IEnumerable既可以指物化集合,如T[]或列表,也可以指非物化枚举器:

至于你的情况,一般来说,切勿从ASP.NET Web API控制器返回非物化实体框架,即数据库支持的IEnumerable或IQueryable,或将非物化IEnumerable作为ViewModel的成员传递。这是因为父DbContext的生存期可能短于IEnumerable/IQueryable对象的生存期。但是,仅使用内存中的对象返回非物化集合是可以的,例如使用枚举数覆盖枚举值以获取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更改为使用IReadOnlyList而不是IEnumerable,这将有助于确保始终返回物化列表,而不是可能的非物化枚举数:

public class SomeController : ControllerBase
{
    public ActionResult<IReadOnlyList<ReportContractVM>> Get()
    {
    }
}
顺便说一句,我在ActionResult与泛型和异步任务操作的配合方面一直存在问题,因此我个人更愿意这样做,但YMMV:

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

    // if async:

    [Produces( typeof( IReadOnlyList<ReportContractVM> ) )
    public async Task<IActionResult> Get()
    {
    }
}

我建议阅读这篇文章,解释什么是枚举器(也称为yield-return-methods-materialization)——因为它是Linq如何工作以及如何使用IEnumerable结果的核心。请注意,IEnumerable既可以指物化集合,如T[]或List,也可以指非物化枚举器:

至于你的情况,一般来说,切勿从ASP.NET Web API控制器返回非物化实体框架,即数据库支持的IEnumerable或IQueryable,或将非物化IEnumerable作为ViewModel的成员传递。这是因为父DbContext的生存期可能短于IEnumerable/IQueryable对象的生存期。但是,仅使用内存中的对象返回非物化集合是可以的,例如使用枚举数覆盖枚举值以获取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更改为使用IReadOnlyList而不是IEnumerable,这将有助于确保始终返回物化列表,而不是可能的非物化枚举数:

public class SomeController : ControllerBase
{
    public ActionResult<IReadOnlyList<ReportContractVM>> Get()
    {
    }
}
顺便说一句,我在ActionResult与泛型和异步任务操作的配合方面一直存在问题,因此我个人更愿意这样做,但YMMV:

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

    // if async:

    [Produces( typeof( IReadOnlyList<ReportContractVM> ) )
    public async Task<IActionResult> Get()
    {
    }
}

基本上,输出是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));
经检查,温度类型为:

System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>
因此,要分解这一点:

System.Linq.Enumerable.WhereSelectListIterator是从Select返回的迭代器类型。这与您的类型略有不同,可能是由于.NET版本不同

int是起始IEnumerable保持的类型

ExampleMespace.A是输出类型的全名


话虽如此,你应该阅读并理解戴相龙在回答中的内容

基本上,输出是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));
经检查,温度类型为:

System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>
因此,要分解这一点:

System.Linq.Enumerable.WhereSelectListIterator是从Select返回的迭代器类型。这与您的类型略有不同,可能是由于.NET版本不同

int是起始IEnumerable保持的类型

ExampleMespace.A是输出类型的全名


话虽如此,你应该阅读并理解戴相龙在回答中的内容

在您通过foreach或类似.ToList的方法迭代结果之前,LINQ IEnumerable查询的结果是它内部使用的某种神奇的LINQ类型。如您所见,由于它没有发出错误信号,编译器认为该类型实现了您所期望的类型。如果在结果上调用.ToList,您将得到正确的列表。在您通过foreach或类似.ToList的方式迭代结果之前,LINQ IEnumerable查询的结果是它内部使用的某种神奇的LINQ类型。正如您所看到的,由于它没有发出错误信号,编译器认为
类型实现您期望的类型。如果你打电话告诉我结果,你会得到正确的名单。谢谢你的回复。根据您的建议,我发现这篇相关文章也很有帮助:。谢谢您的回复。根据您的建议,我发现这篇相关文章也很有帮助:。