views:

95

answers:

2

I am looking into migrate a large project to Entity Framework 4.0 but am not sure if it can handle my inheritance scenario.

I have several projects that inherit from an object in the “main” project. Here is a sample base class:

 namespace People
{
    public class Person
    {
        public int age { get; set; }
        public String firstName { get; set; }
        public String lastName { get; set; }

    }
}

and one of the sub-classes:

namespace People.LawEnforcement
{
    public class PoliceOfficer : People.Person
    {
        public string badgeNumber { get; set; }
        public string precinct { get; set; }
    }
}

And this is what the project layout looks like:

People - People.Education - People.LawEnforcement

Some customers of the application will use classes from the People.LawEnforcement and other users will use People.Education and some will use both. I only ship the assembles that the users will need. So the Assembles act somewhat like plug-ins in that they add features to the core app.

Is there anyway in Entity Framework to support this scenario?

Based on this SO question I'm think something like this might work:

ctx.MetadataWorkspace.LoadFromAssembly(typeof(PoliceOfficer).Assembly);

But even if that works then it seams as if my EDMX file will need to know about all the projects. I would rather have each project contain the metadata for the classes in that project but I'm not sure if that is possible.

If this isn't possible with entity framework is there another solution (NHibernate, Active Record, etc.) that would work?

+3  A: 

Yes this is possible, using the LoadFromAssembly(..) method you've already found.

... but it will only work if you have an specialized model (i.e. EDMX) for each distinct type of client application.

This is because EF (and most other ORMs) require a class for each entity in the model, so if some clients don't know about some classes, you will need a model without the corresponding entities -- i.e. a customized EDMX for each scenario.

To make it easier to create a new model for each client application, if I was you I'd use Code-Only following the best practices laid out on my blog, to make it easy to grab only the fragments of the model you need actually need.

Hope this helps

Alex

Alex James
It looks like Code-Only is the way to go. Can Code-Only be combined with an EDMX model? for example have an EDMX in my main project (Person in my example) then do code-only in the other projects?
TonyB
Well the idea behind Code-Only is that there is NO EDMX. Of course under the hood the same information is generated, which you can convert to EDMX format with the WriteEDMX() method hanging off the ContextBuilder. So my recommendation is use Code-Only everywhere, including your main project, and use WriteEDMX only if you need to visualize the model.
Alex James
+2  A: 

Alex is correct (+1), but I'd strongly urge you to reconsider your model. In the real world, a police officer is not a subtype of a person. Rather, it's an attribute of that person's employment. I think programmers frequently tend to over-emphasize inheritance at the expense of composition in object oriented design, but it's especially problematic in O/R mapping. Remember that an object instance can only ever have one type. When that object is stored in the database, the instance can only have that type for as long as it exists, across multiple application sessions. What if a person had two jobs, as a police officer and a teacher? Perhaps that scenario is unlikely, but the general problem is more common than you might expect.

More relevant to your question, I think you can solve your actual problem at hand by making your mapped entity model more generic, and your application-specific data projections on the entities rather than entities themselves. Consider entities like:

public class JobType
{
    public Guid Id { get; set; }
    // ...
}

public class Job 
{
    public JobType JobType { get; set; }
    public string EmployeeNumber { get; set; }
}

public class Person
{
    public EntityCollection<Job> Jobs { get; set; }
}

Now your law enforcement app can do:

var po = from p in Context.People
         let poJob = p.Jobs.Where(j => j.JobType == JobType.PoliceOfficerId).FirstOrDefault()
         where poJob != null
         select new PoliceOfficer
         {
             Id = p.Id,
             BadgeNumber = poJob.EmployeeNumber
         };

Where PoliceOfficer is just a POCO, not a mapped entity of any kind.

And with that you've achieved your goal of having a common data model, but having the "job type specific" elements in separate projects.

Craig Stuntz
I'm looking into changing the person class into an interface so it would be PoliceOfficer : IPerson which it probably better than inheritance but in my real world app the sub-objects all have some shared behaviors, which normally works better with inheritance.I'll look into the data projections but most of my sub-classes have no overlapping properties. I'm also not sure how that work work with my existing table per subclass database.
TonyB