OData ASP.Net Core Get Related Entities

时间:2018-04-20 21:25:42

标签: c# asp.net-core odata

I've got an OData Beta + ASP.Net Core + EF project. I'm trying to work out the OData controller functions and I'm getting an error trying to return the related entities:

Models / Customer.cs

public class Customer
{
    public int Id { get; set; }
    public string Standing { get; set; }

    public List<Person> People { get; set; }
    public List<Address> Addresses { get; set; }
}

CustomersController.cs

using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Bookings_Server.EF;
using Bookings_Server.OData.Models;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Routing;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;

namespace Bookings_Server.OData.Controllers
{
    [Produces("application/json")]
    [ODataRoutePrefix("customers")]
    public class CustomersController : ODataController
    {
        private readonly DataContext _context;

        public CustomersController(DataContext context)
        {
            _context = context;
        }

[EnableQuery]
[ODataRoute("({key})/people")]
public IQueryable<Customer> GetPeople([FromODataUri] int key)
{
    var result = _context.Customers.Where(m => m.Id == key).Select(m => m.People).ToList();
    return (result);
}
}

I'm getting an intellisense error under the result variable (within the return) stating:

Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.List<Bookings_Server.OData.Models.Person>>' to 'System.Linq.IQueryable<Bookings_Server.OData.Models.Customer>'. An explicit conversion exists (are you missing a cast?)

I've been looking at other OData V4 examples but they all throw implicit convert errors (assuming this is the difference of working on Aps.Net Core).

2 个答案:

答案 0 :(得分:1)

首先:您需要了解表示(通常)数据库查询的IQueryable<T>与表示内存中集合或数据源的IEnumerable<T>之间的区别。所以:

// WRONG
public IQueryable<Customer> GetPeople([FromODataUri] int key)

// CORRECT
public IEnumerable<Customer> GetPeople([FromODataUri] int key) 

您永远不应该在应用程序之外返回实体框架查询。

其次,您希望包含来自相关实体的数据,而不是选择该数据。所以:

// WRONG
return _context.Customers
    .Where(m => m.Id == key)
    .Select(m => m.People) // "only give me People data"
    .ToList();

// CORRECT
return _context.Customers
    .Where(m => m.Id == key)
    .Include(m => m.People) // "give me Customer WITH People data"
    .ToList();

混合这些结构,最终得到:

[EnableQuery]
[ODataRoute("customers({key})/people")]
public IEnumerable<Customer> GetPeople([FromODataUri] int key)
{
    return _context.Customers
        .Where(m => m.Id == key)
        .Include(m => m.People)
        .ToList();
}

还有一点需要注意的是,您应该始终使用Entity Framework Core的异步版本的数据访问方法:

[EnableQuery]
[ODataRoute("customers({key})/people")]
public async Task<IEnumerable<Customer>> GetPeople([FromODataUri] int key)
{
    return await _context.Customers
        .Where(m => m.Id == key)
        .Include(m => m.People)
        .ToListAsync();
}

作为最后一条评论,您应该更喜欢返回内置IActionResult,以便您轻松更改响应,而不必抛出异常:

[EnableQuery]
[ODataRoute("customers({key})/people")]
public async Task<IActionResult> GetPeople([FromODataUri] int key)
{
    var customers = await _context.Customers
        .Where(m => m.Id == key)
        .Include(m => m.People)
        .ToListAsync();

    // this is only an example
    if (!customers.Any())
    {
        return NotFound();
    }

    return Ok(customers);
}

答案 1 :(得分:0)

好。在一些帮助下,我设法解决了这个问题。我没有意识到[OdataRoute]是区分大小写的,它在我的模型中捡起了人,并且在我的控制器中遇到了这个事实的问题。

改变了这一切,一切都开始像@camilo提议的那样工作。