dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.73k stars 3.18k forks source link

Allow mapping to interfaces with polymorphic associations #757

Open ajcvickers opened 10 years ago

ajcvickers commented 10 years ago

Consider scenario described in https://github.com/aspnet/EntityFramework/issues/1489 when looking at this

glen-84 commented 9 years ago

This would really help in enabling the scenario described here.

Following on from that example, the forum may have a "Topic" entity with an associated ApplicationUser. Since the ApplicationUser is in the consuming web application (and can not be referenced by the module), the solution would be to use a common interface like IUser/IApplicationUser/IIdentityUser (or similar) to define the relationship.

public class Topic
{
    public int Id { get; set; }

    // By interface.
    public virtual IUser User { get; set; }

    // By class.
    public virtual AnotherAssociatedEntity Entity { get; set; }
}

I like the idea by @ravensorb in #1489. Perhaps the delegate could also receive some form of context that could provide access to attributes on the property, for example.

EF Core is a rewrite/large refactoring in many places, so now might be the best time to make something like this happen (or at least prepare for it).

Jabronironi commented 8 years ago

Currently, we are getting around this limitation by using a combination of expression trees (to allow predicate based searching against our interfaces) and AutoMapper (to convert from our IoC factoried domain objects that are based on our interfaces to our data objects). This is getting us by, but having this feature would greatly simplify our setup. Any ideas if this is on the roadmap?

ravensorb commented 6 years ago

Is there any possibility of this getting prioritized?

ajcvickers commented 6 years ago

@ravensorb We will consider this item when planning work for 2.2, but I can't promise anything given that there are many other high priority features with which it will be competing for resources.

935main commented 6 years ago

Does someone else saying 'Please' make a difference? I'm about to begin a migration to EFCore and this feature would suuuuure make that transition easier. Please?

ajcvickers commented 6 years ago

@935main It helps, but no more than an up-vote in the issue, which currently only has three.

However, for you and others reading it would be useful to know how much complexity in the mapping is required. For example, consider a simple case like this:

public interface IComment
{
    int Id { get; set; }
}

public interface IBlog
{
    int Id { get; set; }
}

public interface IPost
{
    int Id { get; set; }
}

public class Comment : IComment
{
    public int Id { get; set; }
}

public class Blog : IBlog
{
    public int Id { get; set; }

    public ICollection<IComment> Comments { get; set; }
}

public class Post : IPost
{
    public int Id { get; set; }

    public ICollection<IComment> Comments { get; set; }
}

In this code there are three interfaces, and in each case the interface uniquely identifies the entity type. The relationships between Comment and Post would use one FK, and the relationship between Comment and Blog would use a a different FK. So there is really no fundamental difference between the model that uses interfaces and the one that uses classes, other than the use of interfaces.

However, there is some feeling on the team that there is little value in doing this, since once interfaces are supported, they are not useful unless they:

So, for example:

public interface IComment
{
    int Id { get; set; }
}

public interface IBlog
{
    int Id { get; set; }
}

public interface IPost
{
    int Id { get; set; }
}

public class PostComment : IComment
{
    public int Id { get; set; }
}

public class BlogComment : IComment
{
    public int Id { get; set; }
}

public class Blog : IBlog
{
    public int Id { get; set; }

    public ICollection<IComment> Comments { get; set; }
}

public class Post : IPost
{
    public int Id { get; set; }

    public ICollection<IComment> Comments { get; set; }
}

In this case, IComments related to a Blog would be of type BlogComment, while IComments related toPostwould be of typePostComment`. Is this needed?

In a nutshell, it would be very useful to know how you use interfaces in your model so we know what how much of the scope of this feature needs implementing to be useful.

jhandel commented 5 years ago

Using the example above, I notice you are using shadow properties for the FK back to the comment.. At least for my example (#14260 ) I specifically need to be able to query on Comments and avoid multiple nullable FKs (ie a single parentID field). I'll copy my example use case here since my case was closed as a duplicate)

I have bolded the relevant part, a pair of Indexed fields (parentId and parentDiscriminator) that make up the relationship instead of using a FK with a constraint.

Contact:

Id ParentId ParentDiscriminator [the rest of the fields that make up a contact]

Customer: Id Fields that make up a customer ICollection Contacts

Supplier: Id Fields that make up a supplier ICollection Contacts

Partner: Id Fields that make up a partner ICollection Contacts

Warehouse: Id Fields that make up a partner ICollection Contacts