Seeding deserialised JSON data in the Context and then attempting to return the DbSet. api/module returns all Modules with empty ICollections (null if I don't instantiate them), where as the Assessments happily return the virtual Module.
Previous experience in MVC whereby I would have been accessing the object before it being sent to the view, so I haven't encountered this issue before.
The commented out line:
//Enumerable.ToList(ModuleItems.Include(mi => mi.Assessments));
Resolves the issue but feels very hacky and would need repeating for each DbSet.Passing the model names as parameters to include them when making the call in the repository also feels like a hack too.
What is the best practice?
EDIT: To add, when I inspect the DbSet when seeding the ICollections are populated and later, the Assessment DbSet has 6 items within it.
Module
public class Module
{
[Key]
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public DateTime InstanceStartDate { get; set; }
public DateTime InstanceEndDate { get; set; }
public ICollection<UnitLeaderModules> UnitLeaderModules { get; set; } = new HashSet<UnitLeaderModules>();
public ICollection<Assessment> Assessments { get; set; } = new HashSet<Assessment>();
}
Assessment
public class Assessment
{
[Key]
public int Id { get; set; }
[ForeignKey("Module")]
public int ModuleId { get; set; }
public string Description { get; set; }
public DateTime SubmissionDateMain { get; set; }
public DateTime SubmissionDateResit { get; set; }
public string SubmissionMethod { get; set; }
public virtual Module Module { get; set; }
}
Generic Repository
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext Context;
protected DbSet<T> DbSet;
public Repository(DbContext context)
{
Context = context;
DbSet = context.Set<T>();
}
public T Get<TKey>(TKey id)
{
return DbSet.Find(id);
}
public IQueryable<T> GetAll()
{
return DbSet;
}
public IQueryable<T> GetWhere(Expression<Func<T, bool>> whereExpression)
{
return DbSet.Where(whereExpression);
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
Save();
}
public void Update(T entity)
{
Save();
}
private void Save()
{
Context.SaveChanges();
}
}
Module Controller
[Route("api/module")]
[ApiController]
public class ModuleController : ControllerBase
{
private readonly IRepository<Module> _repository;
public ModuleController(IRepository<Module> repository)
{
_repository = repository;
}
[HttpGet]
public ActionResult<IQueryable<Module>> GetAll()
{
return Ok(_repository.GetAll());
}
[HttpGet("{id}", Name = "GetModule")]
public ActionResult<Module> GetById(int id)
{
var item = _repository.Get(id);
if (item == null)
{
return NotFound();
}
return item;
}
}
Context
public class UnitLeaderContext : DbContext
{
public DbSet<Leader> UnitLeaderItems { get; set; }
public DbSet<UnitLeaderModules> UnitLeaderModuleItems { get; set; }
public DbSet<Module> ModuleItems { get; set; }
public DbSet<Assessment> AssessmentItems { get; set; }
public UnitLeaderContext(DbContextOptions<UnitLeaderContext> options)
: base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
if (!EnumerableExtensions.Any(ModuleItems))
{
var data =
@"[
{
""id"": 1,
""code"": ""YEP404"",
""description"": ""Marine Systems"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 1,
""moduleId"": 1,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-01-15T00:00:00"",
""submissionDateResit"": ""2019-07-06T00:00:00"",
""submissionMethod"": ""Upload""
},
{
""id"": 2,
""moduleId"": 1,
""description"": ""Examination (40%)"",
""submissionDateMain"": ""2019-03-28T00:00:00"",
""submissionDateResit"": ""2019-07-08T00:00:00"",
""submissionMethod"": ""Email Lecturer""
}
]
},
{
""id"": 2,
""code"": ""EEN402"",
""description"": ""Marine Production"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 3,
""moduleId"": 2,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""SOL""
},
{
""id"": 4,
""moduleId"": 2,
""description"": ""Log Book 1 (40%)"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""SOL""
}
]
},
{
""id"": 3,
""code"": ""YEP402"",
""description"": ""Marine Materials"",
""instanceStartDate"": ""2018-09-24T00:00:00"",
""instanceEndDate"": ""2019-05-17T00:00:00"",
""assessments"": [
{
""id"": 5,
""moduleId"": 3,
""description"": ""Report 1 (60%)"",
""submissionDateMain"": ""2019-03-15T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""Hand-in Office""
},
{
""id"": 6,
""moduleId"": 3,
""description"": ""Examination"",
""submissionDateMain"": ""2019-04-10T00:00:00"",
""submissionDateResit"": ""2019-07-03T00:00:00"",
""submissionMethod"": ""In-person Exam""
}
]
}
]
";
var aaa = JsonConvert.DeserializeObject<List<Module>>(data);
ModuleItems.AddRange(aaa);
SaveChanges();
}
//Enumerable.ToList(ModuleItems.Include(mi => mi.Assessments));
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Module>().HasKey(m => m.Id);
builder.Entity<Module>().HasMany(m => m.UnitLeaderModules);
builder.Entity<Module>().HasMany(m => m.Assessments);
builder.Entity<Assessment>().HasKey(m => m.Id);
builder.Entity<Assessment>().HasOne(m => m.Module);
}
}