EF Core 5
I have a classic setup of one-to-many navigation in EF Core code first.
One site <--> Many assets
I'll post the code just so that you see it with your own eyes :
public class Site : IAggregateRoot
{
public Guid Id { get; private set; }
private readonly List<Asset> _assets = new List<Asset>();
public IReadOnlyCollection<Asset> Assets => _assets;
public Site(Guid id) { Id = id; }
}
public class Asset : IAggregateRoot
{
public Guid Id { get; private set; }
public Site? Site { get; private set; }
public Guid? SiteId { get; private set; }
public Asset(Guid id) { Id = id; }
public Asset(Guid id, Guid siteId) : this(id: id)
{
SiteId = siteId;
}
public void SetSite(Guid? siteId)
{
SiteId = siteId;
}
}
Asset model builder :
builder.ToTable("Asset");
BaseModelHelper.InitBaseModel(builder);
builder
.HasOne(x => x.Site)
.WithMany(x => x.Assets)
.HasForeignKey(x => x.SiteId)
.OnDelete(DeleteBehavior.Restrict);
Site model builder:
builder.ToTable("Site");
BaseModelHelper.InitBaseModel(builder);
builder.Metadata
.FindNavigation(nameof(Site.Assets))
.SetPropertyAccessMode(PropertyAccessMode.Field);
It's managed with repositories, which in turn rely on Unit of Work design pattern:
public class AppDbContext : DbContext, IUnitOfWork
{
...
public DbSet<Site> Sites { get; set; } = null!;
public DbSet<Asset> Assets { get; set; } = null!;
...
}
then
public class SiteRepository
{
private readonly AppDbContext _dbContext;
public IUnitOfWork UnitOfWork => _dbContext;
public SiteRepository(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public void UnloadFromContext(Site site)
{
_dbContext.Entry(site).State = EntityState.Detached;
}
public async Task ReloadAsync(Site site)
{
await _dbContext.Entry(site).ReloadAsync();
}
public async Task<Site?> GetWithAssetsAsync(Guid id)
{
return await _dbContext.Sites
.Include(x => x.Assets)
.FirstOrDefaultAsync(x => x.Id == id);
}
public async Task AddAsync(Site site)
{
await _dbContext.Sites.AddAsync(site);
}
}
and
public class AssetRepository : IAssetRepository
{
private readonly AppDbContext _dbContext;
public IUnitOfWork UnitOfWork => _dbContext;
public AssetRepository(AppDbContext dbContext)
{
_dbContext = dbContext;
}
...
//The rest is similar to SiteRepository
}
===================
Now I'm just trying to refresh the navigation after changes to the database. I know that the changes occur because I see it through SQL querying while the app is halted using a breakpoint. After querying, I let the app continue and do the refresh. yet site.Assets remains empty...
var site = new Site(id);
await sitesRepo.AddAsync(site);
await sitesRepo.UnitOfWork.SaveChangesAsync();
then
var assets = await assetsRepo.GetOrCreateAsync(assetIds);
await assetsRepo.UnitOfWork.SaveChangesAsync();
At this point everything I need has been created. A site and some assets. I now want to assign some assets to the site. I do it by changing the navigation from the asset's side because it seems easier that way : I simply change asset.SiteId.
var assetsToAdd = assets.Where(whatever condition);
foreach (var asset in assetsToAdd)
{
asset.SetSite(siteId);
}
await assetsRepo.UnitOfWork.SaveChangesAsync();
await sitesRepo.UnitOfWork.SaveChangesAsync();
At this point I see in the db that the site DOES have assets. All asset.SiteId are correct.
Later in the code I need to start wrking from the site's side of the navigation. Since some assets mught have been added, I need to make sure that the site.Assets navigation now contains those new assets. Therefore I try to "reload" that navigation, since EF doesn't seem to do it by itself :
//My pitiful attempts : I've read that detaching an entity before loading it reloads the navigation...
sitesRepo.UnloadFromContext(site); // This does _dbContext.Entry(site).State = EntityState.Detached;
//await sitesRepo.ReloadAsync(site); //I tried that as an alternative...
var updatedSite = await sitesRepo.GetWithAssetsAsync(siteId); //This does the Include
//updatedSite is not null, I checked
var updatedAssets = updatedSite?.Assets.ToList() ?? new List<Asset>(); //Always empty :'-(
return updatedAssets;
I've looked at those articles but it still eludes me
How to refresh an Entity Framework Core DBContext?
Reload an entity and all Navigation Property Association- DbSet Entity Framework
Why won't it reload the damn navigation???
EDIT : I've detailed the repos to show how the dbContext is managed.