Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 4.1 code-first, required lazy load reference is null on save

I'm building a forum project for ASP.NET MVC 3, running on .NET 4 with the latest version of Entity Framework.

I have the usual forum design, with a Board with Categories, and Categories with Forums, Forums with Topics and Topics with Posts etc.

Simplified:

public class Category {
    [Required]
    public virtual Board Board { get; set; }
}
public class Forum {
    [Required]
    public virtual Category Category { get; set; }
}
public class Topic {
    [Required]
    public virtual Forum Forum { get; set; }
}
public class Post {
    [Required]
    public virtual Topic Topic { get; set; }
}

When a new post is created the topic is informed, and when a topic is changed, the forum is informed.

So, again simplified:

public class Forum {
    public void TopicChanged(Topic topic) {
        // Do stuff
    }
}
public class Topic {
    public void PostAdded(Post post) {
        // Do stuff
        this.Forum.TopicChanged(this);
    }
}

So after creating a new Post (and committing it to the database), I call PostAdded on the parent topic. Now this is where it get odd!

When I commit the changes to my repositories, I get a validation error, Category on Forum is required.

If I look in the database, the Forum has a reference to the parent Category. If I stop the code and looks at the Forum object, it has a Category instance. So this looks like an issue with lazy loading, especially because if I add this line:

var cat = this.Category

At the bottom of the TopicChanged method in the Forum class, there's no longer any errors.

Am I doing something totally wrong here, have I run into a borderline issue, or what is going on? I figured EF would see that the reference is a lazy loaded reference, and if it hasn't been changed, there's no reason why this should fail on save???

Thanks, Steen

like image 867
Steen Tøttrup Avatar asked Feb 23 '23 22:02

Steen Tøttrup


1 Answers

I've fixed my problem. It might not be a really nice and clean solution, but at least I can handle this situation now, without having to change a lot of my code (which needs to run with nHibernate also). So no dirty solution.

Just to give you an idea on how it's solved, I'll try and explain it here.

On the Commit() method on my repository, I start off calling GetValidationErrors() on the DbContext instance. This returns the error I encountered above along with the entity where the error occurs. On the base type of this entity (the entity is a proxy generated by EF) I run through all properties and when I encounter a property with the Required attribute, I call GetValue on the identical property on the proxy object.

Enough talk, more code:

var errors = this.context.GetValidationErrors();
foreach (DbEntityValidationResult result in errors) {
    Type baseType = result.Entry.Entity.GetType().BaseType;
    foreach (PropertyInfo property in result.Entry.Entity.GetType().GetProperties()) {
        if (baseType.GetProperty(property.Name).GetCustomAttributes(typeof(RequiredAttribute), true).Any()) {
            property.GetValue(result.Entry.Entity, null);
        }
    }
}
like image 66
Steen Tøttrup Avatar answered Apr 14 '23 06:04

Steen Tøttrup