OData: как использовать имена DataMember моделей DTO в качестве фильтров с ODataQueryOptions - PullRequest
1 голос
/ 19 сентября 2019

Я занимаюсь разработкой фильтрации для существующего проекта WEB API.Для этого было решено использовать OData.У меня есть базовая инфраструктура (Core EF и DbContext).Кроме того, для веб-API я использую модели DTO этих основных объектов.Все модели DTO имеют аннотации данных.Проблема в том, что фильтрация OData не работает с именами аннотаций данных.Я пытался следовать предложениям из других тем, но потерпел неудачу.Я буду признателен за любые предложения по этой проблеме.

Установлен Microsoft.AspNet.OData 7.2.1.

Основные объекты:

[Serializable]
[DataContract(IsReference = true)]
public class Staff
{
    public Staff
        {
            this.StaffRoles = new HashSet<StaffRole>();
        }

    [DataMember]
    public bool Active { get; set; }

    [DataMember]
    public int? ContactId { get; set; }

    [DataMember]
    public bool? ExternalStaff { get; set; }

    [DataMember]
    public int PrimaryKey { get; set; }

    [DataMember]
    public string StaffLogin { get; set; }

    [DataMember]
    public virtual Contact Contact { get; set; }

    [DataMember]
    public virtual ICollection<StaffRole> StaffRoles { get; set; }
}
[Serializable]
[DataContract(IsReference = true)]
public class Contact
{
    public Contact()
    {
        this.StaffPersons = new HashSet<Staff>();
    }

    [DataMember]
    public string Email { get; set; }

    [DataMember]
    public string FirstName { get; set; }

    [DataMember]
    public string LastName { get; set; }

    [DataMember]
    public int PrimaryKey { get; set; }

    [DataMember]
    public virtual ICollection<Staff> StaffPersons { get; set; }
}

Модель DTO:

[DataContract]
public class StaffDTO
{
    [DataMember(Name = "id")]
    public int Id { get; set; }

    [DataMember(Name = "first_name")]
    public string FirstName { get; set; }

    [DataMember(Name = "last_name")]
    public string LastName { get; set; }

    [DataMember(Name = "is_status_active")]
    public bool IsStatusActive { get; set; }

    [DataMember(Name = "login")]
    public string Login { get; set; }

    [DataMember(Name = "email")]
    public string Email { get; set; }

    [DataMember(Name = "is_staff_external")]
    public bool IsStaffExternal { get; set; }

    [DataMember(Name = "roles")]
    public IEnumerable<StaffRoleDTO> StaffRoles { get; set; }
}

WebApiConfig:

    var builder = new ODataConventionModelBuilder();
    builder.ModelAliasingEnabled = true;

    builder.EntitySet<StaffDTO>("Staffs").EntityType.Name= "Staff";
    var staffType = builder.EntityType<StaffDTO>();

    config.MapODataServiceRoute("ODataRoute", null, builder.GetEdmModel());
    config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);

Контроллер веб-API:

public class CommonODataController : ODataController
{
    private IMapper mapper;

    private MyDbContext dbContext;

    public CommonODataController(IMapper mapper, MyDbContext dbContext)
    {
        this.mapper = mapper;
        this.dbContext = dbContext;
    }

    [HttpGet]
    [EnableQuery]
    public IQueryable<StaffViewModel> GetStaff()
    {
        IQueryable<Staff> staffQuery = this.dbContext.Staffs
            .Include(x => x.Contact)
            .Include(x => x.StaffRoles);

        var staffDTOQuery = this.mapper.ProjectTo<StaffDTO>(staffQuery);

        return staffDTOQuery;
    }
}

Конфигурация AutoMapper:

CreateMap<Staff, StaffDTO>()
    .ForMember(dst => dst.Id, opts => opts.MapFrom(src => src.PrimaryKey))
    .ForMember(dst => dst.FirstName, opts => opts.MapFrom(src => src.Contact.FirstName))
    .ForMember(dst => dst.LastName, opts => opts.MapFrom(src => src.Contact.LastName))
    .ForMember(dst => dst.IsStatusActive, opts => opts.MapFrom(src => src.Active))
    .ForMember(dst => dst.Login, opts => opts.MapFrom(src => src.StaffLogin))
    .ForMember(dst => dst.Email, opts => opts.MapFrom(src => src.Contact.Email))
    .ForMember(dst => dst.IsStaffExternal, opts => opts.MapFrom(src => src.ExternalStaff))
    .ForMember(dst => dst.StaffRoles, opts => opts.MapFrom(src => src.StaffRoles));

Когда я отправляю запрос"... / staff? $ top = 50 & $ filter = first_name eq 'some name'" Я получаю ошибку "Свойство экземпляра 'first_name' не определено для типа 'StaffDTO'}".

Любые идеичто не так?

...