Используя EF и WebAPI, как я могу вернуть ViewModel и поддерживать IQueryable/OData?

У меня есть проект ASP.NET WebAPI. Недавно я создал EntityFramework для всех таблиц данных. Но я не хочу раскрывать свой уровень данных и схему для своих пользователей. Как я могу сопоставить мои объекты с ViewModel (automapper?) И предоставить тип возвращаемого IQueryable, чтобы мой API поддерживал OData?

OData поддерживает состав запросов и SQL-подобные параметры. Думаю, мне нужно было бы предложить двухсторонний перевод для части запроса-композиции? Означает ли это пользовательский поставщик LINQ? Я надеюсь, что это будет легче.

Или я должен отказаться от IQueryable/OData?

Ответ 1

Я нашел ответ здесь: Веб-API Queryable - как применить AutoMapper?

Вместо использования [Queryable] вы можете использовать параметр типа ODataQueryOptions<T> для применения операций OData к любому типу или запросу LINQ. Вот отличный пример, который даже не нужно использовать AutoMapper:

public virtual IQueryable<PersonDto> Get(ODataQueryOptions<Person> odataQuery){
    odataQuery.Validate(new ODataValidationSettings(){
        AllowedFunctions = AllowedFunctions.AllMathFunctions
    });
    var people = odataQuery.ApplyTo(uow.Person().GetAll());
    return ConvertToDtos(people);
}

Здесь страница Microsoft, объясняющая особенности этого использования. (примерно на полпути вниз)

Ответ 2

Мне удалось успешно протестировать это с помощью класса ViewModel.

public class InvoiceViewModel
{
    public int InvoiceID { get; set; }
    public string InvoiceNumber { get; set; }
}

в Get, выберите из своего объекта в свою модель просмотра:

public override IQueryable<InvoiceViewModel> Get()
    {
        var ctx = new CreditPointEntities();
        return ctx.Invoices.Select(i => new InvoiceViewModel
            {
                InvoiceID = i.InvoiceID,
                InvoiceNumber = i.InvoiceNumber
            }).AsQueryable();
    }

Убедитесь, что вы используете модель представления в строке modelbuilder в webapiconfig.cs

modelBuilder.EntitySet<InvoiceViewModel>("Invoice");

с этим, вы можете использовать URL-адрес, например

http://website/odata/Invoice?$filter=InvoiceID eq 1

Я подтвердил через sql-профайлер, что фильтр передается SQL.

Ответ 3

если вы используете Automapper, вы можете использовать в нем проекции. Пример:

    public class ProductsController : EntitySetController<Product, int>
    {
         private DbProductsContext _db = new DbProductsContext();

         public override IQueryable<ProductDto> Get()
         {
            return _db.Products.Project().To<ProductDto>();
         }
    ...