Используя 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>();
}
...