Browsed by
Category: .NET exposed

Change tracking optimization in Entity Framework

Change tracking optimization in Entity Framework

Entity Framework change tracking optimization

EF_readonly_optimization change tracking metaphor

Data changes tracking

Entity Framework gives us an opportunity to work with data without bothering to notify database about entity changes made during data processing. EF provides few methods of tracking this modifications.

Snapshot change tracking

By default in Entity Framework it is enabled snapshot change tracking mechanism. It works by saving entity states each time when it is loaded from the database. When for example SaveChanges method is called, Entity Framework scans all entities in current context and compares them with saved state.

Entity Framework performs Detect Changes automatically when the following methods are called:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

Change tracking mechanism is wider described in following resources:

https://msdn.microsoft.com/en-us/data/jj556205.aspx

http://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/

http://blog.oneunicorn.com/2012/03/11/secrets-of-detectchanges-part-2-when-is-detectchanges-called-automatically/

http://blog.oneunicorn.com/2012/03/12/secrets-of-detectchanges-part-3-switching-off-automatic-detectchanges/

http://blog.oneunicorn.com/2012/03/13/secrets-of-detectchanges-part-4-binary-properties-and-complex-types/

Disabling AutoDetectChanges

If property AutoDetectChangesEnabled in DbContext.Configuration is set, EF runs change tracking according to rules described above. So you can disable it by setting this property to false. But you should remember to reset this value to previous one after making your special operations. In general case this mechanism is very useful, especially to modify multiple objects. However, it is not necessary for read-only queries or bulk insert operations.

Rules to safe disabling

Each time when you want to disable it, you should check if your code operating on data meets two following conditions:

  1. Method from EF cannot change the EF context that DetectChanges method needs to be called.
  2. Each time when entities was changed outside of EF methods and this modifications should be saved, DetectCanges method should be executed manually.

For more information

https://ilmatte.wordpress.com/2013/01/01/entity-framework-code-first-always-disable-autodetectchanges-when-importing-data/

http://rpajak.com/2012/04/

Disable tracking for read-only

The first possibility to optimize time of database queries is to disable this tracking during read-only operations. In case on WebAPI it could means, that each action that doesn’t modify any data need not change tracking mechanism. It leads us to create custom attribute to disable this mechanism per HTTP request.

In the example below I use static IoC.Container to get instance of DbContext. You also have to remember to configure Dependency Injector container to create context in request scope, not in transient scope.

In my experiment on working system, method is executed around 20% faster than before adding this attribute to them.

	public class ReadonlyAttribute : ActionFilterAttribute
	{
	    private IDbContext _dbContext;
	
	    public override void OnActionExecuting(HttpActionContext actionContext)
	    {
	        _dbContext = IoC.Container.GetInstance<IDbContext>();
	        _dbContext.Configuration.AutoDetectChangesEnabled = false;
	        base.OnActionExecuting(actionContext);
	    }
	    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
	    {
	        _dbContext = IoC.Container.GetInstance<IDbContext>();
	        _dbContext.Configuration.AutoDetectChangesEnabled = true;
	        base.OnActionExecuted(actionExecutedContext);
	    }
	
	}

You can use it just by adding this attribute to chosen methods:

    [HttpGet]
    [Readonly]
    public IQueryable<PostDto> Get()
    {
        return _postRepository.All();
    }

Bulk inserts

The other case when you could want to disable tracking is bulk inserts. In this case it is not necessary to disable this functionality for whole action, but you can do it for particular operation:

    using (var context = new DbContext())
    {
        try
        {
            context.Configuration.AutoDetectChangesEnabled = false;
            posts.ForEach(p => context.Posts.Add(p));
        }
        finally
        {
            context.Configuration.AutoDetectChangesEnabled = true;
        }
        context.SaveChanges();
    }

Additionally, I really like the solution described in post http://joshgallagher.info/2014/06/14/entity-framework-performance-tip-for-creating-entities/

It enables to encapsulate this operation in single and simple object:

	public sealed class NoChangeTracking : IDisposable
	{
	    private readonly DbContext _dbContext;
	    private readonly bool _initialAutoDetectChangesValue;
	
	    public NoChangeTracking(DbContext dbContext)
	    {
	        if (dbContext == null) throw new ArgumentNullException("dbContext");
	        _dbContext = dbContext;
	
	        _initialAutoDetectChangesValue = dbContext.Configuration.AutoDetectChangesEnabled;
	
	        SetChangeDetection(false);
	    }
	
	    public void Dispose()
	    {
	        SetChangeDetection(_initialAutoDetectChangesValue);
	    }
	
	    private void SetChangeDetection(bool setting)
	    {
	    	_dbContext.Configuration.AutoDetectChangesEnabled = setting;
	    }
	
	}

Then you can use this object simply

	using(new NoChangeTracking(context))
	{
		context.Posts.Add(new Post());
	}

Disabling for single entity

In some cases you can disable tracking for single entity source. It could be useful for dictionaries types which are not changed in normal code.

	var entities = context.PostCategory.AsNoTracking();

http://gasior.net.pl/quick-tip-12-stosuj-asnotracking-gdzie-sie-da/

http://blog.staticvoid.co.nz/2012/4/2/entity_framework_and_asnotracking

Expression> vs FUNC

Expression> vs FUNC

Both types of types: Expression<Func<T>> and Func<T> are very similar and could be used for the same type of tasks. I will try to dispel any doubts, when to use expressions or when just functions.

Let’s begin with Func<TResult> type. There is simple shortage encapsulation for delegate type taking no argument and returns value of type TResult. You can define this delegate explicitly:

public delegate TResult Func<in T, out TResult>(T arg)

There are also types like Func<T, TResult>, Func<T1, T2, TResult>, … which differ only with number of input parameters.

Full list of encapsulated delegates is below, but we don’t describe it more detailed:

  1. Func
  2. Action
  3. Predicate
  4. Converter
  5. Comparison

We can define Func both using lambda and by assigning other function

public int ExternalFunction 
{ 
   return 12; 
} 
Func<int> func1 = () => 12; 
Func<int> func2 = ExternalFunction;

Similarly we can define Expression<Func<T>>

Expression<Func<int>> func1 = () => 12;

but the behavior of this two object is different. The Expression type store additionally information (in a tree data structure) about what specific lambda expression does. Thanks to this information it could be possible to process this information and execute this function behavior in different way.

Entity Framework

The difference described above could be well presented in Entity Framework context. If we want to access some data in DbContext we refer to DbSet collection, where we can use Where methods.

This Where methods have different definitions:

IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

As you can notice, there are two differences in definitions. The first one uses Func and operates on IEnumerable<T> collection, but the second one uses Expression and works on IQuerable<T>.

This example shows that using the Expression type Entity Framework can translate function into SQL query and return IQuerable object, so we can add additional query conditions to this query. By using Func type condition, all dataset will be loaded from database and filtering will be done in .NET application.

Conversions

To convert Expression<Func<T>> to Func<T> you can use Compile method

Func<T> func = expressionFunc.Compile();