【问题标题】:WebApi cannot return DTO that contains a list of another DTOWebApi 无法返回包含另一个 DTO 列表的 DTO
【发布时间】:2016-08-04 08:04:26
【问题描述】:

我有一个要返回的发票 DTO,其中包含发票项目列表,其中 InvoiceLines 是联结表。

我的 WebApi 控制器代码:

var screenset =
    from inv in context.Invoices where inv.InvoiceId == invoiceID
    join line in context.InvoiceLines on inv.InvoiceId equals line.InvoiceId
    join track in context.Tracks on line.TrackId equals track.TrackId into T
    select new InvoiceDTO
    {
        InvoiceId = inv.InvoiceId,
        InvoiceDate = inv.InvoiceDate,
        CustomerId = inv.CustomerId,
        CustomerFullName = inv.Customer.LastName + ", " + inv.Customer.FirstName,
        CustomerPhoneNumber = inv.Customer.Phone,
        BillingAddress = inv.BillingAddress,
        BillingCity = inv.BillingCity,
        BillingState = inv.BillingState,
        BillingCountry = inv.BillingCountry,
        BillingPostalCode = inv.BillingPostalCode,
        Tracks = T.Select(t => new InvoiceTrackDTO
        {
            InvoiceLineId = line.InvoiceLineId,
            TrackId = t.TrackId,
            TrackName = t.Name,
            Artist = t.Album.Artist.Name,
            UnitPrice = line.UnitPrice,
            Quantity = line.Quantity
        })
    };

    var result = screenset.SingleOrDefault();
    var response = Request.CreateResponse(HttpStatusCode.OK, result);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    return response;

我的 DTO 定义如下:

public class InvoiceDTO
{
    public int InvoiceId { get; set; }
    public DateTime InvoiceDate { get; set; }
    public int CustomerId { get; set; }
    public string CustomerFullName { get; set; }
    public string CustomerPhoneNumber { get; set; }
    public string BillingAddress { get; set; }
    public string BillingCity { get; set; }
    public string BillingState { get; set; }
    public string BillingCountry { get; set; }
    public string BillingPostalCode { get; set; }
    public IEnumerable<InvoiceTrackDTO> Tracks { get; set; }
}

public class InvoiceTrackDTO
{
    public int InvoiceLineId { get; set; }
    public int TrackId { get; set; }
    public string TrackName { get; set; }
    public string Artist { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
}

我的消费者代码是:

message = new HttpClient().GetMessage(host, path, q).Result;
invoice = message.GetObjectFromMessage<InvoiceDTO>().Result;
tracks = invoice.Tracks as IEnumerable<InvoiceTrackDTO>;

我的问题是如果使用 .ToList() 调用 ApiController 会报错:

var result = screenset.ToList();

如果 ApiController 使用 .SingleOrDefault() 返回,它会成功返回发票,但发票项目不能超过一个。

var result = screenset.SingleOrDefault();

我正在尝试根据其 ID 获取一张发票,但该发票包含发票项目列表。

那么这可能吗?

提前致谢。

编辑:

如果我在消费者中这样做:

message = new HttpClient().GetMessage(host, path, q).Result;
invoice = message.GetObjectFromMessage<InvoiceDTO>().Result;
Debug.WriteLine("Invoice: " + invoice.ToJsonString());
tracks = invoice.Tracks as IEnumerable<InvoiceTrackDTO>;
Debug.WriteLine("Tracks: " + tracks.ToJsonString());

ToJsonString 是我将任何对象转换为 Json 字符串的扩展方法。我可以获取发票,但无法跟踪发票项目所在的位置。

我得到的输出和错误如下:

发票:{"InvoiceId":0,"InvoiceDate":"0001-01-01T00:00:00","CustomerId":0,"CustomerFullName":null,"CustomerPhoneNumber":null,"BillingAddress": null,"BillingCity":null,"BillingState":null,"BillingCountry":null,"BillingPostalCode":null,"Tracks":null}

曲目:空

抛出异常:System.Windows.Forms.dll 中的“System.ArgumentOutOfRangeException”

编辑 2: 好的,只是为了澄清一点。

我将内部连接与组连接进行比较,如下所示:

Parent  
Id  Value  
1   A  
2   B  
3   C  

Child  
Id  ChildValue  
1   a1  
1   a2  
1   a3  
2   b1  
2   b2  

内连接

from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }

Result  
Value ChildValue  
A     a1  
A     a2  
A     a3  
B     b1  
B     b2  

组加入

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

Result  
Value  ChildValues  
A      [a1, a2, a3]  
B      [b1, b2]  
C      []  

使用 LINQ 从 Entity Framework 返回时,内部联接有效,但组联接无效。

组连接将像内部连接一样返回。

希望能澄清我的问题:)

【问题讨论】:

  • 当然可以。你暗示你得到一个错误。您是否查看过错误的实际含义?
  • 为什么我在没有得到任何 cmets 或答案的情况下投了反对票?
  • 您收到的错误信息是什么?是编译错误还是运行时错误?
  • 这是一个运行时错误。 ApiController 端没有抛出异常。我已经用我得到的错误更新了我的问题。
  • 在发送到客户端之前,您是否验证了从 LINQ 语句中获取的数据是否正确?

标签: c# entity-framework asp.net-web-api dto


【解决方案1】:

好的,我意识到了这个问题。 Entity Framework 最终基于 Linq 生成 SQL。在 SQL 中,没有包含子对象列表的父对象的概念。返回的记录数将是重复父列的子对象的数量。

我一开始很着急,忘记了一个简单的浏览器或 Fiddler 输出会显示原始 ApiController 方法会返回这个:

所以像这样修改你的 DTO:

public class InvoiceAndItemsDTO
{
    // Invoice
    public int InvoiceId { get; set; }
    public DateTime InvoiceDate { get; set; }
    public int CustomerId { get; set; }
    public string CustomerFullName { get; set; }
    public string CustomerPhoneNumber { get; set; }
    public string BillingAddress { get; set; }
    public string BillingCity { get; set; }
    public string BillingState { get; set; }
    public string BillingCountry { get; set; }
    public string BillingPostalCode { get; set; }
    // Track
    public int InvoiceLineId { get; set; }
    public int TrackId { get; set; }
    public string TrackName { get; set; }
    public string Artist { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
}

然后像这样修改你的 ApiController 方法:

var screenset =
from inv in context.Invoices where inv.InvoiceId == invoiceID
join line in context.InvoiceLines on inv.InvoiceId equals line.InvoiceId
join track in context.Tracks on line.TrackId equals track.TrackId
select new InvoiceAndItemsDTO
{
    InvoiceId = inv.InvoiceId,
    InvoiceDate = inv.InvoiceDate,
    CustomerId = inv.CustomerId,
    CustomerFullName = inv.Customer.LastName + ", " +       inv.Customer.FirstName,
    CustomerPhoneNumber = inv.Customer.Phone,
    BillingAddress = inv.BillingAddress,
    BillingCity = inv.BillingCity,
    BillingState = inv.BillingState,
    BillingCountry = inv.BillingCountry,
    BillingPostalCode = inv.BillingPostalCode,
    InvoiceLineId = line.InvoiceLineId,
    TrackId = track.TrackId,
    TrackName = track.Name,
    Artist = track.Album.Artist.Name,
    UnitPrice = line.UnitPrice,
    Quantity = line.Quantity
};

你的消费者是这样的:

message = new HttpClient().GetMessage(host, path, q).Result;
tracks = message.GetObjectFromMessage<List<InvoiceAndItemsDTO>>().Result;
if (tracks != null)
{
    invoice = tracks.First();
}

【讨论】:

    猜你喜欢
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 2023-02-13
    相关资源
    最近更新 更多