5

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.

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @daryal - 2 questions what does "entityWithIncludes" do as it isn't declared, and is there any other way to get to a primary key using EF5 than the method you suggested?
entityWithIncludes is a typo, it is itemWithIncludes; I fixed it in the question. for the second question, I will post an update; but for now you can change your entities to inherit the base class by changing tt files.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.