Skip to content

Commit

Permalink
fix: fixes undesired context tracking across EFCoreStore methods (#633)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewTriesToCode authored Jan 30, 2023
1 parent 6c21fe9 commit 3605a75
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Finbuckle.MultiTenant.Stores
{
public class EFCoreStore<TEFCoreStoreDbContext, TTenantInfo> : IMultiTenantStore<TTenantInfo>
where TEFCoreStoreDbContext : EFCoreStoreDbContext<TTenantInfo>
where TTenantInfo :class, ITenantInfo, new()
where TTenantInfo : class, ITenantInfo, new()
{
internal readonly TEFCoreStoreDbContext dbContext;

Expand All @@ -22,28 +22,30 @@ public EFCoreStore(TEFCoreStoreDbContext dbContext)

public virtual async Task<TTenantInfo?> TryGetAsync(string id)
{
return await dbContext.TenantInfo
.Where(ti => ti.Id == id)
.SingleOrDefaultAsync();
return await dbContext.TenantInfo.AsNoTracking()
.Where(ti => ti.Id == id)
.SingleOrDefaultAsync();
}

public virtual async Task<IEnumerable<TTenantInfo>> GetAllAsync()
{
return await dbContext.TenantInfo.ToListAsync();
return await dbContext.TenantInfo.AsNoTracking().ToListAsync();
}

public virtual async Task<TTenantInfo?> TryGetByIdentifierAsync(string identifier)
{
return await dbContext.TenantInfo
.Where(ti => ti.Identifier == identifier)
.SingleOrDefaultAsync();
return await dbContext.TenantInfo.AsNoTracking()
.Where(ti => ti.Identifier == identifier)
.SingleOrDefaultAsync();
}

public virtual async Task<bool> TryAddAsync(TTenantInfo tenantInfo)
{
await dbContext.TenantInfo.AddAsync(tenantInfo);

return await dbContext.SaveChangesAsync() > 0;
var result = await dbContext.SaveChangesAsync() > 0;
dbContext.Entry(tenantInfo).State = EntityState.Detached;

return result;
}

public virtual async Task<bool> TryRemoveAsync(string identifier)
Expand All @@ -63,14 +65,10 @@ public virtual async Task<bool> TryRemoveAsync(string identifier)

public virtual async Task<bool> TryUpdateAsync(TTenantInfo tenantInfo)
{
var existingLocal = dbContext.TenantInfo.Local.Where(ti => ti.Id == tenantInfo.Id).SingleOrDefault();
if(existingLocal != null)
{
dbContext.Entry(existingLocal).State = EntityState.Detached;
}

dbContext.TenantInfo.Update(tenantInfo);
return await dbContext.SaveChangesAsync() > 0;
var result = await dbContext.SaveChangesAsync() > 0;
dbContext.Entry(tenantInfo).State = EntityState.Detached;
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,76 @@ public void AddIdentifierUniqueConstraint()
var prop = GetModelProperty("Identifier");
Assert.True(prop!.IsIndex());
}

[Fact]
public void NotTrackContextOnGet()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = store.TryGetAsync("initech-id").Result;

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

[Fact]
public void NotTrackContextOnGetByIdentifier()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = store.TryGetByIdentifierAsync("initech").Result;

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

[Fact]
public void NotTrackContextOnGetAll()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = store.GetAllAsync().Result.First();

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

[Fact]
public void NotTrackContextOnAdd()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = new TenantInfo
{
Id = "test-id",
Identifier = "test-identifier",
Name = "test"
};
store.TryAddAsync(tenant);

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

[Fact]
public void NotTrackContextOnUpdate()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = store.TryGetByIdentifierAsync("initech").Result;
tenant.Name = "new name";
store.TryUpdateAsync(tenant);

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

[Fact]
public void NotTrackContextOnRemove()
{
var store = CreateTestStore() as EFCoreStore<TestEfCoreStoreDbContext, TenantInfo>;
var tenant = store.TryGetByIdentifierAsync("initech").Result;
tenant.Name = "new name";
store.TryRemoveAsync(tenant.Id);

var entity = store.dbContext.Entry(tenant);
Assert.Equal(EntityState.Detached, entity.State);
}

// Basic store functionality tested in MultiTenantStoresShould.cs

Expand Down

0 comments on commit 3605a75

Please sign in to comment.