6

I made a partial class file to add new properties to my Entity-Framework generated model.

I am using WebAPI + OData, and the $metadata doesn't list my new/custom properties, and so the JSON it returns doesn't include my new/custom properties.

For example, let's say my Entity is "Person"

"Person" has one Database property; NumSpouses; an int which is returned in $metadata like this: <Property Name="NumSpouses" Type="Edm.Int32"/>

That's great, but I added a property like this to a separate file, with a partial class:

public partial class Person {
  ...
  public string MarriedStatus { 
     get { return this.NumSpouses==0 ? "Single" : "Married"; }
  }
  ... 
}

How can I get this Property available in my OData responses?

<Property Name="MarriedStatus" Type="Edm.String"/>

Currently, if I asked for MarriedStatus in $expand (as if it were a NavigationProperty.... which it's not [I thought I'd try $expand anyway as if it magically provided custom properties]), I'd get a message like this:

{
  "odata.error":{
    "code":"","message":{
      "lang":"en-US","value":"The query specified in the URI is not valid. Could not find a property named 'MarriedStatus' on type 'fakeDataModels.Person'."
},"innererror":{
  "message":"Could not find a property named 'MarriedStatus' on type 'fakeDataModels.Person'.","type":"Microsoft.Data.OData.ODataException","stacktrace":"   at ..."
    }
  }
}
8
  • 1
    Is your partial class in the same namespace as the EF generated model? Commented Apr 1, 2016 at 3:06
  • Yes it is. The namespace is consistent, let's call it "PersonDataModels", in both files, and both files are in the same project. I know Controller.Json JSON serialization is not the same as OData serialization, but I will point out that JSON will serialize the MarriedStatus property unless I explicitly say not to (ScriptIgnoreAttribute). Maybe this calls for the equivalent of a "ViewModel", I should add to my client-side (Javascript) code, which will just provide me the convenience function getMarriedStatus(). Commented Apr 1, 2016 at 16:05
  • 1
    Partial classes work as expected in a toy project that I created. Maybe the problem lies in your OData configuration. Are you using ODataConventionModelBuilder and simply registering entity sets? Or something more sophisticated? Commented Apr 1, 2016 at 16:43
  • I am showing you fake code, not my real code (to hide my business goal/logic). But I think I am simply registering entity sets; I have the equivalent of this: ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Person>("Person"); config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel()); Commented Apr 1, 2016 at 17:07
  • 1
    I wrote the toy as a single file. The configuration is designed for running under OWIN, but you should still be able to grok what I've done. Note that I'm calling config.MapODataServiceRoute rather than config.Routes.MapODataRoute. Is your project OData V3 or V4? Commented Apr 1, 2016 at 17:25

2 Answers 2

6

MarriedStatus is a calculated/readonly property. The ASP.NET implementation of OData does not currently support such properties. As a workaround, add a setter that throws NotImplementedException.

    public string MarriedStatus {
        get { return this.NumSpouses > 0 ? "Married" : "Single"; }
        set { throw new NotImplementedException(); }
    }

Optionally, if you are using OData V4, you can annotate MarriedStatus to specify that it is calculated. See Yi Ding's answer to OData read-only property. But the annotation is advisory only; it does not prevent clients from attempting to set a calculated property (e.g., in a POST request).

Sign up to request clarification or add additional context in comments.

1 Comment

There is, however, still an issue when trying to serialize the property as LINQ to Entities chokes if there is a setter and the property doesn't exist in the database. See this question
3

In addition to the answer of lencharest. You should use the Ignore() function of the Entity Framework fluent API instead of the [NotMapped] attribute. Because OData looks for this attribute to ignore properties for serialization. If you use the fluent API you will not have this problem.

dbModelBuilder.Entity<TEntity>()
    .Ignore(i => i.ComputedProperty);

2 Comments

But I want to include more columns... I don't want to Ignore columns. I don't see how Ignore/NotMapped is relevant
Do note that .Ignore() simply tells Entity Framework to ignore this property as if it were not in the database. So it will ignore this for inserts, selects, updates, while still allowing you to have it on your model. This way you can add whatever you want. I was also providing an answer to @MylesRip.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.