Reputation:
I want to add OData syntax for querying my application's data. I don't want to fully implement ODataController, but have ApiController and implement one GET action that will support OData queries, as described here: Supporting OData Query Options in ASP.NET Web API 2
Example of what I want to have:
public class LetterEntity
{
public int Id {get; set;}
public string Title {get; set;}
public string Content {get; set;}
public string Source {get; set;}
public DateTime SendingTime {get; set;}
public string AnotherWierdString {get; set;
...
}
public class LetterDTO
{
public int Id {get; set;}
public string Title {get; set;}
public string LetterContent {get; set;}
public string Source {get; set;}
public DateTime SendingTime {get; set;}
}
public class LetterInsideFolderDTO
{
public string Title {get; set;}
public string Source {get; set;}
}
public class LettersController : ApiController
{
// Is there a way to do something like the following?:
[HttpGet]
[Route("api/letters")]
[EnableQuery]
public IQueryable<LetterInsideFolderDTO> Get(ODataQueryOptions<LetterDTO> query)
{
IQueryable<Letter> letters = db.Letters;
var queryOnEntites = // Convert the query to work on the entities somehow? - This is where I need help!!
var afterQuery = query.ApplyTo(letters)
IQueryable<LetterInsideFolderDTO> dtos = afterQuery.ProjectTo<LetterInsideFolderDTO>(afterQuery)
return dtos;
}
}
Because of the fact that at the moment I take Entity model directly in the clients query, there is a strong coupling between clients and server.
For example if i want to query and get all the letters that has "abc" inside the Content
field, I need to route to the following:
api/letters/?$filter=contains(Content,'abc')
If tomorrow I decide to change that property from "Content" to "LetterContent" all clients code will be broken.
How can I surpass it?
Upvotes: 4
Views: 598
Reputation: 2561
This will work for Entity Framework (not sure for Nhibrenate but probably will) and will do a real SQL query not in memory filtering.
var queryOnEntites = db.Letters.Select(l=>new LetterDTO{Id =l.Id ... });
var afterQuery = query.ApplyTo(queryOnEntites);
But you shouldn't use DTOs with odata if you want to make some properties private to the API use builder.EntitySet<T1>("TEndpoint").EntityType.Ignore(o => o.SomeProp);
Now if you don't want the whole LetterEntity sent to client you have the $select=Id,... option for this purpose.
If you don't setup
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
you don't have an Odata endpoint and $metadata won't be available.
I haven't tested it but this way I don't think the client libraries(.net,java,js...) would work and you will have to make raw ajax requests to get the data.
ODataQueryOptions<> and translate it to ODataQueryOptions<>
You can't do this. ProjectTo(AutoMapper)
function dose the same thing as .Select(l=>new LetterDTO{Id =l.Id ... });
but you will have problems with this. This all is dependent on your back end since IQueryable is not the same as IEnumerable.
http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/
This all depends on what level of LINQ your back end provides (NHibrenate tends to be worse then EF, and if you are using Mongo, Elastic, Cassandra... who knows what might go wrong when you use AutoMapper)
If tomorrow I decide to change that property from "Content" to "LetterContent" all clients code will be broken.
You can setup Name property on an Entity set with oData. Remember Odata is data access level not BL. You should treat it the same way as changing SQL column name in the database.
Upvotes: 1
Reputation: 930
I have not tried it. This and this can help you to map the query url to actual controller by using router map and controller selector where you can map LetterDto to LetterEntity
Upvotes: 1
Reputation: 82
Instead of exposing your entity models directly, create ViewModels corresponding to each entity model. They are nothing but simple classes having same or only required properties which we want to be exposed to the outer world. Having implemented this way, your entity model properties may change but ViewModels can remain unchanged. You are also addressing the security aspect of not exposing every property of your entity to end clients.
Mapping between entity model and ViewModel has to be done by yourself or by an object-object mapper like AutoMapper.
Upvotes: 0