In my case, I have two classes:
class BaseClass { public int ID {get; set;} }class DerivedClass : BaseClass { public string A { get; set; }
}
And in the modelBuilder configuration (which may not be necessary here but in my case, the builder would treat some other property as the primary key instead of this one and caused other cryptic errors):
builder.EntitySet<DerivedClass>("DerivedClasses").EntityType.HasKey(c => c.ID);
Now in my DerivesClassesController ODataController, if I try to access the derived class like so:
http://someaddress/api/DerivedClasses
I get this OData V4 formatted error (in french) :
{ "error":{ "code":"","message":"An error has occurred.","innererror":{ "message":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata.metadata=minimal'.","type":"System.InvalidOperationException","stacktrace":"","internalexception":{ "message":"Une valeur primitive a \u00e9t\u00e9 sp\u00e9cifi\u00e9e alors qu'une valeur de type non primitif '' \u00e9tait attendue.","type":"Microsoft.OData.Core.ODataException","stacktrace":" \u00e0 Microsoft.OData.Core.ValidationUtils.ValidateIsExpectedPrimitiveType(Object value, IEdmPrimitiveTypeReference valuePrimitiveTypeReference, IEdmTypeReference expectedTypeReference, Boolean bypassValidation)\r\n \u00e0 .............................(Here I used to suggest defining a "new int ID" in the DerivedClass which worked but was kind of dirty and defeated the whole base class purpose. Here's a better way:)
The trick is to map the base class' ID property:
builder.EntityType<BaseClass>().HasKey(c => c.ID);
And do not remap the key on each derived class or you'll end up with a cryptic "The sequence contains more than one element" error.
The problem with this, however, is that the builder will try to map every entity that is inherited from the base class which you might or might not want. As of this writing, there is no way of preventing this automatic discovery.
So another trick is to explicitly ignore all derived types and only after that, remap wanted entities. Doing this by hand would be problematic wouldn't it. So here's a nice reflective loop that'll do the ignore part in a couple of lines. Place this just after the base class key definition so you end up with:
builder.EntitySet<DerivedClass>("DerivedClasses").EntityType.HasKey(c => c.ID);
var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => t.IsSubclassOf(typeof(AbstractModel))); foreach (var type in types) builder.Ignore(types.ToArray());
Then proceed on the derived types mappings as usual.
2 comments:
THANK YOU SO MUCH I spent hours on this, such a cryptic error for a major framework
Glad I could help :)
Post a Comment