The app type is a Hosted Blazor web-assembly. And below are the versions of the nuget packages I am using. There is an error that occurs when trying to expand a navigation property that is a many-to-many relationship. The classes are mapped to DTO classes that flattens the middle relationship class.
- .Net core Version="3.1"
- AutoMapper Version="10.0.0"
- AutoMapper.AspNetCore.OData.EFCore Version="2.0.1"
- AutoMapper.Extensions.ExpressionMapping Version="4.0.1"
- AutoMapper.Extensions.Microsoft.DependencyInjection Version="8.0.1"
- Microsoft.AspNetCore.Components.WebAssembly.Server Version="3.2.1"
- Microsoft.AspNetCore.OData Version="7.5.0"
To run this repo, you will need the free version of SQL Server or better
Set the EfCoreAutomapperOdata.Server project as the startup project and navigate to the Courses page (https://localhost:5001/courses) and click on either course. This will throw the following error:
System.InvalidOperationException: No generic method 'Include' on type 'Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. at System.Linq.Expressions.Expression.FindMethod(Type type, String methodName, Type[] typeArgs, Expression[] args, BindingFlags flags)...
See here - Entity Models and here - Dto Models for class definition
Automapper Config public class AutomapperConfig : Profile
{
public AutomapperConfig()
{
CreateMap<Instructor, InstructorDto>();
CreateMap<InstructorDto, Instructor>();
CreateMap<Course, CourseDto>()
.ForMember(dto => dto.Students, opt => {
opt.MapFrom(_ => _.Students.Select(y => y.Student));
});
CreateMap<CourseDto, Course>()
.ForMember(ent => ent.Students, ex => ex
.MapFrom(x => x.Students.Select(y => new CourseStudent {
CourseId = x.Id,
StudentId = y.Id
})));
CreateMap<Student, StudentDto>()
.ForMember(dto => dto.Courses, opt => {
opt.MapFrom(x => x.Courses.Select(y => y.Course));
})
.ForMember(dto => dto.Friends, opt => {
opt.MapFrom(x => x.Friends.Select(y => y.Friend));
});
CreateMap<StudentDto, Student>()
.ForMember(ent => ent.Courses, ex => ex
.MapFrom(x => x.Courses.Select(y => new CourseStudent
{
StudentId = x.Id,
CourseId = y.Id
})));
}
}
Startup
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// ------ Some code removed for brevity ------
services.AddOData();
services.AddAutoMapper(cfg => { cfg.AddExpressionMapping(); },typeof(AutomapperConfig));
// ------ Some code removed for brevity ------
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ------ Some code removed for brevity ------
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Filter().OrderBy().Count().Expand().MaxTop(1000);
endpoints.MapODataRoute("odata", "odata", GetEdmModel());
endpoints.MapFallbackToFile("index.html");
});
}
private IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<CourseDto>("Courses");
builder.EntitySet<InstructorDto>("Instructors");
builder.EntitySet<StudentDto>("Students");
return builder.GetEdmModel();
}
}
Course Controller
public class CourseController : ODataController
{
protected readonly BlazorContext _context;
protected readonly IMapper _mapper;
public CourseController(BlazorContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
[HttpGet]
[ODataRoute("Courses")]
public async Task<IActionResult> Get(ODataQueryOptions<CourseDto> options)
{
return Ok(await _context.Course.GetAsync(_mapper, options));
}
[HttpGet]
[ODataRoute("Courses({id})")]
public async Task<IActionResult> Get([FromODataUri] int id, ODataQueryOptions<CourseDto> options)
{
return Ok((await _context.Course.GetAsync(_mapper, options)).Where(s => s.Id == id).ToList());
}
}
Sample odata api query that fails
/odata/Courses?$expand=Students
I have built demo Blazor WASM app for this issue to reproduce