Execute Code Before Save and After Fetch in Entity Framework

Thanks to Sam Changtum for the insight in his post.

This tutorial addresses two issues:

  • If you want to execute some code before your DbContext is saved
  • If you want to execute some code after your DbContext fetches some data and materializes them into your Model POCO objects
  • If you want to throw an exception depending on some constraints when SaveChanges() is called.

DbContext implements the interface IObjectContextAdapter and it returns an ObjectContext instance. ObjectContext has two events that are related to our problem: ObjectMaterialized and SavingChanges. You can attach methods to those events to handle them.

Clarification:
When you call a query such as the following, ObjectMaterialized event is raised for each student, e.g. If the result of your query has 10 students, the event will be raised 10 times. It is the same for SavingChanges.

YourDbContext.Students.Where(s => s.Courses.Any(c => c.Name == "Math")).ToList()

So this is how you modify YourDbContext class:

public class YourDbContext : DbContext
{
	public YourDbContext()
	{
		((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += ObjectContext_ObjectMaterialized;
		((IObjectContextAdapter)this).ObjectContext.SavingChanges += ObjectContext_SavingChanges;
	}

    static void ObjectContext_ObjectMaterialized(object sender, System.Data.Objects.ObjectMaterializedEventArgs e)
    {
        if (e.Entity == null)
            return;
        if (e.Entity.GetType() == typeof(Student))
        {
            Student student = e.Entity as Student;
            // There is no problem in using Navigation Properties here
			// If they haven't been materialized yet, it will be done here, but only once.
			// Don't worry, there will be no stack overflow.

			Teacher teacher = student.Teacher;
			Course firstCourse = student.Courses.First();

			// Do whatever you want here with your entities.
        }
    }

    static void ObjectContext_SavingChanges(object sender, System.Data.Objects.SavingChangesEventArgs e)
	{
		ObjectContext context = sender as ObjectContext;
		if(context != null)
		{
			// You can use other EntityState constants here
			foreach(ObjectStateEntry entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
			{
				// You can handle multiple Entity Types here. I use Student as an example for the sake of simplicity
				if(entry.Entity.GetType() == typeof(Student))
				{
					Student student = entry.Entity as Student;
					// Do whatever you want with your entities.
					// You can throw an exception here if you want to
					// prevent SaveChanges().
					if(student.Teacher == null)
						throw Exception("Something went wrong.");
				}	 
			}
		}
	}
}

Performance Implications

Let’s assume that there is a function called MyFunction(Student s). The following snippet is equivalent to calling MyFunction for each student entity object in ObjectMaterialized event handler, in terms of performance. The same goes for SavingChanges event handler if you move the foreach loop before a call to SaveChanges. So the performance implications depend on what’s inside MyFunction. Thus, when you use navigation properties, be careful. EF will fetch related entity objects if they aren’t already fetched. If you’re not confident in your code, check some resources on Lazy Loading/EF.


DbContext context = new DbContext();
List<Student> students = context.Students.ToList();

foreach(var student : students)
{
	MyFunction(student);
}

About these ads

2 Responses to Execute Code Before Save and After Fetch in Entity Framework

  1. Virat says:

    What are the performance implications in this implementation? For ex, I’m fetching 5000 rows and when this custom logic is applied on each of the rows then it seems like it will effect the performance badly?
    Will it be so, if yes, any solution to overcome? Or am I missing something?

    • anilanar says:

      I will quickly edit this post to talk about it. If you need to use navigation properties, say Students property of a Classroom, you should use Include() (e.g. Include(c->c.Students) ) before you call ToList() or any other method that triggers fetching from the persistence unit.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 40 other followers

%d bloggers like this: