Search code examples
restasp.net-coreodataautomapperdto

Using DTO with OData in .NetCore 2.1


I am writing a test OData Rest API with an InMemoryDatabase. I would like to use DTO(s) to hide the SQL model and adjust a few fields (geographic positions and so on). However, when I use ProjectTo<...> method from AutoMapper, GET request to the API return an empty collection instead of the actual result list.

Do you have any idea about what I am doing wrong ?

Here is the controller :

namespace offers_api.Controllers
{
    public class OffersController : ODataController
    {
        private readonly OfferContext _context;
        private IMapper _mapper;

        public OffersController(OfferContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        [EnableQuery]
        public IActionResult Get()
        {
            IQueryable<Offer> res = _context.Offers.ProjectTo<Offer>(_mapper.ConfigurationProvider); // <-- works without ProjectTo !
            return Ok(res);
        }
    }
}

The automapper declaration :

namespace offers_api.Entities
{
    public class Mapping : Profile
    {
        public Mapping()
        {
            //CreateMap<CategoryEntity, string>().ConvertUsing(cat => cat.Name ?? string.Empty);
            CreateMap<LocationEntity, Location>()
                .ForMember(x => x.longitude, opt => opt.MapFrom(o => 0))
                .ForMember(x => x.latitude, opt => opt.MapFrom(o => 0))
                .ReverseMap();
            CreateMap<OfferEntity, Offer>()
                .ForMember(x => x.Category, opt => opt.MapFrom(o => o.Category.Name))
                .ReverseMap()
                .ForMember(x => x.Category, opt => opt.MapFrom(o => new CategoryEntity { Name = o.Category }));
            CreateMap<OfferPictureEntity, OfferPicture>().ReverseMap();
            CreateMap<UserEntity, User>().ReverseMap();
        }
    }
}

The EDM model :

private static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Offer>("Offers");
    return builder.GetEdmModel();
}

Solution

  • I found the solution.

    In fact, automapper loaded more data than OData's default behaviour. The relation between an offer and it's author was described by a non nullable foreing key. I didn't insert any author in the DB, but OData tried to load a user and saw it was missing in the USER table, so it discarded the Offer result.

    Solution : make the foreign key nullable.

    namespace offers_api.Entities
    {
        public class OfferEntity
    {
            [Key]
            public long Id { get; set; }
            public string Title { get; set; }
            public string Description { get; set; }
            public long AuthorId { get; set; } // <== Bug here : add long? to resolve it...
            public virtual UserEntity Author { get; set; }
        }
    }