Entity Framework Pitfalls: Entity Refresh

After an entity is loaded by Entity Framework, it gets stored in its first level cache. After that, whenever EF executes a query that returns this entity, identified by its primary key, EF always returns the cached entry, and makes no attempt to see if the cached data (the rest of the properties) is still up to date with that returned from the query. This feature is common to other ORMs, but it may result in unexpected behavior – we cannot rely on the state of a loaded entity if the database can change by other processes..

The solution for EF pre-Core is either:

  • Remove (evict) the entity from first level cache, by marking it as detached, when we want it to be reloaded from the database:
MyEntity e = ...;

 

ctx.Entry(e).State = EntityState.Detached;

  • Explicitly ask for it to be reloaded:
MyEntity e = ...;

 

ctx.Entry(e).Reload();

In EF Core, there is no Reload method – although I did write an implementation for it –, so the only option is to detach the entity from the context.

Implementing Missing Features in Entity Framework Core – Part 2

This is the second in a series of posts about missing functionality in EF Core. You can find the first here.

Entity Framework used to support three ways to load related entities (one to one, one to many, many to one, many to many):

Entity Framework Core doesn’t include (yet) the explicit loading and lazy loading mechanisms. Lazy loading is not included because EF Core does not generate proxies for the entities it loads, and I guess nobody bothered to implement explicit loading. Well, that’s exactly what we’ll do here!

The idea is, even if we didn’t require a collection to be loaded when we issued the query, we can still load it afterwards. We should keep a syntax similar to the previous version, when this feature was available:

//load a blog

var blog = ctx.Blogs.Single(b => b.BlogId == 1);

 

//Posts collection is empty

blog.Posts.Count(); //0

 

//eager load all Posts

ctx.Entry(blog).Load(b => b.Posts);

 

//Posts collection is no longer empty

blog.Posts.Count(); //!= 0

 

We will create an extension method over EntityEntry<T>:

public static void Load<TSource, TDestination>(this EntityEntry<TSource> entry, Expression<Func<TSource, IEnumerable<TDestination>>> path, Expression<Func<TDestination, TSource>> pathBack = null) where TSource : class where TDestination : class

{

    var entity = entry.Entity;

    var context = entry.Context;

    var entityType = context.Model.FindEntityType(typeof(TSource));

    var keys = entityType.GetKeys();

    var keyValues = context.GetEntityKey(entity);

    var query = context.Set<TDestination>() as IQueryable<TDestination>;

    var parameter = Expression.Parameter(typeof(TDestination), "x");

    PropertyInfo foreignKeyProperty = null;

 

    if (pathBack == null)

    {

        foreignKeyProperty = typeof(TDestination).GetProperties().Single(p => p.PropertyType == typeof(TSource));

    }

    else

    {

        foreignKeyProperty = (pathBack.Body as MemberExpression).Member as PropertyInfo;

    }

 

    var i = 0;

 

    foreach (var property in keys.SelectMany(x => x.Properties))

    {

        var keyValue = keyValues[i];

 

        var expression = Expression.Lambda(

                Expression.Equal(

                    Expression.Property(Expression.Property(parameter, foreignKeyProperty.Name), property.Name),

                    Expression.Constant(keyValue)),

                parameter) as Expression<Func<TDestination, bool>>;

 

        query = query.Where(expression);

 

        i++;

    }

 

    var list = query.ToList();

 

    var prop = (path.Body as MemberExpression).Member as PropertyInfo;

    prop.SetValue(entity, list);

}

The GetEntityKey method was introduced in the previous post, but I will add it here, for your convenience:

public static object[] GetEntityKey<T>(this DbContext context, T entity) where T : class

{

    var state = context.Entry(entity);

    var metadata = state.Metadata;

    var key = metadata.FindPrimaryKey();

    var props = key.Properties.ToArray();

 

    return props.Select(x => x.GetGetter().GetClrValue(entity)).ToArray();

}

You can see that the Load method takes two parameters: the first is required, it represents the collection that we with to load, the second is optional, and it represents the go-back property, in case there is more than one property of the root entity’s type. For example:

ctx.Entry(blog).Load(b => b.Posts, p => p.Blog);

In this case, the path p => p.Blog is needless, because a Post only belongs to one Blog.

A similar approach can be used to load explicitly other kinds of relations (one to one and many to one).

Hope you find this useful! Winking smile

Implementing Missing Features in Entity Framework Core

Introduction

By now, we all know that Entity Framework Core 1.0 will not include several features that we were used to. In this post, I will try to explain how we can get over this by implementing them ourselves, or, at least, working out some workaround.

This time, I am going to talk about mimicking the Reload and Find methods, plus, give a set of other useful methods for doing dynamic programming.

Find

Find lets us query an entity by its identifier. We will first define a strongly typed version:

public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class

{

    var context = set.GetInfrastructure<IServiceProvider>().GetService<DbContext>();

    var entityType = context.Model.FindEntityType(typeof(TEntity));

    var keys = entityType.GetKeys();

    var entries = context.ChangeTracker.Entries<TEntity>();

    var parameter = Expression.Parameter(typeof(TEntity), "x");

    IQueryable<TEntity> query = context.Set<TEntity>();

 

    //first, check if the entity exists in the cache

    var i = 0;

 

    //iterate through the key properties

    foreach (var property in keys.SelectMany(x => x.Properties))

    {

        var keyValue = keyValues[i];

 

        //try to get the entity from the local cache

        entries = entries.Where(e => keyValue.Equals(e.Property(property.Name).CurrentValue));

 

        //build a LINQ expression for loading the entity from the store

        var expression = Expression.Lambda(

                Expression.Equal(

                    Expression.Property(parameter, property.Name),

                    Expression.Constant(keyValue)),

                parameter) as Expression<Func<TEntity, bool>>;

 

        query = query.Where(expression);

 

        i++;

    }

 

    var entity = entries.Select(x => x.Entity).FirstOrDefault();

 

    if (entity != null)

    {

        return entity;

    }

 

    //second, try to load the entity from the data store

    entity = query.FirstOrDefault();

 

    return entity;

}

And then a loosely-typed one:

private static readonly MethodInfo SetMethod = typeof(DbContext).GetTypeInfo().GetDeclaredMethod("Set");

 

public static object Find(this DbContext context, Type entityType, params object[] keyValues)

{

    dynamic set = SetMethod.MakeGenericMethod(entityType).Invoke(context, null);

    var entity = Find(set, keyValues);

    return entity;

}

Not sure if you’ve had to do queries through an entity’s Type, but I certainly have!

The Find method will first look in the DbContext local cache for an entity with the same keys, and will return it if it finds one. Otherwise, it will fallback to going to the data store, for that, it needs to build a LINQ expression dynamically.

Sample usage:

//strongly typed version

var blog = ctx.Blogs.Find(1);

 

//loosely typed version

var blog = (Blog) ctx.Find(typeof(Blog), 1);

Getting an Entity’s Id Programmatically

This is also important: getting an entity’s id values dynamically, that is, without knowing beforehand what are the properties (normally just one) that keeps them. Pretty simple:

public static object[] GetEntityKey<T>(this DbContext context, T entity) where T : class

{

    var state = context.Entry(entity);

    var metadata = state.Metadata;

    var key = metadata.FindPrimaryKey();

    var props = key.Properties.ToArray();

 

    return props.Select(x => x.GetGetter().GetClrValue(entity)).ToArray();

}

Here’s how to use:

Blog blog = ...;

var id = ctx.GetEntityKey(blog);

Reload

The Reload method tells Entity Framework to re-hydrate an already loaded entity from the database, to account for any changes that might have occurred after the entity was loaded by EF. In order to properly implement this, we will first need to define the two methods shown above (no need for the loosely-coupled version of Find, though):

public static TEntity Reload<TEntity>(this DbContext context, TEntity entity) where TEntity : class

{

    return context.Entry(entity).Reload();

}

 

public static TEntity Reload<TEntity>(this EntityEntry<TEntity> entry) where TEntity : class

{

    if (entry.State == EntityState.Detached)

    {

        return entry.Entity;

    }

 

    var context = entry.Context;

    var entity = entry.Entity;

    var keyValues = context.GetEntityKey(entity);

 

    entry.State = EntityState.Detached;

 

    var newEntity = context.Set<TEntity>().Find(keyValues);

    var newEntry = context.Entry(newEntity);

 

    foreach (var prop in newEntry.Metadata.GetProperties())

    {

        prop.GetSetter().SetClrValue(entity, prop.GetGetter().GetClrValue(newEntity));

    }

 

    newEntry.State = EntityState.Detached;

    entry.State = EntityState.Unchanged;

 

    return entry.Entity;

}

Here’s two versions of Reload: one that operates on an existing EntityEntry<T>, and another for DbContext; one can use them as:

Blog blog = ...;

 

//first usage

ctx.Entry(blog).Reload();

 

//second usage

ctx.Reload(blog);

You will notice that the code is updating the existing instance that was already loaded by EF, if any, and setting its state to Unchanged, so any changes made to it will be lost.

Local

EF Core 1.0 also lost the Local property, which allows us to retrieve cached entities that were previously loaded. Here’s one implementation of it:

public static IEnumerable<EntityEntry<TEntity>> Local<TEntity>(this DbSet<TEntity> set, params object [] keyValues) where TEntity : class

{

    var context = set.GetInfrastructure<IServiceProvider>().GetService<DbContext>();

    var entries = context.ChangeTracker.Entries<TEntity>();

 

    if (keyValues.Any() == true)

    {

        var entityType = context.Model.FindEntityType(typeof(TEntity));

        var keys = entityType.GetKeys();

        var i = 0;

 

        foreach (var property in keys.SelectMany(x => x.Properties))

        {

            var keyValue = keyValues[i];

            entries = entries.Where(e => keyValue.Equals(e.Property(property.Name).CurrentValue));

            i++;

        }

    }

 

    return entries;

}

the keyValues parameter is optional, it is the entity’s identifier values. If not supplied, Local will return all entries of the given type:

//all cached blogs

var cachedBlogs = ctx.Set<Blog>().Local();

 

//a single cached blog

var cachedBlog = ctx.Set<Blog>().Local(1).SingleOrDefault();

Evict

Entity Framework has no Evict method, unlike NHibernate, but it is very easy to achieve the same purpose through DbEntityEntry.State (now EntityEntry.State, in EF Core). I wrote an implementation that can evict several entities or one identified by an identifier:

public static void Evict<TEntity>(this DbContext context, TEntity entity) where TEntity : class

{

    context.Entry(entity).State = EntityState.Detached;

}

 

public static void Evict<TEntity>(this DbContext context, params object [] keyValues) where TEntity : class

{

    var tracker = context.ChangeTracker;

    var entries = tracker.Entries<TEntity>();

 

    if (keyValues.Any() == true)

    {

        var entityType = context.Model.FindEntityType(typeof (TEntity));

        var keys = entityType.GetKeys();

 

        var i = 0;

 

        foreach (var property in keys.SelectMany(x => x.Properties))

        {

            var keyValue = keyValues[i];

 

            entries = entries.Where(e => keyValue.Equals(e.Property(property.Name).CurrentValue));

 

            i++;

        }

    }

 

    foreach (var entry in entries.ToList())

    {

        entry.State = EntityState.Detached;

    }

}

As usual, an example is in order:

var blog = ...;

var id = ctx.GetEntityKey(blog);

 

//evict the single blog

ctx.Evict(blog);

 

//evict all blogs

ctx.Evict<Blog>();

 

//evict a single blog from its identifier

ctx.Evict<Blog>(id);

Conclusion

Granted, some of the missing functionality will give developers a major headache, but, even with what we have, it’s not impossible to go around them. Of course, this time, it was simple stuff, but in future posts I will try to address some more complex features.