How to utilize CodeFirst's interface properties

entity-framework

Question

I have the following entities:

public interface IMyEntity
{
    [Key]
    int Id { get; set; }
    IMyDetail MyDetail { get; set; }
    ICollection<IMyDetail> CollectionOfReferences { get; set; }
}

public interface IMyDetail
{
    [Key]
    int Id { get; set; }
    int IntValue { get; set; }
}

public class MyEntity : IMyEntity
{
    [Key]
    public virtual int Id { get; set; }
    public virtual IMyDetail MyDetail { get; set; }
    public virtual ICollection<IMyDetail> CollectionOfReferences { get; set; }
}

public class MyDetail : IMyDetail
{
    [Key]
    public virtual int Id { get; set; }
    public virtual int IntValue { get; set; }
}

I want to use EF CodeFirst to access the database and to create database schema. But CodeFirst doesn't allow to use interface types for relations between entities. Therefore it doesn't create relation between MyEntity and MyDetail. I can't change interfaces therefore I can't change the type of property to MyDetail instead of IMyDetail. But I know that the client of this model will use only one implementation of each interface.

I've found a workaround for properties of type IMyDetail. I can create a property of type MyDetail and explicitly implement property of interface:

    private MyDetail _myDetail;

    public virtual MyDetail MyDetail
    {
        get
        {
            return this._myDetail;
        }
        set
        {
            this._myDetail = value;
        }
    }

    IMyDetail IMyEntity.MyDetail
    {
        get
        {
            return this._myDetail;
        }
        set
        {
            this._myDetail = (MyDetail)value;
        }
    }

It works fine. But this solution doesn't work with ICollection<IMyDetail> because I can't cast it to ICollection<MyDetail>.

Are there any solutions for this?

1
32
3/21/2012 1:12:09 PM

Accepted Answer

An imperfect solution is to just merge these interfaces you want to persist into base classes and break down the underlying objects with subclasses. EF does support this, and if you go with Table Per Hierarchy (the default), you can sort all of the underlying subclassed objects by a shared property using a regular LINQ query from EF instead of having to get crafty and do things like write raw SQL or get multiple lists into memory and sort the union without the help of the DB, like you would with Cel's solution of interfaces and adapters.

You could also take the child/parent types of interfaces as generics, so that when the implementer uses concrete classes in the Db they can mostly use your interfaces, but tell EF to use concrete classes:

public interface IParent<out TChild>
    where TChild : IChild
{
    ICollection<TChild> Children { get; set; }

Someone could create their Db classes like:

public class Parent : IParent<Child>
. . .

But still use them like:

IParent<IChild> parents = db.Parents.Include(p => p.Children).ToArray();

Because the generic is marked out, the generic is covariant and therefore can take anything that meets the generic's restrictions, including the above cast up the type tree to the IChild interface.

That said, if you really want to persist interfaces, the right answer is probably to use NHibernate: How to map an interface in nhibernate?

And some coders recommend you keep interfaces on entities in any ORM limited to a few shared properties, or risk misuse: Programming to interfaces while mapping with Fluent NHibernate

15
5/23/2017 12:03:05 PM

Popular Answer

A workaround is to create a special implementation for each interface you want to use with Entity Framework, utilizing the adapter pattern:

Wrapper per Interface

// Entity Framework will recognize this because it is a concrete type
public class SecondLevelDomainRep: ISecondLevelDomain
{
    private readonly ISecondLevelDomain _adaptee;

    // For persisting into database
    public SecondLevelDomainRep(ISecondLevelDomain adaptee)
    {
        _adaptee = adaptee;
    }

    // For retrieving data out of database
    public SecondLevelDomainRep()
    {
        // Mapping to desired implementation
        _adaptee = new SecondLevelDomain();
    }

    public ISecondLevelDomain Adaptee
    {
        get { return _adaptee; }
    }

    public string Id
    {
        get { return _adaptee.Id; }
        set { _adaptee.Id = value; }
    }

    // ... whatever other members the interface defines
}

Saving and Loading Example

    // Repositor is your DbContext

    public void SubmitDomain(ISecondLevelDomain secondLevelDomain)
    {
         Repositor.SecondLevelDomainReps.Add(new SecondLevelDomainRep(secondLevelDomain));
         Repositor.SaveChanges();
    }

    public IList<ISecondLevelDomain> RetrieveDomains()
    {
         return Repositor.SecondLevelDomainReps.Select(i => i.Adaptee).ToList();
    }

Using Navigational Properties / Foreign Keys / Parent Child Mappings

For more complex interfaces/classes, you may get InvalidOperationException - see Conflicting changes with code first foreign key in Entity Framework for an implementation that works with such object hierarchies



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow