简体   繁体   中英

Entity Framework - Navigation Property Not Eager/Lazy Loaded?

I have the following entites:

public class Ticket 
{
    public int Id { get; set; }
    public string RequestBy { get; set; }
    public int PriorityId { get; set; }

    public ApplicationUser Requester { get; set; }
    public Priority Priority { get; set; }
}

public class ApplicationUser
{
    public ApplicationUser()
    {
        Id = Guid.NewGuid().ToString();
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Priority
{
    public int Id { get; set; }
    public string Name { get; set; }
}

When I create a new Ticket as follows in my controller:

 public class BaseController : Controller
{
    protected readonly IUnitOfWork UnitOfWork;

    public BaseController(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork; 
    }
}

public class TicketController : BaseController
{
    public TicketController(IUnitOfWork unitOfWork) : base(unitOfWork) { }

    [HttpPost]
    public ActionResult CreateTicket(CreateTicketVM viewModel)
    {
         // repopulate dropdowns incase we need to return view
         // viewModel.Priorities is IEnumerable list
         viewModel.Priorities = UnitOfWork.PriorityRepository.GetPriorities();

        //validation code removed for brevity...

        var ticket = new Ticket
        {
           RequestBy = !string.IsNullOrEmpty(viewModel.RequestBy) ? viewModel.RequestBy : User.Identity.GetUserId(),
           PriorityId = viewModel.PriorityId != 0 ? viewModel.PriorityId : (int)PriorityLevel.Medium,
        };

        UnitOfWork.TicketRepository.Add(ticket);
    }
}

Now when I debug var ticket after calling .Add(ticket) why has it loaded the related navigation property Priority and not the Requester navigation property despite the RequestBy value being set? As you can see I haven't defined any virtual keywords against either navigation properties?

Strangley this in only happening for all navigation properties that are of ApplicationUser type. All other navigation properties seem to get loaded even without using the virtual keyword???

Using fluent api I have defined the RequestBy FK as follows:

modelBuilder.Entity<Ticket>()
 .HasRequired(x => x.Requester)
 .WithMany()
 .HasForeignKey(x => x.RequestBy);

Below is additional code to provide some more context.

Ticket Repository:

public class TicketRepository : ITicketRepository
{
    private readonly ApplicationDbContext _context;

    public TicketRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public void Add(Ticket ticket)
    {
        _context.Ticket.Add(ticket);
    }
}

Priority Repository:

public class PriorityRepository : IPriorityRepository
{
    private readonly ApplicationDbContext _context;

    public PriorityRepository(ApplicationDbContext context)
    {
        _context = context;
    }

    public IEnumerable<Priority> GetPriorities()
    {
        return _context.Priority.ToList();
    }
}

Unit of work:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;

    public ITicketRepository TicketRepository { get; private set; }
    public IPriorityRepository PriorityRepository { get; private set; }

    public UnitOfWork(ApplicationDbContext context)
    {
        _context = context;
        TicketRepository = new TicketRepository(_context);
        PriorityRepository = new PriorityRepository(_context);
    }

    public void Complete()
    {
        _context.SaveChanges();
    }
}

Neither navigation property should be loaded. What you see is most likely a result of the so called navigation property fixup feature - if entity is already tracked by the context, EF updates navigation properties of the entities referencing it even if you don't specifically request that.

You can easily verify that by using var _context = new YourDbContext(); local variable instead of the _context field. Or something like this:

var ticket = new Ticket
{
    RequestBy = !string.IsNullOrEmpty(viewModel.RequestBy) ? viewModel.RequestBy : User.Identity.GetUserId(),
    PriorityId = viewModel.PriorityId != 0 ? viewModel.PriorityId : (int)PriorityLevel.Medium,
};

bool priorityLoaded = _context.Priority.Local.Any(e => e.Id == ticket.PriorityId);
bool userLoaded = _context.ApplicationUser.Local.Any(e => e.Id == ticket.RequestBy);

_context.Ticket.Add(ticket);

According to your description, priorityLoaded should be true while userLoaded - false .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM