user3352250
user3352250

Reputation: 326

Null property in object recovered by EF

I'm having trouble pulling a ApplicationUser object in a query to the DbContext.

Here is my Foo model

public class Foo
{
    public int Id { get; set; }
    public string Title { get; set; }
    public ApplicationUser Creator { get; set; }
}

Inside of my Foo controller in the edit action I want to check that the User requesting the edit is the Creator.

//ApplicationDbContext inherits IdentityDbContext
private ApplicationDbContext appDb { get; set; }
...
public async Task<ActionResult> Edit(int? id)
{
        Foo target =  appDb.Foos.First(o => o.Id == id);

        if (target == null)
        {
            return HttpNotFound();
        }

        if (target.Creator.Id != User.Identity.GetUserId())
        {
            ViewBag.Error = "Permission denied.";
            return Redirect(Request.UrlReferrer.ToString());
        }

        return View(target);
    }

The problem is that for some reason target.Creator returns null, although all Foo have a Creator object referenced. In the same controller, in the details action it gets pulled just fine:

var entry = appDb.Foos.Where(f=>f.Id == id)
            .Select(foo => new FooViewModel()
            {
                Id = foo.Id,
                Title = foo.Title,
                CreatorId = foo.Creator.Id,
                CreatorName = foo.Creator.FirstName,
            }).FirstOrDefaultAsync();

What am I missing? What is so different about trying to access foo.Creator inside of the Display query than trying to access it directly on a Foo object? I thought EF would have queried for foo.Creator when I requested the Foo itself.

Should I change foo.Creator to store foo.CreatorId instead?

Thanks.

Upvotes: 3

Views: 59

Answers (2)

JotaBe
JotaBe

Reputation: 39055

The navigation properties are not feched by default. If you need them, eager load them using .Include():

Foo target =  appDb.Foos.First(o => o.Id == id).Include(f => f.Creator);

Your code will not work at all: if you try to find the first element, with .First() and it doesn't exist, you'll get an exception. I.e, your code will never hit if (target == null).

You should use .FirstOrDefault(), or better thant that, Find().

Upvotes: 4

Amit Kumar Ghosh
Amit Kumar Ghosh

Reputation: 3726

you need eager loading -

var returned = appDb.Foos.First(o => o.Id == id).Include(a => a.Creator);

On the other hand, you could use Lazy Loading by marking the ApplicationUser member as virtual.

Upvotes: 2

Related Questions