C
C#2y ago
pyrodistic

MVC EF 5 - How to reference Model FK Properties?

Hello, I'm having some trouble figuring out the logic to reference FK table properties from a model. I'm currently using the same model for index and details views for an entity. On the index I have:
@foreach (var item in Model)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Species.Name)</td>
...
@foreach (var item in Model)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Species.Name)</td>
...
This displays the Species.Name corretly in the column (Dog/Cat/...), where Species is a FK in the Pet class (public virtual Species Species { get; set; }). On the details view I have:
<dt>@Html.DisplayNameFor(model => model.Species)</dt>
<dd>@Html.DisplayFor(model => model.Species)</dd>
<dt>@Html.DisplayNameFor(model => model.Species)</dt>
<dd>@Html.DisplayFor(model => model.Species)</dd>
For the index on the controller I do:
public async Task<IActionResult> Index(int id)
{
var dataContext = _petRepository.GetByOwner(id).Include("Species.Taxonomy");
return View(await dataContext.ToListAsync());
}
public async Task<IActionResult> Index(int id)
{
var dataContext = _petRepository.GetByOwner(id).Include("Species.Taxonomy");
return View(await dataContext.ToListAsync());
}
However on the details I'm unable to use Include:
public async Task<IActionResult> Details(int? id)
{
var pet = await _petRepository.GetByIdAsync(id.Value); // Cannot use .Include("Species.Taxonomy") here.

return View(pet);
}
public async Task<IActionResult> Details(int? id)
{
var pet = await _petRepository.GetByIdAsync(id.Value); // Cannot use .Include("Species.Taxonomy") here.

return View(pet);
}
The error I get is:
Error CS1061 'Task<Pet>' does not contain a definition for 'Include' and no accessible extension method 'Include' accepting a first argument of type 'Task<Pet>' could be found (are you missing a using directive or an assembly reference?)
Error CS1061 'Task<Pet>' does not contain a definition for 'Include' and no accessible extension method 'Include' accepting a first argument of type 'Task<Pet>' could be found (are you missing a using directive or an assembly reference?)
I'm using repository pattern and dependency injection. So the GetByIdAsync() is implemented on a general repository for all entities.
public async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().AsNoTracking().FirstOrDefaultAsync(e => e.Id == id);
}
public async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().AsNoTracking().FirstOrDefaultAsync(e => e.Id == id);
}
While the GetByOwner() is specific to the Pets repository and returns a IQueryable<Pet>. Both repositories have the same references, tried to use .AsQueryable(), but have the same error. What am I doing wrong and how can I fix it? Thanks in advance.
7 Replies
Angius
Angius2y ago
I'm using repository pattern
That's your first mistake Either expose IQueryable from your repositories, or use the service pattern instead In the former case, you'd be essentially duplicating what EF already provides But I hear some people like redundancy
pyrodistic
pyrodistic2y ago
It's required for this project. What would be your suggestion for fixing the issue, would you mind explaining?
Angius
Angius2y ago
My suggestion would be creating a repository method that basically just returns _ctx.Set<T>.AsQueryable() and thus, lets you chain other methods in the controller, like .Include() Not entirely sure, though, since it seems like it'd break the purpose of the repository pattern. Perhaps someone better versed in enterprise redundancy will be able to answer.
pyrodistic
pyrodistic2y ago
Yeah, I was attempting that but didn't have much success. If I include the species repo on the Pets controller, and on the Details GET do:
var pet = await _petRepository.GetByIdAsync(id.Value);
pet.Species = _speciesRepository.GetByIdAsync(pet.SpeciesId).Result;
return View(pet);
var pet = await _petRepository.GetByIdAsync(id.Value);
pet.Species = _speciesRepository.GetByIdAsync(pet.SpeciesId).Result;
return View(pet);
And then on the View call model.Species.Name it works. But I don't know if there is a better solution.
Angius
Angius2y ago
You can do it this way, sure Just don't use .Result await it properly It'll cause two calls to the database too
pyrodistic
pyrodistic2y ago
Thanks, don't know why I defaulted to Result. Yeah the two call are the "big" issue... I guess my issue/expectation was that the Pet returned by "await _petRepository.GetByIdAsync(id.Value);" should already include the reference/values for its foreign tables. That it would automatically recognize that Pet.SpeciesId refereces Species.Id and would bring all the info for that specific row.
Angius
Angius2y ago
Only if you use lazy loading Which is, generally, bad idea Because it still results in multiple database calls, but now they're hidden instead of explicit