Adding Include option in Repository pattern's Get method

.net c# entity-framework entity-framework-6

Question

I started to use Repository pattern with my exercise data base and until now everything was fine with this implementation of Get method:

public T Get(int id)
{
    return _context.Set<T>().Find(id);
}

This mehtod simply returns an object of given ID. The problem appeared when the returned object contained some navigation properties - using my Get they were alwys null. Using http://www.janholinka.net/Blog/Article/9 I modified my Get a little:

public T Get(int id, params Expression<Func<T, object>>[] includes)
{
    IQueryable<T> query = _context.Set<T>();
    if (includes != null)
        foreach (Expression<Func<T, object>> include in includes)
            query = query.Include(include);

    return ((DbSet<T>)query).Find(id);
}

Now I have an array of include expressions as an additional parameter, so I should be able to use this method like this (for example):

var data = myRepository.Get(1, n => n.MyProperty1);

Unforutnately I get an exception on the last line of my method:

'Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery`1[MjIot.Storage.Models.EF6Db.Device]' to type 'System.Data.Entity.DbSet`1[MjIot.Storage.Models.EF6Db.Device]'.'

It isn't able to cast DbQuery to DbSet. I tried many different casts, already my method differs a little from what was provided on the janholinka site, since the method given there didn't even compile for me.

Entity Framework is still kind of magical for me with its DbSets, DbQueries, etc.. I wasn't able to come up with a solution for this.

1
1
6/30/2018 11:08:28 AM

Accepted Answer

The simplest is to use FirstOrDefaultAsync or SingleOrDefaultAsync instead:

model.Item = await db.Items.Include(i => i.ItemVerifications)
    .FirstOrDefaultAsync(i => i.Id == id.Value);

The reason you are getting the error is because Find / FindAsync methods are defined for DbSet<T>, but the result of Include is IQueryable<T>.

Another way is to combine FindAsync with explicit loading:

model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
    return HttpNotFound();
}
await db.Entry(model.Item).Collection(i => i.ItemVerifications).LoadAsync();    
68
11/1/2016 1:13:49 PM

Popular Answer

If you are using a generic repository and you don't know the PK at runtime, this approach can help:

public interface IGenericRepository<TEntity> where TEntity : class
{
    Task<TEntity> Get(int id, string[] paths = null);
}

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    private readonly ApplicationDbContext _context;
    private readonly DbSet<TEntity> _dbSet;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = _context.Set<TEntity>();
    }

    public async Task<TEntity> Get(int id, string[] paths = null)
    {
        var model = await _dbSet.FindAsync(id);
        foreach (var path in paths)
        {
            _context.Entry(model).Reference(path).Load();
        }
        return model;
    }
}


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow