Reputation: 637
I'm having a complex situation where I either get a problem with the Code-First portion of my project, or a problem with the OData portion of my project...
I have an abstract class Person
of which I derive two classes User
and Driver
:
[DataContract]
[KnownType(typeof(User))]
[KnownType(typeof(Driver))]
public abstract class Person
{
[Key, DataMember]
public int ID { get; set; }
[DataMember]
public int RelationID { get; set; }
}
public class User : Person { }
public class Driver : Person { } // shortened for sake of readability
Now I have a model Relation
, which can contain both users and/or drivers.
[DataContract]
public class Relation
{
[Key, DataMember]
public int ID { get; set; }
[DataMember]
public virtual ICollection<User> Users { get; set; }
[DataMember]
public virtual ICollection<Driver> Drivers { get; set; }
}
The problem is, when I let Code-First generate migration code, I can see that it wants to add two columns Relation_ID
and Relation_ID1
in order to cope with the mapping of Users
and Drivers
. I applied the [InverseProperty("ID")]
attribute to both navigation properties in order to try to solve this (as described here: Code First DataAnnotations). But still, the problem persists. So, this is not working.
Now, you would say: why not make it an ICollection<Person> People
property, and use this.db.People.OfType<User>()
in order to cast the objects to the right model and retrieve it that way. This is perfectly acceptable, but now it turns into an OData problem.
I want to be able to call public IQueryable<User> GetUsers([FromODataUri] int key)
from the Relations
controller in order to use /odata/Relations(16)/Users
to get all users from a relation. If there is no Users
property present in the Relation
model, the OData parser rejects any such call because it can not resolve to the correct method.
So I'm stuck between the designs of both worlds. I'm guessing that in order to correctly solve this problem, the focus should lie on the Code-First part, not on the OData part. Any ideas are appreciated, thanks in advance!
Upvotes: 1
Views: 491
Reputation: 6793
You can add the ICollection People property as well and exclude Users and Drivers properties from the Data model. For example,
[DataContract]
public class Relation
{
[Key, DataMember]
public int ID { get; set; }
[DataMember]
public virtual ICollection<Person> People { get; set; }
[DataMember]
public virtual ICollection<User> Users
{
get
{
return People.OfType<User>();
}
}
[DataMember]
public virtual ICollection<Driver> Drivers
{
get
{
return People.OfType<Driver>();
}
}
}
In your DbModelBuilder code exclude the Users and Drivers properties by doing,
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var relation = modelBuilder.Entity<Relation>();
relation.Ignore(p => p.Users);
relation.Ignore(p => p.Drivers);
}
In your ODataModelBuilder, exclude the People property if you want to.
Upvotes: 1