Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Entity Framework and Base Entity class

, 20 May 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Entity Framework and Base Entity class.

Entity Framework does great job with taking care of entities changes and entities collection internally. And its uses almost plain POCO objects. Almost because collections of dependents data like in 1 to many tables relationship needs virtual keyword. Its understandable since EF needs to track what happens to collection. For instance lets have 2 tables: Customer and dependent Order.

To map this relation in EF model we need to create two classes: Customer with relationship to Orders collection and Order with relationship to single Customer. Both end of relation needs to be virtual. Oh well. Actually they aren't have to be virtual, but as you can read here this allow for 'lazy loading' and 'change tracking' capabilities of EF, so its pretty useful to add virtual keywords in those places.

public partial class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Order> Orders { get; set; }
}

public partial class Order
{
    public int Id { get; set; }
    public virtual Customer Customer { get; set; }
}

That's pretty much it. There some rare cases when for example useful is to initialize collection of child entities on entity creation but its not requirement from EF point of view. So what could be the reason to add base class for entity?

Thing is that whenever you want to transfer entity to some another subsystem to make changes there, Entity Framework cannot and could not track those changes (you NEVER EVER should transfer entity object directly but map it to some other data object first!). Mapping data from entity to some data transfer object (DTO), making changes and returning them again to EF, will more likely fail. With simple object its achievable and should working, but it can spawn errors with collections mapping. In last application I was working that was the case. But I used EntityFramework.Patters extension so, in pure EF this can work fine (but I doubt it).

Root of the problem was Order collection in Customer entity. When I tried to transfer customer through i.e. WCF service, make changes at client side, and return them to EF, it caused EF to think that I deleted all items from Orders, and added some new instead. Its cool that it can tell that collection changed. To bad that this happens even when it not changed at all! To be frank EF wasn't entirely responsible it was mapping mechanism in AutoMapper. It wasn't considering two Order items with same values the same because they weren't referencing same object. After replacing old Order with new Order in AutoMapper, EF wasn't considering them equal for exactly same reason: reference to them was different.

How to remedy that? There have to be some custom mechanism of entities equality. And its best to that on some BaseEntity object, right?

public class BaseEntity : BaseEntity where TEntity : BaseEntity
    {
        private int? oldHashCode;

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj == this)
            {
                return true;
            }
            if (obj is TEntity)
            {
                var objAsBaseEntity = obj as TEntity;
                return objAsBaseEntity.Id == this.Id;
            }
            return false;
        }
    }

This implementation is partly based on MSDN Equals guidelines and on base entity class I have found for NHibernate. But is not enough.  With Equals implementation we have to implement also GetHashCode. Problem with this is that, new entity should have whole time its exists the same hash code. More important hash code should be only dependent on Id property, which is only thing that allow two separate objects to be equal. So we need to make entity hash code dependent on id. But what about two new objects? They will have empty id and because of that the same hash code? One solution is to make hash code to be generated from base class when Id is empty.

public override int GetHashCode()
{
    // once we have a hashcode we'll never change it
    if (oldHashCode.HasValue)
    return oldHashCode.Value;
    // when this instance is new we use the base hash code
    // and remember it, so an instance can NEVER change its
    // hash code.
    var thisIsNew = Id == Guid.Empty;
    if (thisIsNew)
    {
        oldHashCode = base.GetHashCode();
        return oldHashCode.Value;
    }
    return Id.GetHashCode();
}

When id is set, hash code is generated from it. When it is not set, hash code is generated from base class which is object in our case. And first time hash code is generated is final. Hash code cannot be changed. It is because it would brake functionality of HashSet and Dictionary classes. EF uses both of them so it would be big issue. It is best implementation I could find, and still it have some drawbacks. For example entity loaded from DB and mapped from outside of EF with the same data and the same Id will have different hash code. It is because in first situation hash code is generated from database id. In second situation hash code is generated from new object which have empty id. It does not matter that after hash code generation id is set to real value. Once it is generated it can not be changed.

To remedy that we need to make mapping of mapped entities and entities loaded from DB by hand.
In next post I will explain how to make class that will resolve mapping of entities to correct this issue.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

n.podbielski
Software Developer
Poland Poland
No Biography provided

Comments and Discussions

 
QuestionAnother object-to-relational mismatch PinmemberDavidSherwood29-May-13 16:28 
AnswerRe: Another object-to-relational mismatch Pinmembern.podbielski30-May-13 22:11 
QuestionAnother object-to-relational mis-match PinmemberDavidSherwood29-May-13 16:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150301.1 | Last Updated 20 May 2013
Article Copyright 2013 by n.podbielski
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid