Making the Entity Framework Fit Your Domain – Part 1

Writing

I’m assuming that like myself, many of you out there work for companies that base much of their IT infrastructure (or at least software development tools) around Microsoft products. So, when a new tool like the Entity Framework comes out, even if you are not a fan, you still need to have a solid knowledge of it because you are going to have to use it at some point. At this point most of my ORM experiences have been with NHibernate, but I still feel the need to explore the Entity Framework to see if I can make it palatable for me to use. I say “palatable” because of the fact that the Entity Framework is designed almost entirely around database first design, which is not the way that I like to design my applications.

My goal with this post is not to trash talk the entity framework, but instead to take it as far as I can toward a usable solution that I would be okay with putting into a production application. This post is going to be written as I explore, so please let me know if you see anything that is wrong or missing.

Let’s first talk about the domain that we are getting ready to look at. It is going to be a very simple domain, because otherwise it would just overwhelm the blog post by introducing too much complexity. I do want to have enough entities though so that you can see where each technology differs. What we are going to do is start off with a scenario that everyone is familiar with… a user with groups and roles. The user will also have a list of addresses associated with it.

Database Schema

We have 4 main tables along with two join tables. I would explain this schema to you, but if you don’t get the schema then this article might be confusing anyway so I’m not going to waste everybody else’s time with it. Basically we have already started designing this application in a way that would bother most people who are using DDD. Normally I wouldn’t start with the database, but since the Entity Framework essentially forces you into starting with the database first, we are going to take this approach. The reason that I say that the Entity Framework forces you into database first design is because the primary method of generating an EF model is to generate it off the database. And then later on as you make changes, you can then update your model to reflect those changes.

At this point in the process the Entity Framework allows us to get up and running very quickly. We simply add a new Entity Data Model:

image

Then we get a wizard that lets us connect the model to our database and generate our entities. So, in just a few seconds we are looking at this:

Entity Data Model

Kinda cool actually. It knows about our join tables and generates many to many relationships automatically. It doesn’t however know about join tables with payloads, but then again there is ambiguity about how we might want that sort of data modeled in our app. So now we have our entities in our Entity Data Model, but where are we really at this point? Well, we are actually already at the point where we can create new entities and save them off to the database.

var user = new User();
user.Username = "TestUser";
user.EmailAddress = "test@test.com";

var address1 = new Address();
address1.Street = "111 Test Street";
address1.City = "Test City";
address1.State = "Virginia";
address1.PostalCode = "22055";

var address2 = new Address();
address2.Street = "222 Test Street";
address2.City = "Test City";
address2.State = "Virginia";
address2.PostalCode = "23000";

user.Addresses.Add(address1);
user.Addresses.Add(address2);

var entities = new TestEFAppEntities();
entities.AddToUserSet(user);
entities.SaveChanges(true);

That was painless, wasn’t it? But where did the “User” and “Address” classes come from? I don’t remember creating any classes… But that is because we didn’t. The Entity framework spit out all of these classes into a file that is hidden under our Entity Model called “Domain.Designer.cs”. Here is the user class that was generated for the model (I removed all the comments so that it would only be kinda huge) 😉

[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="TestEFAppModel", Name="User")]
[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
[global::System.Serializable()]
public partial class User : global::System.Data.Objects.DataClasses.EntityObject
{
    public static User CreateUser(int id, string username, string emailAddress)
    {
        User user = new User();
        user.Id = id;
        user.Username = username;
        user.EmailAddress = emailAddress;
        return user;
    }

    [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
    [global::System.Runtime.Serialization.DataMemberAttribute()]
    public int Id
    {
        get
        {
            return this._Id;
        }
        set
        {
            this.OnIdChanging(value);
            this.ReportPropertyChanging("Id");
            this._Id = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
            this.ReportPropertyChanged("Id");
            this.OnIdChanged();
        }
    }
    private int _Id;
    partial void OnIdChanging(int value);
    partial void OnIdChanged();

    [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)]
    [global::System.Runtime.Serialization.DataMemberAttribute()]
    public string Username
    {
        get
        {
            return this._Username;
        }
        set
        {
            this.OnUsernameChanging(value);
            this.ReportPropertyChanging("Username");
            this._Username = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false);
            this.ReportPropertyChanged("Username");
            this.OnUsernameChanged();
        }
    }
    private string _Username;
    partial void OnUsernameChanging(string value);
    partial void OnUsernameChanged();

    [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(IsNullable=false)]
    [global::System.Runtime.Serialization.DataMemberAttribute()]
    public string EmailAddress
    {
        get
        {
            return this._EmailAddress;
        }
        set
        {
            this.OnEmailAddressChanging(value);
            this.ReportPropertyChanging("EmailAddress");
            this._EmailAddress = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false);
            this.ReportPropertyChanged("EmailAddress");
            this.OnEmailAddressChanged();
        }
    }
    private string _EmailAddress;
    partial void OnEmailAddressChanging(string value);
    partial void OnEmailAddressChanged();

    [global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("TestEFAppModel", "FK_Addresses_Users", "Addresses")]
    [global::System.Xml.Serialization.XmlIgnoreAttribute()]
    [global::System.Xml.Serialization.SoapIgnoreAttribute()]
    [global::System.Runtime.Serialization.DataMemberAttribute()]
    public global::System.Data.Objects.DataClasses.EntityCollection<Address> Addresses
    {
        get
        {
            return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedCollection<Address>("TestEFAppModel.FK_Addresses_Users", "Addresses");
        }
        set
        {
            if ((value != null))
            {
                ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedCollection<Address>("TestEFAppModel.FK_Addresses_Users", "Addresses", value);
            }
        }
    }

    [global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("TestEFAppModel", "UserXGroups", "Groups")]
    [global::System.Xml.Serialization.XmlIgnoreAttribute()]
    [global::System.Xml.Serialization.SoapIgnoreAttribute()]
    [global::System.Runtime.Serialization.DataMemberAttribute()]
    public global::System.Data.Objects.DataClasses.EntityCollection<Group> Groups
    {
        get
        {
            return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedCollection<Group>("TestEFAppModel.UserXGroups", "Groups");
        }
        set
        {
            if ((value != null))
            {
                ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedCollection<Group>("TestEFAppModel.UserXGroups", "Groups", value);
            }
        }
    }
}

Hmmmm…. so where is my domain object in there? In fact, all of the domain objects are generated into a single file like this. You get to extend your domain objects by using partial classes. The partial classes that we implement allow us to take advantage of some partial methods that are in the generated classes. As you can see from this code in one of the above setters:

set
{
    this.OnUsernameChanging(value);
    this.ReportPropertyChanging("Username");
    this._Username = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false);
    this.ReportPropertyChanged("Username");
    this.OnUsernameChanged();
}

We have two partial methods “OnUsernameChanging” and “OnUsernameChanged” along with two events “ReportPropertyChanging” and “ReportPropertyChanged”. So when we create our partial classes we can tap into these methods. This way if we wanted to intercept the setting of our Username property we could implement a partial class like this (if you haven’t ever used partial classes, then go here):

public partial class User
{
    partial void OnUsernameChanging(string value)
    {            
        // check value and do something here
    }
}

But what if we want to do something when the property is retrieved? We don’t really have a lot of options, the getter in the user partial class looks like this:

get
{
    return this._Username;
}

Hmmm. So I guess they don’t want us to hook into the getter! Having user code hooked into the getter might have caused issues at some infrastructure level for them, so I’m going to give them the benefit of the doubt! But not too much. At first I thought that maybe I could go to the EDM (Entity Data Model) and make the getter and setter on the property as private or protected. Then rename the property to something like “UsernameInternal”. Then I could introduce a completely new property in the partial class to expose this property:

public string Username
{
    get
    {
        return this.UsernameInternal;
    }
    set
    {
        this.UsernameInternal = value;
    }
}

The only problem is that because we have wrapped the EDM generated property, we can no longer query against it! How would I write this query?

var userQuery = from u in entities.UserSet 
    where u.Username == "TestUser" 
    select u;

Sure we can put “Username” in the query because we have exposed our own property, but since this property is not in the EDM, the Entity Framework has no knowledge of this property so we can’t query against it! Dang. And to think I was going to try and use this same technique to implement some lazy loading. Hmmmmmmm. So if we want to be able to query against any property then we are going to have to directly expose the property that has the “EdmScalarPropertyAttribute” on it. This is the attribute that signifies to the Entity Framework that this property is a mapped scalar property.

This property has to share the name of the property tag in our CSDL file. Sadly, in order to get any control over my classes, it looks like I am going to have to ditch the Entity Designer altogether. Which isn’t necessarily a bad thing considering that all of the xml and class files are generated into only two files which creates an interesting situation for teams that are editing these files independently. Definitely a merge nightmare. What blows my mind is that they also actually store the metadata for the EDM diagram right in with the mapping xml.

Before you start thinking that I ditched the Entity Designer too soon, another issue here is that all of the generated entities descend from a base EntityObject class that keeps us from forming our own object heirarchy. And we can’t edit any of the generated classes or else all of our changes will be overridden whenever we make a change to it. Another issue is that we don’t really have any control over our object lifetime. The Entity Designer spits out default public constructors and a default factory method containing parameters for all properties of the object. Why? I don’t understand why they just didn’t leave off the constructor and factory method and let me define them in my partial classes. I can’t remove them from the generated classes, but I could easily add them if they weren’t there. Geeeez.

Alright, so I mentioned that we need to ditch the EDM designer, so what do we use now? Thankfully Microsoft provided us a tool called EdmGen.exe. This is a command-line tool that we can use to generate the mapping files for our database:

 image

EdmGen actually spits out different files for everything. And when I say “everything” I mean only the different file types. So we get these files:

image

What we are going to do here is ditch the ObjectLayer.cs and Views.cs files and create our own Entities. Hopefully we can isolate most of the EF specific code into a base entity class. But since we are going to remove generated entities we are going to have to keep the behavior that the previously generated classes had. This is where that IPOCO stuff comes in. IPOCO allows you to implement a few interfaces (in our case three) instead of using the base EntityObject class. Our base entity class’ definition will end up looking like this:

public class Entity : IEntityWithKey, IEntityWithChangeTracker, IEntityWithRelationships

These are the three interfaces that we must implement in order to use this with our EDM. The first just gives the entity framework something to identify your entities by, the second provides a change tracking mechanism, and the third provides a way for your entity to hold its relationships. These are fairly self explanatory, and luckily we can isolate most of the behavior for these into our base class. So our goal is to have business entities that lack Entity Framework specific details, and which expose no Entity Framework specific types. This post is getting  a bit long, so I am going to leave it off right there for now…

In future parts of this series we will break down the base entity class that I have created and take a look at the different entities and how we can create them. We will also take a look at creating an ObjectContext and show you how we can use and query these entities just like we could if we created them through the EDM designer. I will also provide you with the full source to the project that I am using in this series, so stay tuned!

Loved the article? Hated it? Didn’t even read it?

We’d love to hear from you.

Reach Out

Comments (5)

  1. Seems like an inheritance model would be better than partial classes. Of course the properties should be virtual.

  2. "So our goal is to have business entities that lack Entity Framework specific details, and which expose no Entity Framework specific types."
    That’s not going to work, you always have to at least implement interfaces which tie your class to the EDM.

    But then again, why bother? I agree with you about the ‘all classes in the same file’ and ‘no ability to tap into the getter’ and other nonsense they thought of getting away with, but at the end of the day, what will bring doing it all yourself to the table? That you hand-wrote boring code which can be generated as well. (though their generator sucks, I give you that).

    In your article you sound a bit like a carpenter who is only interested in using a bright new shiny hammer he bought yesterday, no matter if he has to use the hammer or not: just because you can write the code yourself doesn’t mean you should. After all, your model (which has a value-type as an entity btw, ‘Address’ isn’t an entity with an own identity) is very small. If you’re facing a model with 200-300 entities or more, no client of yours will pay you for writing thousands of lines of domain class code you could have generated yourself as well.

  3. @Frans I’m not sure that I made the point of this post completely clear. I work for a company that is Microsoft based, so my reality is that at some point I’m going to have to use the EF. I am merely sharing my experiences in trying to make the tool a bit more palatable, and then letting others decide if going through all of this effort is worth it just to use the "Microsoft tool".
    I think there are numerous better options out there (including LLBLGen Pro), and IMO they should all be considered before implementing an application.

  4. I think what we all have to keep in mind that the Entity Framework (EF) is designed for the data domain, and should be used as such. The designer isn’t just for developers, and is expected to enter into ETL and Reporting solutions. I think as you start to explore the designer more you will notice that you can model your data domain without a db scheme in place already. I’m new to this style of modeling too, and find that I really need more knowledge about the practical application of the EF, and the tools surrounding it. I can’t remember the last time I’ve used fat objects that contain a lot of logic, and I tend to use more of a service model to create lighter objects for my client anyway.

    I think as the framework matures more, and developers invest more time in this domain they won’t be so quick to write it off. Maybe Microsoft should drop names like the WCF team did to get developers to adopt it. Like say Martin Fowler… I know that is retarded, but perception goes a long why with a developers first experience.

    Happy Learning…

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

More Insights

View All