FindAsync and Include LINQ statements
The code I have got so far works fine
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
But I want to include 1 table more and cannot use FindAsync
public async Task<ActionResult> Details(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ItemDetailModel model = new ItemDetailModel();
model.Item = await db.Items.Include(i=>i.ItemVerifications).FindAsync(id);
if (model.Item == null)
{
return HttpNotFound();
}
return View(model);
}
So I am facing this error
Severity Code Description Project File Line Suppression State Error CS1061 'IQueryable' does not contain a definition for 'FindAsync' and no extension method 'FindAsync' accepting a first argument of type 'IQueryable' could be found (are you missing a using directive or an assembly reference?)
Any clue how to fix it?
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();
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;
}
}
When you program using solid principles and domain design then use generics. The Repository pattern uses a generic class. I pass a lambda express to the GetObjectsQueryable function. I have setup lazy loading to be on, using code first handle bars. However, I am moving away from lazy loading and implement a microservice architecture. The include table is a string and you can use the nameof(xxclass) function to ensure the correct name. The function returns and IQueryable results. The repository class methods can be used by its derived class enhance the method is protected. This is a dotnet.core demonstration.
public class Repository
where T : class
{
public IQueryable<T> GetObjectsQueryable(Expression<Func<T, bool>> predicate, string includeTable="")
{
IQueryable<T> result = _dbContext.Set<T>().Where(predicate);
if (includeTable != "")
result = result.Include(includeTable);
return result;
}
}