C# ASP.NET WebApi OData对DTO的支持

C# ASP.NET WebApi OData对DTO的支持,c#,asp.net-web-api,odata,C#,Asp.net Web Api,Odata,我有项目实体和ProjectDTO。 我正在尝试创建一个WebAPI控制器方法,该方法可以获取和返回ProjectDTO,并使其支持OData 问题是我使用的ORM可以使用项目实体而不是项目DTO查询数据库。是否有任何方法可以将基于ProjectDTO的OData的筛选/排序/分页应用于项目实体查询 public ODataQueryResult<ProjectDTO> GetProjects(ODataQueryOptions<ProjectDTO> query) {

我有项目实体和ProjectDTO。 我正在尝试创建一个WebAPI控制器方法,该方法可以获取和返回ProjectDTO,并使其支持OData

问题是我使用的ORM可以使用项目实体而不是项目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;
}
公共ODataQueryResult GetProject(ODataQueryOptions查询) { var context=新的ORM_context(); var projects=context.projects;//IQueryable var projectDtos=query.ApplyTo(projectDtos));// 新项目 { Id=x.Id, Name=x.Name }); var projectsQueriedList=projectDtos.ToList(); var结果=新的ODataQueryResult(projectsQueriedList,totalCount); 返回结果; } 类似这样的内容(我没有尝试编译它)

使用(var dataContext=new ORM_Context())
{
var projects=dataContext.projects;//IQueryable
//为内部类创建一组ODataQueryOptions
ODataModelBuilder modelBuilder=新ODataConventionModelBuilder();
modelBuilder.EntitySet(“项目”);
var context=新的ODataQueryContext(
getedModel(),typeof(Project));
var newOptions=新的ODataQueryOptions(上下文、请求);
var t=new-ODataValidationSettings(){MaxTop=25};
var s=new-ODataQuerySettings(){PageSize=25};
newOptions.Validate(t);
可数结果=
(IEnumerable)newOptions.ApplyTo(projects,s);
int skip=newOptions.skip==null?0:newOptions.skip.Value;
int take=newOptions.Top==null?25:newOptions.Top.Value;
var项目DTOS=
internalResults.Skip(Skip).Take(Take).Select(x=>
新项目
{
Id=x.Id,
Name=x.Name
});
var projectsQueriedList=projectDtos.ToList();
var结果=新的ODataQueryResult(
ProjectScreedList,totalCount);
返回结果;
}
试试这个:

    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()
        };
    }
公共对象GetProjects(ODataQueryOptions查询) { var context=新的ORM_context(); var projects=query.ApplyTo(context.projects); var projectDTOs=projects。选择( x=> 新项目 { Id=x.Id, Name=x.Name }); 还新 { TotalCount=Request.GetInlineCount(),//分页前 结果=projectDTOs.ToList() }; } 显然,这里最重要的事情是将正确的类型传递给ODataQueryOptions,然后它就可以很好地执行它的魔法。这是因为它使用该特定类型来查询集合/数据库上下文,因此它必须是实际存储在集合/上下文中的类型,而不是您试图返回的类型

显然,DTO应该与ORM对象非常相似(在代码片段中也是如此),否则从用户/客户机的角度来看,这不会很好地工作


我没有尝试编译上面的代码,因为我没有您的类和其他基础结构,但它应该足以传达这个想法。

我认为最简单的方法是使用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>();
Mapper.CreateMap();
在为OData构建IEdmModel的过程中:

builder.EntitySet<ProductDTO>("Products");
builder.EntitySet(“产品”);
控制器的代码如下所示

public class ProductsController : ODataController
{
    [EnableQuery]
    public IHttpActionResult Get()
    {
        var products = context.Products; // IQueryable<Product>
        return Ok(products.Project().To<ProductDTO>());
    }
}
公共类产品控制器:ODataController
{
[启用查询]
public IHttpActionResult Get()
{
var products=context.products;//IQueryable
返回Ok(products.Project().To());
}
}

通过这种方式,您不需要直接公开ORM实体,并且可以使用OData进行过滤、分页、计数甚至扩展嵌套集合,对于EF,它将使用产品映射到的表转换为相应的SQL请求。但要小心:对于更复杂的情况(例如嵌套集合),它可能会导致非最佳SQL请求。

非常感谢您的回答!乍一看很有希望。我将测试这个解决方案,并希望它能起作用。几乎完美,对我来说非常出色,只是做了一个小小的改变,ODataQueryResult已经不存在了,所以我返回了一个IQueryable。它没有包含对分页来说不太好的项目总数,这是一个遗憾,但它确实包含到下一页的链接。@BenCr现在有一个
PageResult
类将$inlinecount=allpages添加到查询中会将计数作为结果的一部分返回。qujck我认为控制器会将返回的IQueryable转换为PageResult因为它包含了下一页的链接和总数,所以你最终做了什么?
public class ProductsController : ODataController
{
    [EnableQuery]
    public IHttpActionResult Get()
    {
        var products = context.Products; // IQueryable<Product>
        return Ok(products.Project().To<ProductDTO>());
    }
}