8

I have an ODataController in an Asp.Net Web Api application that allows OData queries. I'm only allowing reads, not updates. Instead of exposing the data model directly, I've created a set of DTOs. The property names on the DTOs do not necessarily match the properties on the EF model. This causes a problem when I try to use the OData query against the EF model. I've looked at other posts on StackOverflow around this subject but none of them seemed to resolve this issue.

Here is what I have right now:

public IQueryable<Customer> GetCustomer(ODataQueryOptions<Customer> query)
{
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Customer>("Customers");
        builder.EntitySet<RECORD>("Records");
        builder.Namespace = "MyDataService.Models";

        var opts = new ODataQueryOptions<RECORD>(new ODataQueryContext(builder.GetEdmModel(), typeof(RECORD)), this.ActionContext.Request);
        var records = (IQueryable<RECORD>)opts.ApplyTo(db.RECORDS);
        return ConvertToCustomerList(records);
}

This works until I reference specific fields in a select or filter. As soon as I reference a field in my OData query I get an ODataException like - Could not find a property named 'LastName' on type 'MyDataService.Models.RECORD. This is because the EF properties have a different naming convention. In this case it should be using "LAST_NAME".

It seems like I need to parse the query and then replace the field references with the correct names. I found the ODataUriParser which seems like it could help with this but it's not as clean as I was hoping.

Can anyone provide me some pointers in resolving this issue? Is there a better approach?

BenR
  • 11,296
  • 3
  • 28
  • 47

1 Answers1

7

The WebApi OData new feature Model Aliasing may resolve your problem. You don't have to have the same names between a Edm Model and a aDTO. for example, there is a property name OrderDto.Total, but in Edm Model it becomes Order.Check

        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.ModelAliasingEnabled = true;

        EntitySetConfiguration<CustomerDto> customers = builder.EntitySet<CustomerDto>("Customers");
        EntitySetConfiguration<OrderDto> orders = builder.EntitySet<OrderDto>("Orders");
        orders.EntityType.Name = "Order";
        orders.EntityType.Property(p => p.Total).Name = "Check";
        return builder.GetEdmModel();

Please reference the ODataModelAliasingSample in https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/

Tan Jinfu
  • 3,327
  • 1
  • 19
  • 20
  • Thanks. This was extremely helpful. This is only available in pre-release right now but it looks promising. Also, you can use DataContract and DataMember attributes on the model to define the alias. – BenR Apr 10 '14 at 19:51
  • 2
    Also, I think the ModelAliasingEnabled property has been deprecated with the latest version but aliasing still works. – BenR Apr 10 '14 at 19:52
  • Can this aliasing be used also when implementing OData queries inside an ApiController? –  Jun 11 '16 at 17:26
  • I want to have both `Customer` and `CustomerDto` exposed via OData, in my controller, when querying `GetAll`, I want the DTOs to be returned, whereas when querying an individual object, the `Customer` one. – Shimmy Weitzhandler Oct 02 '19 at 04:40
  • what does this line do? orders.EntityType.Name = "Order"; you already set it above with EntitySetConfiguration orders = builder.EntitySet("Orders"); ? just curious, thanks –  Jun 30 '20 at 21:31
  • hi @Tan, so how do I apply it? I setup the mapping in startup, see this question https://stackoverflow.com/questions/62650392/net-core-using-odata-in-onion-architecture-mapping-query-parameters-from-dto-t –  Jul 01 '20 at 03:18
  • @ShimmyWeitzhandler your comment should really be posted as a question, you know this ;) In your comment here, you post a link to your question to attract attention to it. Using the Alias feature is a permanent replacement and won't help your situation. Use a custom function instead. – Chris Schaller Oct 09 '22 at 20:57