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).
答案 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提议的那样工作。