对 DTO 的 ASP.NET WebApi OData 支持
Posted
技术标签:
【中文标题】对 DTO 的 ASP.NET WebApi OData 支持【英文标题】:ASP.NET WebApi OData support for DTOs 【发布时间】:2013-06-02 11:03:41 【问题描述】:我有项目实体和 ProjectDTO。 我正在尝试创建一个可以获取和返回 ProjectDTO 并使其支持 OData 的 WebAPI 控制器方法。
问题是我使用的 ORM 可以使用 Project 实体而不是 Project DTO 来查询数据库。有什么方法可以将基于 ProjectDTO 的 OData 过滤/排序/分页应用于项目实体查询?
public ODataQueryResult<ProjectDTO> GetProjects(ODataQueryOptions<ProjectDTO> query)
var context = new ORM_Context();
var projects = context.Projects; // IQueryable<Project>
var projectDtos = query.ApplyTo(projectDTOs)); // <-- I want to achieve something similar here
var projectDTOs =
projects.Select(
x =>
new ProjectDTO
Id = x.Id,
Name = x.Name
);
var projectsQueriedList = projectDtos.ToList();
var result = new ODataQueryResult<ProjectDTO>(projectsQueriedList, totalCount);
return result;
【问题讨论】:
你最后做了什么? 【参考方案1】:类似 的东西(我没有尝试编译它)
using(var dataContext = new ORM_Context())
var projects = dataContext.Projects; // IQueryable<Project>
//Create a set of ODataQueryOptions for the internal class
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Project>("Project");
var context = new ODataQueryContext(
modelBuilder.GetEdmModel(), typeof(Project));
var newOptions = new ODataQueryOptions<Project>(context, Request);
var t = new ODataValidationSettings() MaxTop = 25 ;
var s = new ODataQuerySettings() PageSize = 25 ;
newOptions.Validate(t);
IEnumerable<Project> internalResults =
(IEnumerable<Project>)newOptions.ApplyTo(projects, s);
int skip = newOptions.Skip == null ? 0 : newOptions.Skip.Value;
int take = newOptions.Top == null ? 25 : newOptions.Top.Value;
var projectDTOs =
internalResults.Skip(skip).Take(take).Select(x =>
new ProjectDTO
Id = x.Id,
Name = x.Name
);
var projectsQueriedList = projectDtos.ToList();
var result = new ODataQueryResult<ProjectDTO>(
projectsQueriedList, totalCount);
return result;
【讨论】:
非常感谢您的回答!乍一看,它看起来很有希望。我会测试这个解决方案,希望它能奏效。 几乎完美,稍作改动就为我出色地工作,ODataQueryResult 不再存在,所以我返回了一个 IQueryable。它不包含项目的总数,因此对于分页来说并不出色,这是一种耻辱,但它确实包含指向下一页的链接。 @BenCr 现在有一个PageResult<>
类msdn.microsoft.com/en-us/library/jj890608(v=vs.111).aspx
将 $inlinecount=allpages 添加到查询中会返回计数作为结果的一部分。
qujck 我认为控制器将返回的 IQueryable 转换为 PageResult 因为它包含下一页链接和总数【参考方案2】:
试试这个:
public object GetProjects(ODataQueryOptions<Project> query)
var context = new ORM_Context();
var projects = query.ApplyTo(context.Projects);
var projectDTOs = projects.Select(
x =>
new ProjectDTO
Id = x.Id,
Name = x.Name
);
return new
TotalCount = Request.GetInlineCount(), //before paging
Results = projectDTOs.ToList()
;
显然,这里最重要的是将正确的类型传递给 ODataQueryOptions,然后它就会很好地发挥它的魔力。这是因为它使用该特定类型来查询您的集合/数据库上下文,因此它必须是实际存储在集合/上下文中的类型,而不是您尝试返回的类型。
显然,您的 DTO 应该与您的 ORM 对象非常相似(它们在您的 sn-p 中也是如此),否则从用户/客户的角度来看,这将无法很好地工作。
我没有尝试编译上面的代码,因为我没有你的类和其他基础设施,但它应该足以传达这个想法。
【讨论】:
【参考方案3】:我认为最简单的方法是使用 AutoMapper。所以,对于你的 DTO
[DataContract(Name = "Products")]
public class ProductDTO
[Key]
[DataMember]
public string MyProductMember1 get; set;
[DataMember]
public string MyProductMember2 get; set;
...
你应该在 AutoMapper 配置的某个地方写:
Mapper.CreateMap<Product, ProductDTO>();
以及为 OData 构建 IEdmModel 的某处:
builder.EntitySet<ProductDTO>("Products");
你的控制器的代码看起来像
public class ProductsController : ODataController
[EnableQuery]
public IHttpActionResult Get()
var products = context.Products; // IQueryable<Product>
return Ok(products.Project().To<ProductDTO>());
这样你就不需要直接暴露你的 ORM 实体,并且可以使用 OData 进行过滤、分页、计数甚至扩展嵌套集合,对于 EF 它将使用 Product 所在的表转换为相应的 SQL 请求映射。但要小心:对于更复杂的情况(例如嵌套集合),它可能会导致非最佳 SQL 请求。
【讨论】:
以上是关于对 DTO 的 ASP.NET WebApi OData 支持的主要内容,如果未能解决你的问题,请参考以下文章
这是如何使用 Entity Framework Core 和 ASP.NET Core MVC 2.2+ 和 3.0 创建数据传输对象 (DTO)
ASP.NET WebAPI 服务对公司内服务的身份验证(Intranet)
如何对 ASP.NET WebApi 的每个请求应用自定义验证到 JWT 令牌?
对“asp.net core WebApi”的 ajax 调用返回一个奇怪/奇怪的值
ASP.NET WebApi 对 ValidateClientAuthentication 上的 OPTIONS 的 400 错误请求,即使在 context.Validated() 上也是如此