问题描述
的WebAPI OData的支持,因此API的消费者可以指定自己需要的字段,它工作正常。但是,有没有使用DTO对象和突起的WebAPI的方式?
WebAPI supports OData, so API consumer can specify fields that he requires, and it works fine. But is there a way to use DTO objects and projections in WebAPI?
例如我有
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对象:
And i also have DTO object:
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端点:
And some typical WebAPI endpoint that supports OData:
[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;
}
在这种情况下,网页对象将被返回。但如何返回WebSiteDTO对象,仍然有OData的支持?是否有可能由财产而不是类本身做映射?如果通过的OData被请求的URL一样,那么我们会从数据库加载只是地址和地图它DTO对象url属性?我可能有复杂的情况下,当在DTO的属性,必须通过一些自定义逻辑,像IsDotNet样本中的设置,或者可能依赖于多个属性。
In that case WebSite object will be returned. But how to return WebSiteDTO object and still have OData support? Is it possible to do mapping by property and not by class itself? Like if Url was requested via OData, then we will load just Url from DB and map it to Url property in DTO object? I may have complicated cases when properties in DTO must be set by some custom logic like in IsDotNet sample, or it may depend on more than one property.
我想我可以写一些自定义的中间件将与OData的指定的字段执行原始查询,然后将其保存在字典中,然后做这样的事情:
I suppose i can write some custom middleware that will execute original query with fields specified by OData, and then save it in Dictionary and then do something like that:
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属性,那么我还需要从网站的对象,这使得事情复杂加载技术和CreatedAt领域。
But it looks ugly and in that case somehow i need to specify that if IsTrendThing property was requested in OData request, then I also need to load Technology and CreatedAt fields from WebSite object, that makes things complicated.
有什么能在我的情况是有用的?也许有人可以帮我正确的方向吗?
Is there something that can be usefull in my case? Maybe someone can help me with correct direction?
Automapper 的,但它不在我的情况下工作,因为它需要特定的类型,在我的情况下,原来的类型可以因为的OData指定的字段是不同的。
Automapper has projections mapping but it doesn't work in my scenario, as it requires specific type, and in my case original type can be different because of fields specified by OData.
有类似的问题,但它是关于纯DTO ,在我的情况下,我要支持选择\\扩大'从OData的运营商为我的对象可以有很多属性。我想如果不要求他们不从数据库加载它们。
There is another similar question but it was about pure DTO, and in my case i want to support 'select\expand' operators from OData as my objects can have a lot of properties. And i want to not load them from DB if they weren't requested.
推荐答案
您应该在应用之前ODataQueryOptions做投影。该ODataQueryOptions可以应用于存在于WebSiteDto属性
You should do the projection before applying the ODataQueryOptions. The ODataQueryOptions can be applied to properties which exist in 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);
完整的查询(应用选项)只会控制器动作返回时执行。你可以做任何预测code这与选择方法中LinqToEntities工作(避免调用像Equals和使用方法,而不是==或调用任何构造函数)。你甚至不加载技术或CreatedAt,查询被SQL服务器上执行,你回来只布尔值。
The full query (with options applied) will only be executed when the controller action returns. You can do any projection code which works with LinqToEntities inside the select method (Avoid calling methods like Equals and use == instead or calling any constructor). You don't even load Technology or CreatedAt, the query is executed on SQL server and you get back only boolean values.
如果客户端将执行额外的$选择它会被放在这个查询的顶部,所以不需要的数据永远不会到达从SQL Server应用程序服务器(就像在你的IQueryable申请另一个。选择())
If the client will perform additional $select it will be placed on top of this query, so the unnecessary data will never reach your application server from SQL Server (just like applying another .Select() on your IQueryable)
我不知道如何与IQueryable的AutoMapper交易(如果强制查询与否的评价),但选择方法不会评估您的查询,将只建立在其上,以便它可以被发送到SQL Server。
I am not sure how AutoMapper deals with IQueryable (if it forces evaluation of the query or not), but the Select method will not evaluate your query, will just build on it so that it can be sent to the SQL Server.
这篇关于DTO和的WebAPI预测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!