Bern Bern - 1 month ago 13
C# Question

Need a generic EF method that accepts entity id and includes

I'm using Entity Framework 5 and have a generic repository, within which are several methods like the two Get() methods below:

public TEntity GetById(int id)
{
return DbSet.Find(id);
}

public TEntity Get(
Expression<Func<TEntity, bool>> filter = null,
IEnumerable<string> includePaths = null)
{
IQueryable<TEntity> query = DbSet;

if (filter != null)
{
query = query.Where(filter);
}

if (includePaths != null)
{
query = includePaths.Aggregate(query, (current, includePath) => current.Include(includePath));
}

return query.SingleOrDefault();
}


These are both very helpful, however when I want to make a slightly more complex GetById() call and retrieve some entity references at the same time, like so:

var user = _userRepository.GetById(
id,
new List<string> { "Roles", "Invoices" });


I end up having to roll out entity-specific (so non-generic) GetById(id, includes) calls for each entity so that I can access their specific Id fields in the lambda, i.e. UserId, or InvoiceId etc.

public User GetById(
int id,
IEnumerable<string> includes)
{
return Get(
(u => u.UserId == id),
includes);
}


It seems that I can't, with my average EF skills, work out how to combine the goodness of DbSet.Find(id) with the .Include() call in a generic fashion.

So the question is - is there a way to write a generic EF method that I can use to get an entity by it's id and include some references, and in turn remove the need to write entity specific GetById(id, includes) calls like I've done above.

Thanks in advance.

Answer

Heres how I do it in my generic repository:

    public T GetBy(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includes)
    {
        var result = GetAll();
        if (includes.Any())
        {
            foreach (var include in includes)
            {
                result = result.Include(include);
            }
        }
        return result.FirstOrDefault(predicate);
    }

Note this is using lambda includes and FirstOrDefault rather than find but the result is the same.

You can check out the full source for my generic repository here.

You can call this by the following:

var entity = myRepository.GetBy(e=>e.Id == 7, /*Includes*/ e=> e.ANavigationProperty, e=>e.AnotherNavProperty);

Edit:

I don't use generic repositories anymore, instead I use extension methods to build the query on the fly. I find this gets much better reuse. (see my article here on Composable Repositories)

Comments