Reputation: 79
Have a fairly basic Web API project on ASP.NET Core configured with EF which was working okay with Web API. I was following this article to convert to use Odata and I can't quite get it to work.
I have a parent object called customer with 2x child objects: Addresses and Person. I have seeded the database so I can see there is data there, and the Odata endpoint looks good because when you start the project it displays the entities and odata/$metadata displays the EDM structure as expected.
The only issue I have currently is that when I navigate to a URL, such as /odata/customers, I receive a blank screen. In postman it returns 404.
I've combed through the example project from Lucas (the article I was following) and reseached a fair bit online and can't quite see what I'm doing wrong.
I'm sure it's something simple/silly, but any constructive guidance welcomed :)
** Edit ** Removed additional code for simplicity (and based on feedback). Let me know if additional information is required.
Cheers,
Adam
File Path: Odata\BookingsModelBuilder.cs
using System;
using Microsoft.AspNet.OData.Builder;
using Microsoft.OData.Edm;
using Bookings_Server.OData.Models;
namespace Bookings_Server
{
public class BookingsModelBuilder
{
public IEdmModel GetEdmModel(IServiceProvider serviceProvider)
{
var builder = new ODataConventionModelBuilder(serviceProvider);
builder.EntitySet<Address>("addresses")
.EntityType
.Filter() // Allow for the $filter Command
.Count() // Allow for the $count Command
.Expand() // Allow for the $expand Command
.OrderBy() // Allow for the $orderby Command
.Page() // Allow for the $top and $skip Commands
.Select();// Allow for the $select Command;
builder.EntitySet<Customer>("customers")
.EntityType
.Filter() // Allow for the $filter Command
.Count() // Allow for the $count Command
.Expand() // Allow for the $expand Command
.OrderBy() // Allow for the $orderby Command
.Page() // Allow for the $top and $skip Commands
.Select();// Allow for the $select Command;
builder.EntitySet<Person>("people")
.EntityType
.Filter() // Allow for the $filter Command
.Count() // Allow for the $count Command
.Expand() // Allow for the $expand Command
.OrderBy() // Allow for the $orderby Command
.Page() // Allow for the $top and $skip Commands
.Select();// Allow for the $select Command;
return builder.GetEdmModel();
}
}
}
File path: EF\DataContext.CS
using Microsoft.EntityFrameworkCore;
using Bookings_Server.OData.Models;
namespace Bookings_Server.EF
{
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
public DbSet<Address> Addresses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Tenant> Tenants { get; set; }
}
}
File path: Controllers\CustomersController.cs
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Bookings_Server.EF;
using Bookings_Server.OData.Models;
using Microsoft.AspNet.OData;
namespace Bookings_Server.OData.Controllers
{
[Produces("application/json")]
public class CustomersController : ODataController
{
private readonly DataContext _context;
public CustomersController(DataContext context)
{
_context = context;
}
// GET: odata/customers
[EnableQuery(PageSize = 20)]
public IQueryable<Customer> Get() => _context.Customers.AsQueryable();
/*
public IActionResult Get()
{
return Ok(_context.Customers.AsQueryable());
}
*/
}
}
File path: startup.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNet.OData.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Bookings_Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, BookingsModelBuilder BookingsModelBuilder)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("cors");
// app.UseMvc();
// Added for Odata config
app.UseMvc(routeBuilder =>
{
routeBuilder.MapODataServiceRoute("ODataRoutes", "odata", BookingsModelBuilder.GetEdmModel(app.ApplicationServices));
});
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("cors", builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}
));
var connection = @"Server=(localdb)\mssqllocaldb;Database=BookingsDB;Trusted_Connection=True;";
services.AddDbContext<EF.DataContext>(options => options.UseSqlServer(connection));
// Add OData configuration
services.AddOData();
services.AddTransient<BookingsModelBuilder>();
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
}
}
Upvotes: 1
Views: 3355
Reputation: 79
Ok. Worked this out. It was something silly in the end. I was missing the decorator on the CustomerContoller.cs
[ODataRoute("customers")]
and the namespace:
using Microsoft.AspNet.OData.Routing;
After that everything started working fine.
// GET: odata/customers
[ODataRoute("customers")]
[EnableQuery(PageSize = 20)]
public IQueryable<Customer> Get() => _context.Customers.AsQueryable();
Additional Info: http://odata.github.io/WebApi/03-03-attrribute-routing/
Upvotes: 1
Reputation: 20987
As a work around you can apply the changes directly to existing controllers, This code can easily be made Generic and added to a base controller, and this will work with existing controllers:
[HttpGet]
[EnableQuery]
public async Task<Skill[]> GetFilteredODataList(ODataQueryOptions<Skill> q)
{
var skillsQuery = this._context.Skills.AsQueryable();
if (q?.Filter != null)
{
skillsQuery = q.Filter.ApplyTo(skillsQuery, new ODataQuerySettings()) as IQueryable<Skill>;
}
return await skillsQuery.ToArrayAsync();
}
Upvotes: 0