简体   繁体   English

ASP.NET WebApi OData对DTO的支持

[英]ASP.NET WebApi OData support for DTOs

I have Project entity and ProjectDTO. 我有Project实体和ProjectDTO。 I'm trying to create an WebAPI controller method that can take and return ProjectDTOs and make it support OData. 我正在尝试创建一个WebAPI控制器方法,该方法可以获取并返回ProjectDTO并使其支持OData。

The problem is that I'm using ORM that can query the database using Project entity not Project DTO. 问题是我正在使用可以使用Project实体而不是Project DTO查询数据库的ORM。 Is there any way that I can apply filtering/sorting/paging from OData based on ProjectDTO to Project entity query? 有什么方法可以根据ProjectDTO从OData应用过滤/排序/分页到Project实体查询?

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;
}

Something like this (I haven't tried to compile it) 这样的东西(我还没试过编译)

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;
}

I think the easiest way to do this is by using AutoMapper. 我认为最简单的方法是使用AutoMapper。 So, for your DTO 所以,对于你的DTO

    [DataContract(Name = "Products")]
    public class ProductDTO
    {
        [Key]
        [DataMember]
        public string MyProductMember1 { get; set; }

        [DataMember]
        public string MyProductMember2 { get; set; }
        ...
    }

you should write somewhere in AutoMapper configuration: 你应该在AutoMapper配置中写一些地方:

Mapper.CreateMap<Product, ProductDTO>();

and somewhere in building IEdmModel for OData: 以及为OData构建IEdmModel的某个地方:

builder.EntitySet<ProductDTO>("Products");

and the code for your controller will look like 并且控制器的代码看起来像

public class ProductsController : ODataController
{
    [EnableQuery]
    public IHttpActionResult Get()
    {
        var products = context.Products; // IQueryable<Product>
        return Ok(products.Project().To<ProductDTO>());
    }
}

This way you don't need to expose your ORM entities directly, and can use OData for filtering, paging, count, and even expand nested collections, and for EF it will translate into corresponding SQL requests using table to which Product is mapped. 这样您就不需要直接公开ORM实体,并且可以使用OData进行过滤,分页,计数甚至扩展嵌套集合,对于EF,它将使用Product映射到的表转换为相应的SQL请求。 But be careful: for more complicated cases (nested collections, for example) it may result in non-optimal SQL request. 但要小心:对于更复杂的情况(例如,嵌套集合),可能会导致非最佳SQL请求。

Try this: 尝试这个:

    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()
        };
    }

Apparently the most important thing here is to pass the correct type to ODataQueryOptions<> and then it performs its magic just fine. 显然,最重要的是将正确的类型传递给ODataQueryOptions <>然后它就可以很好地执行它的魔法。 This is because it uses that particular type to query your collection/db context so it must the of the type that is actually stored in the collection/context rather that what you are trying to return. 这是因为它使用该特定类型来查询您的集合/ db上下文,因此它必须是实际存储在集合/上下文中的类型,而不是您要返回的内容。

Obviously, your DTOs should closely resemble your ORM objects (and they do in your snippet) or this will not work very well from user's/client's perspective. 显然,您的DTO应该与您的ORM对象非常相似(并且它们在您的代码段中),或者从用户/客户的角度来看,这不会很好。

I did not try to compile the above code cause I don't have your classes and other infrastructure but it should be sufficient to convey the idea. 我没有尝试编译上面的代码,因为我没有你的类和其他基础设施,但它应该足以传达这个想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM