Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Repository & Entity Framework Logic to Retrieve Entity with Includes

I have two Entity Framework 5 Get() methods that perform (i) a single entity get by ID, and (ii) a single entity get via a filter with any eager loading bolted on. See below for the code:

internal readonly FallenNovaContext Context;
private readonly DbSet<TEntity> _dbSet;

internal GenericRepository(FallenNovaContext context)
{
    Context = context;
    _dbSet = context.Set<TEntity>();
}

// (i) Get by ID.
public TEntity GetById(int id)
{
    return _dbSet.Find(id);
}

// (ii) Get by filter and optional eager loading includes.
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();
}

All of which works fine now what I'm finding as my application grows is I'm writing a lot of non-generic methods that need a mix of both - more specifically I want a generic get by ID and also be able to eager load related entities.

So the method signature would look something like this:

 public TEntity GetById(
     int id,
     IEnumerable<string> includePaths)
 {
       // ???
 }

Which I could call like this:

 User user = UnitOfWork.UserRepository.GetById(117, new List<string>() { "UserRole", "UserStatus" });

Or like this:

 Car car = UnitOfWork.CarRepository.GetById(51, new List<string>() { "Make", "Model", "Tyres" });

Any help on the suggestions of how I use Entity Framework 5 to code the logic for the TEntity GetById(int id, IEnumerable includePaths) method would be appreciated.

like image 946
Bern Avatar asked Nov 04 '22 18:11

Bern


1 Answers

First, write a base class for entities, which defines the primary key field. Something like the following may work:

public abstract class BaseEntity
{
    public int Id {get;set;}
}

Then, write a base class for your repositories; define all generic methods in this base repository. Let this repository have a generic parameter of entity type:

public class RepositoryBase<TEntity> where TEntity : BaseEntity
{
   public TEntity GetById(
     int id,
     params Expression<Func<TEntity, object>>[] includeList)
     {
            TEntity entity = null;
            ObjectQuery<TEntity> itemWithIncludes = context.Set<TEntity>() as ObjectQuery<TEntity>;
            foreach (Expression<Func<TEntity, object>> path in includeList)
            {
                itemWithIncludes = ((IQueryable)itemWithIncludes.Include(path)) as ObjectQuery<T>;
            }

            IQueryable<TEntity> items = itemWithIncludes.AsQueryable<TEntity>();
            entity = items.Where(p => p.Id == id).SingleOrDefault();
            return entity;
     }
}

Update: @Bern asked whether there is any other way to find primary key than declaring a base class. The following questions refer to this problem.

Entity Framework 4: How to find the primary key?

Entity Framework code first. Find primary key

On the otherhand I do not know if there is any other way in EF 5.

like image 148
daryal Avatar answered Nov 10 '22 00:11

daryal