Value cannot be null or empty. (Parameter 'entitySetName')
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 505
- Forks
- 186
- PR merge metrics
- No merged PRs in 30d
Description
ASP.NET Core OData 8.2.3
I am trying to do a post to http://localhost:5107/v1/users('michel')/calendarGroups which works without Accept: application/json;odata.metadata=full , but with I get the error
Value cannot be null or empty. (Parameter 'entitySetName')
see screenshot. It seems Identifier in this case is empty, I am not sure why. It does seems to work even with full when I rewrite the url to /v1/ instead of v1/.
What am I doing wrong ? I think I have followed most standard documentation. ( If you see errors in naming convention, I have temporarily disabled my camelcase converter to make sure that wasn't the problem, haven't updated all urls in code).
I am trying to use attribute routing and have this as my program.cs
using My.Booking.API;
using My.Booking.Domain.Models;
using My.Booking.Persistence;
using Microsoft.AspNetCore.OData.Batch;
using Microsoft.OData.ModelBuilder;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.OData;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
var edmBuilder = new ODataConventionModelBuilder
{
Namespace = "graph"
};
edmBuilder.EntitySet<Entity>("entities");
edmBuilder.EntitySet<OutlookItem>("outlookItem");
edmBuilder.EntitySet<Calendar>("calendars");
edmBuilder.EntitySet<CalendarGroup>("calendarGroups");
edmBuilder.EntitySet<Event>("events");
var users = edmBuilder.EntitySet<User>("users");
users.HasRequiredBinding(p => p.Calendar,"Calendar");
edmBuilder.Singleton<Me>("me");
edmBuilder.EnableMyLowerCamelCase();
var edmModel = edmBuilder.GetEdmModel();
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
//policy.WithHeaders("X-API-KEY", "Origin", "X-Requested-With", "Content-Type", "Accept", "Access-Control-Request-Method","prefer","Access-Control-Allow-Origin");
//policy.WithMethods("GET", "POST", "OPTIONS", "PUT", "DELETE");
});
});
builder.Services.AddDbContext<BookingDbContext>(opt => opt.UseSqlServer());
builder.Services.AddControllers().AddOData(options =>
{
options.RouteOptions.EnablePropertyNameCaseInsensitive = true;
options.RouteOptions.EnableControllerNameCaseInsensitive = true;
options.RouteOptions.EnableKeyAsSegment = true;
options.RouteOptions.EnableKeyInParenthesis = true;
options.Select().Filter().Count().OrderBy().Expand()
.AddRouteComponents("v1", edmModel, opts =>
{
opts.AddSingleton<ODataBatchHandler,DefaultODataBatchHandler>();
});
options.TimeZone = TimeZoneInfo.Utc;
});
var app = builder.Build();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseCors();
app.UseODataRouteDebug();
app.UseODataBatching();
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.UseHttpsRedirection();
await app.ResetDatabaseAsync();
app.Run();
This is my UsersController.cs
using My.Booking.Domain.Models;
using My.Booking.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Deltas;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using Microsoft.EntityFrameworkCore;
namespace My.Booking.API.Controllers;
public class UsersController(BookingDbContext dbContext) : ODataController
{
[EnableQuery]
public ActionResult<IQueryable<User>> Get()
{
return Ok(dbContext.Users);
}
public ActionResult<User> Get([FromRoute] string key)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
public ActionResult Post([FromBody] User user)
{
dbContext.Users.Add(user);
dbContext.SaveChanges();
return Created(user);
}
public ActionResult Put([FromRoute] string key, [FromBody] User updatedUser)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user == null)
{
return NotFound();
}
// customer.Name = updatedCustomer.Name;
// customer.CustomerType = updatedCustomer.CustomerType;
// customer.CreditLimit = updatedCustomer.CreditLimit;
// customer.CustomerSince = updatedCustomer.CustomerSince;
dbContext.SaveChanges();
return Updated(user);
}
public ActionResult Patch([FromRoute] string key, [FromBody] Delta<User> delta)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user == null)
{
return NotFound();
}
delta.Patch(user);
dbContext.SaveChanges();
return Updated(user);
}
public ActionResult Delete([FromRoute] string key)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user != null)
{
dbContext.Users.Remove(user);
}
dbContext.SaveChanges();
return NoContent();
}
[HttpGet("v1/Users({key})/CalendarGroups")]
[HttpGet("v1/Users/{key}/CalendarGroups")]
[EnableQuery]
public IQueryable<CalendarGroup> GetCalendarGroups([FromRoute] string key)
{
Microsoft.OData.Evaluation.ODataConventionalUriBuilder.BuildEntitySetUri(
return (from cgu in dbContext.CalendarGroupUsers
join cg in dbContext.CalendarGroups on cgu.CalendarGroupsId equals cg.Id
where cgu.UserId == key
select cg).AsNoTracking();
}
[HttpGet("v1/users({key})/calendarGroups({relatedKey})")]
[HttpGet("v1/users/{key}/calendarGroups/{relatedKey}")]
[EnableQuery]
public IQueryable<CalendarGroup> GetCalendarGroups([FromRoute] string key,[FromRoute] string relatedKey)
{
return (from cgu in dbContext.CalendarGroupUsers
join cg in dbContext.CalendarGroups on cgu.CalendarGroupsId equals cg.Id
where cgu.UserId == key && cg.Id == relatedKey
select cg).AsNoTracking();
}
[HttpPost("v1/Users({key})/CalendarGroups")]
[HttpPost("v1/Users/{key}/CalendarGroups")]
public IActionResult PostToCalendarGroups([FromRoute] string key, [FromBody] CalendarGroup calendarGroup)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user == null)
{
return NotFound();
}
dbContext.CalendarGroups.Add(calendarGroup);
user.CalendarGroups.Add(calendarGroup);
dbContext.SaveChanges();
return Created(calendarGroup);
}
[HttpGet("v1/users({key})/calendar")]
[HttpGet("v1/users/{key}/calendar")]
public IQueryable<Calendar> GetCalendar([FromRoute] string key)
{
return (from cgu in dbContext.CalendarGroupUsers
join cg in dbContext.CalendarGroups on cgu.CalendarGroupsId equals cg.Id
join c in dbContext.Calendars on cg.Id equals EF.Property<string>(c, "CalendarGroupId")
where c.IsDefaultCalendar == true && cgu.Default == true && cgu.UserId == key
select c)
.AsNoTracking();
}
[HttpGet("v1/users({key})/calendarGroups({relatedKey})/calendars")]
[HttpGet("v1/users/{key}/calendarGroups/{relatedKey}/calendars")]
public IQueryable<Calendar> GetCalendars([FromRoute] string key,[FromRoute] string relatedKey)
{
return (from cgu in dbContext.CalendarGroupUsers
join cg in dbContext.CalendarGroups on cgu.CalendarGroupsId equals cg.Id
join c in dbContext.Calendars on cg.Id equals EF.Property<string>(c, "CalendarGroupId")
where cgu.UserId == key && cg.Id == relatedKey
select c)
.AsNoTracking();
}
[HttpPost("v1/users({key})/calendarGroups({relatedKey})/calendars")]
[HttpPost("v1/users/{key}/calendarGroups/{relatedKey}/calendars")]
public IActionResult PostToCalendarGroups([FromRoute] string key, [FromRoute] string relatedKey, [FromBody] Calendar calendar)
{
var user = dbContext.Users.SingleOrDefault(d => d.Id == key);
if (user == null)
{
return NotFound();
}
var t = (from cgu in dbContext.CalendarGroupUsers
join cg in dbContext.CalendarGroups on cgu.CalendarGroupsId equals cg.Id
where cgu.UserId == key && cg.Id == relatedKey
select cg).SingleOrDefault();
if (t == null)
{
return NotFound();
}
calendar.Owner = new EmailAddress(user.Mail,user.DisplayName);
dbContext.Calendars.Add(calendar);
t.Calendars.Add(calendar);
dbContext.SaveChanges();
return Created(calendar);
}
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Program.cs and UsersController.cs, focusing on the POST /v1/users({key})/calendarGroups route and its alternate attribute routes. Reproduce the request with and without Accept: application/json;odata.metadata=full, then compare it with the working /v1/ URL. Done means the reported POST request no longer produces the empty entitySetName error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100