From 4c58790ef2b54fb883d4f78b20cba8430ac630e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=AC=E9=9B=A8=E5=A3=B0?= Date: Thu, 19 May 2022 09:52:57 +0800 Subject: [PATCH] fix(softdelete): Fix the problem that soft delete fails when using IsolationDbContext (#64) * fix(Data.Contracts.EF): Fix the problem that soft delete fails when using IsolationDbContext * test(EntityFramework): Add SoftDelete Integration Test * chore: Remove invalid judgments --- .../MasaDbContextOptionsBuilderExtensions.cs | 2 +- .../CustomDbContext.cs | 16 ++++ ...Masa.Contrib.Isolation.UoW.EF.Tests.csproj | 1 + .../TestIsolation.cs | 95 ++++++++++++++++--- .../_Imports.cs | 3 + 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs index 997e47aa7..de6b72e41 100644 --- a/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs @@ -49,7 +49,7 @@ private static void UseSoftDelete(this MasaDbContextOptionsBuilder masaDbContext var constructorInfo = softDeleteSaveChangesFilterType.GetConstructors().FirstOrDefault()!; var invokeDelegate = InstanceBuilder.CreateInstanceDelegate(constructorInfo); - masaDbContextOptionsBuilder.Services.TryAdd( + masaDbContextOptionsBuilder.Services.Add( new ServiceDescriptor(typeof(ISaveChangesFilter), serviceProvider => { diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/CustomDbContext.cs b/test/Masa.Contrib.Isolation.UoW.EF.Tests/CustomDbContext.cs index 3c70f7f92..c0e8e0624 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/CustomDbContext.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/CustomDbContext.cs @@ -9,6 +9,8 @@ public CustomDbContext(MasaDbContextOptions options) : base(options) { } public DbSet User { get; set; } + public DbSet Tag { get; set; } + protected override void OnModelCreatingExecuting(ModelBuilder builder) { builder.Entity(ConfigureUserEntry); @@ -40,3 +42,17 @@ public User() this.Id = Guid.NewGuid(); } } + +public class Tag : ISoftDelete +{ + public Guid Id { get; private set; } + + public string Name { get; set; } = default!; + + public bool IsDeleted { get; protected set; } + + public Tag() + { + this.Id = Guid.NewGuid(); + } +} diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/Masa.Contrib.Isolation.UoW.EF.Tests.csproj b/test/Masa.Contrib.Isolation.UoW.EF.Tests/Masa.Contrib.Isolation.UoW.EF.Tests.csproj index 863a918a9..a5bd40440 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/Masa.Contrib.Isolation.UoW.EF.Tests.csproj +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/Masa.Contrib.Isolation.UoW.EF.Tests.csproj @@ -24,6 +24,7 @@ + diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs b/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs index e4c8cd870..16e21e12a 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs @@ -1,8 +1,6 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -using Masa.BuildingBlocks.Data; - namespace Masa.Contrib.Isolation.UoW.EF.Tests; [TestClass] @@ -36,7 +34,8 @@ public void TestUseIsolationUoW2() eventBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); Assert.ThrowsException(() => { - eventBuilder.Object.UseIsolationUoW(null!, dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); + eventBuilder.Object.UseIsolationUoW(null!, + dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); }); } @@ -69,7 +68,8 @@ public void TestUseIsolationUoWByUseEnvironment() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), + dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsNotNull(serviceProvider.GetService()); @@ -81,7 +81,9 @@ public void TestUseIsolationUoWByUseMultiEnvironment() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW( + isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiEnvironment(), + dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -93,7 +95,8 @@ public void TestUseIsolationUoWByUseTenant() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), + dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsNotNull(serviceProvider.GetService()); @@ -105,7 +108,8 @@ public void TestUseIsolationUoWByUseMultiTenant() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiTenant(), + dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -122,7 +126,8 @@ public void TestUseIsolation() _services.AddSingleton(configurationRoot); Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite()); + dispatcherOption.Object.UseIsolationUoW( + isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite()); var serviceProvider = _services.BuildServiceProvider(); var customDbContext = serviceProvider.GetRequiredService(); var unitOfWorkAccessor = serviceProvider.GetRequiredService(); @@ -164,7 +169,8 @@ public void TestUseIsolation() unifOfWorkNew3.ServiceProvider.GetRequiredService().SetTenant(new Tenant("00000000-0000-0000-0000-000000000002")); unifOfWorkNew3.ServiceProvider.GetRequiredService().SetEnvironment("development"); var dbContext3 = unifOfWorkNew3.ServiceProvider.GetRequiredService(); - Assert.IsTrue(GetDataBaseConnectionString(dbContext3) == "data source=test2" && unitOfWorkAccessorNew3.CurrentDbContextOptions!.ConnectionString == "data source=test2"); + Assert.IsTrue(GetDataBaseConnectionString(dbContext3) == "data source=test2" && + unitOfWorkAccessorNew3.CurrentDbContextOptions!.ConnectionString == "data source=test2"); var unifOfWorkNew4 = unitOfWorkManager.CreateDbContext(true); var unitOfWorkAccessorNew4 = unifOfWorkNew4.ServiceProvider.GetRequiredService(); @@ -200,7 +206,8 @@ public void TestUseMultiEnvironment() }); Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite()); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), + dbOptionBuilder => dbOptionBuilder.UseSqlite()); var serviceProvider = _services.BuildServiceProvider(); var customDbContext = serviceProvider.GetRequiredService(); var unitOfWorkAccessor = serviceProvider.GetRequiredService(); @@ -214,19 +221,22 @@ public void TestUseMultiEnvironment() var unitOfWorkAccessorNew2 = unifOfWorkNew2.ServiceProvider.GetRequiredService(); unifOfWorkNew2.ServiceProvider.GetRequiredService().SetEnvironment("dev"); var dbContext2 = unifOfWorkNew2.ServiceProvider.GetRequiredService(); - Assert.IsTrue(GetDataBaseConnectionString(dbContext2) == "data source=test5" && unitOfWorkAccessorNew2.CurrentDbContextOptions!.ConnectionString == "data source=test5"); + Assert.IsTrue(GetDataBaseConnectionString(dbContext2) == "data source=test5" && + unitOfWorkAccessorNew2.CurrentDbContextOptions!.ConnectionString == "data source=test5"); var unifOfWorkNew3 = unitOfWorkManager.CreateDbContext(true); var unitOfWorkAccessorNew3 = unifOfWorkNew3.ServiceProvider.GetRequiredService(); unifOfWorkNew3.ServiceProvider.GetRequiredService().SetEnvironment("pro"); var dbContext3 = unifOfWorkNew3.ServiceProvider.GetRequiredService(); - Assert.IsTrue(GetDataBaseConnectionString(dbContext3) == "data source=test6" && unitOfWorkAccessorNew3.CurrentDbContextOptions!.ConnectionString == "data source=test6"); + Assert.IsTrue(GetDataBaseConnectionString(dbContext3) == "data source=test6" && + unitOfWorkAccessorNew3.CurrentDbContextOptions!.ConnectionString == "data source=test6"); var unifOfWorkNew4 = unitOfWorkManager.CreateDbContext(true); var unitOfWorkAccessorNew4 = unifOfWorkNew4.ServiceProvider.GetRequiredService(); unifOfWorkNew4.ServiceProvider.GetRequiredService().SetEnvironment("staging"); var dbContext4 = unifOfWorkNew4.ServiceProvider.GetRequiredService(); - Assert.IsTrue(GetDataBaseConnectionString(dbContext4) == "data source=test4" && unitOfWorkAccessorNew4.CurrentDbContextOptions!.ConnectionString == "data source=test4"); + Assert.IsTrue(GetDataBaseConnectionString(dbContext4) == "data source=test4" && + unitOfWorkAccessorNew4.CurrentDbContextOptions!.ConnectionString == "data source=test4"); } [TestMethod] @@ -254,7 +264,8 @@ public void TestUseMultiTenant() }); Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseSqlite()); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), + dbOptionBuilder => dbOptionBuilder.UseSqlite()); var serviceProvider = _services.BuildServiceProvider(); var customDbContext = serviceProvider.GetRequiredService(); var unitOfWorkAccessor = serviceProvider.GetRequiredService(); @@ -293,7 +304,8 @@ public void TestUseMultiTenantAndAddMasaConfiguration() builder.AddMasaConfiguration(); Mock dispatcherOption = new(); dispatcherOption.Setup(opt => opt.Services).Returns(builder.Services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite()); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), + dbOptionBuilder => dbOptionBuilder.UseSqlite()); var serviceProvider = builder.Services.BuildServiceProvider(); var customDbContext = serviceProvider.GetRequiredService(); var unitOfWorkAccessor = serviceProvider.GetRequiredService(); @@ -302,5 +314,58 @@ public void TestUseMultiTenantAndAddMasaConfiguration() Assert.IsTrue(currentDbContextOptions.ConnectionString == "data source=test1"); } + [TestMethod] + public async Task TestUseMultiTenantAndUseFilterAsync() + { + var services = new ServiceCollection(); + services.Configure(option => + { + option.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = $"data source=test_{Guid.NewGuid()}" + }; + option.IsolationConnectionStrings = new List + { + new() + { + ConnectionString = $"data source=test_{Guid.NewGuid()}", + TenantId = "1" + } + }; + }); + Mock dispatcherOption = new(); + dispatcherOption.Setup(opt => opt.Services).Returns(services).Verifiable(); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => + isolationBuilder.UseMultiTenant(), + dbOptionBuilder => dbOptionBuilder.UseSqlite().UseFilter().UseFilter()); + + var serviceProvider = services.BuildServiceProvider(); + var customDbContext = serviceProvider.GetRequiredService(); + await customDbContext.Database.EnsureCreatedAsync(); + var tag = new Tag() + { + Name = "Tom" + }; + await customDbContext.Set().AddAsync(tag); + await customDbContext.SaveChangesAsync(); + + Assert.IsTrue(await customDbContext.Set().CountAsync() == 1); + + tag = await customDbContext.Set().FirstOrDefaultAsync(t => t.Id == tag.Id); + Assert.IsNotNull(tag); + + customDbContext.Set().Remove(tag); + await customDbContext.SaveChangesAsync(); + Assert.IsTrue(await customDbContext.Set().CountAsync() == 0); + + var dataFilter = serviceProvider.GetRequiredService(); + using (dataFilter.Disable()) + { + Assert.IsTrue(await customDbContext.Set().CountAsync() == 1); + tag = await customDbContext.Set().FirstOrDefaultAsync(t => t.Id == tag.Id); + Assert.IsNotNull(tag); + } + } + private string GetDataBaseConnectionString(CustomDbContext dbContext) => dbContext.Database.GetConnectionString()!; } diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs b/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs index 8f68488b0..c578e8b6d 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs @@ -1,12 +1,15 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Contracts.DataFiltering; global using Masa.BuildingBlocks.Data.UoW; global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.BuildingBlocks.Isolation.Environment; global using Masa.BuildingBlocks.Isolation.MultiTenant; global using Masa.BuildingBlocks.Isolation.Options; global using Masa.Contrib.Configuration; +global using Masa.Contrib.Data.Contracts.EF; global using Masa.Contrib.Data.EntityFrameworkCore; global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Isolation.MultiEnvironment;