EF4, POCO, inheritance, interface mapping


  • Please, consider the following scenario:
    - an interface IFoo defining some accessors and 2 concrete classes "A : IFoo" (A implements IFoo interface) and "B : IFoo" implementing them
    - a database with following tables Foo (storing IFoo exposed properties) , Foo_A (A specific properties) and Foo_B (B specific properties)

    How may I map those entities?

    By introducing a "Foo : IFoo" class and refactoring A and B as derived classes of Foo, no problem of course but in my POCO model I can't adopt such a hierarchy.

    My experimentations failed at runtime with mapper trying to set "IFoo properties" on base class of A which is here, the Object class. How may I instruct the mapper to use the IFoo interface and not class inheritance?

    If interface mapping is not supported in current EF4 release, may we expect this feature for RTM?

    Monday, October 05, 2009 1:36 PM

All replies

  • The entity data model itself does not contain the notion of interfaces, and this situation will not change at RTM, as far as I am aware. There are also some fundamental mapping issues here: You havea a common table Foo, but if A and B don't share the same set of keys, then how is data to be correctly partitioned? E.g. since A and B have no shared keys, what is to prevent an instance of A with key [1] and an instance of B with key [1] from overwriting each other's entries in table Foo?

    This posting is provided "AS IS" with no warranties, and confers no rights.
    Monday, October 05, 2009 4:59 PM
  • Thx for the quick response.

    A and B can share a common key exposed by IFoo interface and stored as primary identity key in Foo table :

    // POCO:
    public interface IFoo { int Id { get; set; } ... } // with Id value auto-generated during insert operation
    public class A : IFoo { ... }
    public class B : IFoo { ... }


    -- DB standard inheritance-by-type:
    TABLE Foo ( [Id] int IDENTITY(1, 1) NOT NULL, ... )
    TABLE Foo_A ( [Id] int NOT NULL, ... )
    TABLE Foo_B ( [Id] int NOT NULL, ... ) 

    or maybe I'm missing something in your pointed issue...
    Tuesday, October 06, 2009 12:30 AM
  • Mat

    I have done a lot of work on the issue of associations that are defined by Interfaces rather than by Classes.  As Noam has said there is no support for this in the EF4 Beta and it doesn't sound as if that will change for the release.  So you need to do a workaround. 

    While you might be able to get it to work using inheritance, along the lines you proposed, this is not a satisfactory answer since it effectively means that A and B are then sub-classes of IFoo  -  in which case, why use an interface when you could just make Foo and abstract superclass.  I assume that your real need is the same as mine i.e. that IFoo might be implemented by an arbitrary number of classes, and you cannot assume that they have a single common superclass. Indeed in my own designs, I want my classes to be able to implement many different such 'role' interfaces, arbitrarily.

    The answer is that a property of type IFoo cannot be treated as an association (Navigation property) as far as EF is concerned.  You need to implement it as one or more scalar fields.  My preferred option is as two scalar fields.  One holds the actiual implementation type as a fully-qualified pathname e.g younamespace.A  or yournamespace.B and the other holds an interface value representing the particular instance of that type (typically an integer key).  In database terms the first points you at the correct table, the second points you at the row in that table.

    A more sophisticated approach is to combine these two pieces of information into a single composite string value that, effectively, represents an Object ID (this approach can also cope with the types having compositive keys, whereas the former assumes a single key).

    These two (or one) scalar fields will be hidden from both the user.  Then you need a separate property that is not persisted (so unknown to EF) of type IFoo, and some code in the getter to retrieve the correct instance based on the scalar fields.  Similarly, some code in the setter to translate back.

    This all sounds like a bit of a nightmare, but you can write some helper objects that do all the work for you, and then just use some code snippets to insert an 'Interface Association' (as I term it) into your class.  Complex types are also useful here.

    I recommend that you read my blog entry about this: which includes a pointer to a video demo I recorded.  The video only shows how to use the InterfaceAssociation helper classes that I created to use within Naked Entities, not the code within them, but I'd be happy to share the underlying code if you want to use the same approach within raw EF.  There's one magic line in it that creates a templated method dynamically using reflection -  so that you can get an IQueryable<T> where T is dynamically determined from the pathname (as mentioned above).  The rest is straightforward.

    One issue you need to be aware of is that navigating the association defined by an interface is necessarily a separate database trip.  But this is not necessarily a bad thing if you want alzy loading anyway.  And one up side is that the same technique then works for associations that span across different databases  -  another limitation of EF4 also.

    Let me know if you want more detail.  This is a major issue.  As the EF team knows, I have been bellyaching about it for more than a year now.  We really do need to see native support for Interfaces in EF.


    Wednesday, October 07, 2009 12:54 PM
  • Thank you for this very interesting post Richard!
    You are absolutely right with my real need and your workaround, articles and video are precious to me!
    You seem to be an authority in interface support fight, if you setup a petition, I'd sign it immediately as those 400+ visitors of current thread I think :)
    PS: thx for your offer to share your code, I think I know how to implement this. In case of failure, I'd invoke your help!
    Wednesday, October 14, 2009 4:33 PM