asp.net - WebAPI 中的 DTO 和投影

标签 asp.net entity-framework asp.net-web-api odata projection

WebAPI支持OData,因此API使用者可以指定他需要的字段,而且效果很好。但是有没有办法在 WebAPI 中使用 DTO 对象和投影?

例如我有

public class WebSite
{
    public string Url {get;set;}
    public string Author {get;set;}
    public string Technology {get;set;}
    public DateTime CreatedAt {get;set;}
    // 20 more different properties
}

我也有 DTO 对象:

public class WebSiteDTO
{
    public string Url {get;set;}
    public string Author {get;set;}

    public bool IsDotNet {get;set;} // it should be set during mapping as webSite.Technology == ".Net";
    public bool IsTrendThing {get;set;} // should be set as  webSite.Technology == ".Net" and webSite.CreatedAt > new DateTime(2014,0,0);
}

以及一些支持 OData 的典型 WebAPI 端点:

[HttpGet]
[Route("Test")]
public IQueryable Test(ODataQueryOptions<WebSiteDTO> options)
{
    var efDbContext = new MyDBContext();
    var query = efDbContext.WebSites;
    var odataQuery = options.ApplyTo(query, settings);
    return odataQuery;
}

在这种情况下,将返回 WebSite 对象。但是如何返回 WebSiteDTO 对象并仍然具有 OData 支持?是否可以按属性而不是按类本身进行映射?就像如果通过 OData 请求 Url,那么我们将从 DB 加载 Url 并将其映射到 DTO 对象中的 Url 属性?当 DTO 中的属性必须由某些自定义逻辑(如 IsDotNet 示例中)设置时,我可能会遇到复杂的情况,或者它可能依赖于多个属性。

我想我可以编写一些自定义中间件,它将使用 OData 指定的字段执行原始查询,然后将其保存在字典中,然后执行类似的操作:

MyMapper.Map<WebSite, WebSiteDTO>().
    MapProperty(o, dict => o.Url = (string)dict["Url"]).
    MapProperty(o, dict => o.IsDotNet = (string)dict["Technology"] == ".Net").
    MapProperty(o, dict => o.IsTrendThing = (string)dict["Technology"] == ".Net" && (DateTime)dict["CreatedAt"] > new DateTime(2014,0,0));

但它看起来很难看,在这种情况下,我需要指定如果在 OData 请求中请求 IsTrendThing 属性,那么我还需要从 WebSite 对象加载 Technology 和 CreatedAt 字段,这使事情变得复杂。

有什么对我的情况有用的吗?也许有人可以帮助我找到正确的方向?

自动映射器 has projections mapping但它在我的场景中不起作用,因为它需要特定类型,并且在我的情况下,由于 OData 指定的字段,原始类型可能会有所不同。

another similar question但这是关于纯粹的 DTO,在我的例子中,我想支持 OData 中的“select\expand”运算符,因为我的对象可以有很多属性。如果没有请求,我不想从数据库加载它们。

最佳答案

您应该在应用 ODataQueryOptions 之前进行投影。 ODataQueryOptions 可以应用于 WebSiteDto 中存在的属性。

var date = new DateTime(2014,0,0);
// this will not load your data
var query = efDbContext.WebSites.Select(w => new WebSiteDto() 
{
    /* projection code */
    IsDotNet = w.Technology == ".Net",
    IsTrendThing = w.Technology == ".Net" && w.CreatedAt > date,
    Url = w.Url,
    Author = w.Author
});

// this will still not load your data but will be applied on your projected object
var odataQuery = options.ApplyTo(query, settings);

仅当 Controller 操作返回时才会执行完整查询(应用了选项)。您可以在 select 方法中执行任何与 LinqToEntities 配合使用的投影代码(避免调用 Equals 等方法,而使用 == 代替或调用任何构造函数)。您甚至不加载 Technology 或 CreatedAt,查询在 SQL Server 上执行,并且您只返回 bool 值。

如果客户端执行额外的 $select,它将被放置在该查询之上,因此不必要的数据将永远不会从 SQL Server 到达您的应用程序服务器(就像在 IQueryable 上应用另一个 .Select() 一样)

我不确定 AutoMapper 如何处理 IQueryable(是否强制评估查询),但 Select 方法不会评估您的查询,只会在其基础上进行构建,以便将其发送到 SQL Server。

关于asp.net - WebAPI 中的 DTO 和投影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27160574/

相关文章:

c# - EF-Code First : Unable to create a constant value of type '' . Only primitive types ('such as Int32, String, and Guid' ) are supported in this context

c# - 使用对象 ID 与将对象填充到其他类之间的权衡是什么?

asp.net-mvc - 将 ASP.NET Web API 应用程序配置为 ASP.NET MVC 应用程序下的虚拟目录

c# - ASP.NET 网站的自定义登录机制

asp.net - 在 vb.net 中比较对象与 Nothing

entity-framework - 使用 LINQ to Entities 时返回 IQueryable 与 ObjectQuery

azure - 通过 Web API 提供静态内容

c# - 每个资源有一个 Web API Controller ,还是有更多自定义操作的更少 Controller ?

c# - 防止在 Visual Studio 中加载符号

c# - AJAX 调用的空白输出