0

Imagine I have a hierarchy of objects I want to store in my Entity Framework 4.1 data store. I am creating them using Code First. They look like this:

BasicState
  has many -> StatefulEntities
  has a -> CreationDate

ReceivedState 
  has a -> EntityLocation

ApprovedState
  has a -> EntityLocation

OrderedState
  has a -> Order

DispatchedState 
  has a -> Order
  has a -> DispatchNumber

The BasicState is the parent for all the other states, everything else shares those two fields/relationships.

My initial thought, and the way I have implemented this, was to use Table Per Hierarchy inheritance because it seemed that I could just inherit from BasicState and have my other states partake of those common properties.

However that caused a problem because when different states share a field, the underlying data model doesn't, so for example, my database would have EntityLocation_Id from one of ReceivedState and ApprovedState and EntityLocation_Id1 from the other. That makes it hard to create test data as you don't know what fields belong to what models. I asked about this previously.

The solution I went for was along these lines:

public abstract class BaseState
{
     public DateTime CreatedDate { get; set; }

     public ICollection<StatefulEntity> StatefulEntities{get;set;}
}


public abstract class LocationState : BaseState
{
     public Location EntityLocation { get; set; }
}

public class ReceivedState : LocationState
{

}

public class ApprovedState : LocationState
{

}

This worked partially, however although it created a single EntityLocation_Id it still created two Order_Ids for OrderedState and DispatchedState, even though they behave in the same way and the implementations are indistinguishable.

With that barely half solved, I have now hit another problem which is leading me to ask this question:

Given that I the relationship between BasicState and StatefulEntities is many to many in both directions, I need to be able to find the Order details from the OrderedStates belonging to a StatefulEntity.

The problem is that although this is quite easy to represent in SQL, the inheritance involved means that Entity Framework doesn't know whether any given BasicState is an OrderedState so there appears to be no way to do a single trip to the database to include the OrderedState.Order.

So what I can't do is something like this:

statefulEntityRepository.Get().Where( x => x.Id == 1 ).Include( x => x.BasicStates ).Include( x.BasicStates.Order );

Obviously, that isn't code that would run but it shows what the problem is- the Order only belongs to certain subtypes of BasicState and so EF won't preload it, even if I am using some kind of cast.

If I try projection I could get something like this:

statefulEntityRepository.Get().Select( x=> new { 
             StatefulEntity = x,
             EntityLocation = ( from state in x.BaseStates where state is ReceivedState select state.EntityLocation )
          }

But having got that I need to find my way back from the anonymous type returned by the projection to my StatefulEntity for the rest of my code to be able to handle it, which feels like a significant longcut.

I have got as far as trying to define my base class and inheritors thus:

public abstract class BaseState
{
     public DateTime CreatedDate { get; set; }

     public ICollection<StatefulEntity> StatefulEntities{get;set;}

     public long EntitytLocationId { get; set; }
}


public abstract class LocationState : BaseState
{
     [Column(name="EntityLocationId")]
     public Location EntityLocation { get; set; }
}

And so on, but then I seem to be breaking the basic point of having TPH in the first place and I feel as though I might as well be rolling my own object model over a generic data class.

Edit: I think I have a little more understanding of this now- from trying to set the ForeignKey on the far end of that relationship ( i.e. the relationship between Location and LocationState ) it looks as though the inverse collection on the Order or the Location can't talk to a property that isn't on the base object, so I would have to use the version above. This creates a new problem in how I manage cases which have no EntityLocationId or OrderId- if I make those fields nullable it raises an error that reference fields cannot be null during database creation and by default there is no way to set an initial value on a field in Code First.

Can anyone recommend a better way of representing this type of model that will work with Entity Framework?

Community
  • 1
  • 1
glenatron
  • 11,018
  • 13
  • 64
  • 112
  • 1
    The two paragraphs beginning with "*Given that...*" and "*The problem is...*" are rather unclear to me. Of course does EF know if a `BasicState` is an `OrderedState` (there is a discriminator column in the table) and you can also load the related `Order` together with the state entities in a single roundtrip (`OfType` or in a projection (might be ugly, but possible) depending on details of your query). I also don't see why it matters in your context if the relationship is many-to-many or one-to-many. Can you try to clarify what exactly you want to query? – Slauma Apr 23 '13 at 17:33
  • Also for the two `Order_Id`s: Why don't you follow the same approach that solved your problem with the duplicate `EntityLocation_Id` and introduce a base class for `ReceivedState` and `ApprovedState`? – Slauma Apr 23 '13 at 17:36
  • @Slauma, that is my point- I have followed exactly the same approach, yet Entity Framework has created two Order_Ids but not two Entity_Location_Ids and I cannot see any difference between the two. – glenatron Apr 24 '13 at 09:01
  • Do you have an inverse collection property (or even two) in `Order` that refers to one of the state entities? – Slauma Apr 24 '13 at 18:31
  • @Slauma I have one each in Order and Location - see the edit at the end of my question for a bit more information about this. – glenatron Apr 25 '13 at 10:16

0 Answers0