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();
}
}
RequestByis a string in a model, so it doesn't have aFirstNamepropertyvar ticketafter call to_context.Ticket.Add(ticket);I observe the above behaviour.RequestByis not following the convention for explicit FK property name (which in this case isRequesterId), you need data annotations/fluent API to associate it withRequesternavigation property. Currently EF is using shadow (hidden) property calledRequester_Id.