From 60e51e95e9facd31935fe635370fb59a9577d8b0 Mon Sep 17 00:00:00 2001
From: zhenlei520 <wangzhenlei520@gmail.com>
Date: Tue, 2 May 2023 16:25:19 +0800
Subject: [PATCH] fix: fix issues-592 and Uniform type conversion method

---
 .../DefaultTypeConvertProvider.cs             |  41 ++++---
 .../DefaultTypeConvertProviderTest.cs         |  88 +++++++++++++
 .../Masa.BuildingBlocks.Data.Tests.csproj     |   1 +
 .../_Imports.cs                               |   3 +
 .../IConvertProvider.cs                       |   9 --
 .../Masa.BuildingBlocks.Service.Caller.csproj |   1 -
 .../DbContextTest.cs                          | 116 ++++++++++++++++--
 .../DefaultTypeAndDefaultValueProviderTest.cs |  85 +++++++++++++
 .../Entities/People.cs                        |  14 +++
 .../Infrastructure/CustomDbContext.cs         |   2 +
 .../DefaultTypeAndDefaultValueProvider.cs     |  27 ++++
 .../Filters/SaveChangeFilter.cs               |  36 ++++--
 .../Filters/SoftDeleteSaveChangesFilter.cs    |  12 +-
 .../ITypeAndDefaultValueProvider.cs           |  18 +++
 .../Extensions/DbContextExtensions.cs         |   8 +-
 .../Isolation/IsolationSaveChangesFilter.cs   |   8 +-
 .../Internal/TypeExtensions.cs                |   3 +-
 .../{ => Internal}/DefaultJsonDeserializer.cs |   8 +-
 .../{ => Internal}/DefaultJsonSerializer.cs   |   7 +-
 ...ib.Extensions.BackgroundJobs.Memory.csproj |   4 +-
 .../ConvertProvider.cs                        |  13 --
 .../IsolationBuilderExtensions.cs             |   1 -
 .../TenantTest.cs                             |  32 -----
 23 files changed, 428 insertions(+), 109 deletions(-)
 create mode 100644 src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/DefaultTypeConvertProviderTest.cs
 delete mode 100644 src/BuildingBlocks/Isolation/Masa.BuildingBlocks.Isolation/IConvertProvider.cs
 create mode 100644 src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DefaultTypeAndDefaultValueProviderTest.cs
 create mode 100644 src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Entities/People.cs
 create mode 100644 src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/DefaultTypeAndDefaultValueProvider.cs
 create mode 100644 src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/ITypeAndDefaultValueProvider.cs
 rename src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/{ => Internal}/DefaultJsonDeserializer.cs (64%)
 rename src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/{ => Internal}/DefaultJsonSerializer.cs (63%)
 delete mode 100644 src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/ConvertProvider.cs

diff --git a/src/BuildingBlocks/Data/Masa.BuildingBlocks.Data/TypeConverts/DefaultTypeConvertProvider.cs b/src/BuildingBlocks/Data/Masa.BuildingBlocks.Data/TypeConverts/DefaultTypeConvertProvider.cs
index 42bfc475c..fbfb9c1b7 100644
--- a/src/BuildingBlocks/Data/Masa.BuildingBlocks.Data/TypeConverts/DefaultTypeConvertProvider.cs
+++ b/src/BuildingBlocks/Data/Masa.BuildingBlocks.Data/TypeConverts/DefaultTypeConvertProvider.cs
@@ -15,15 +15,23 @@ public class DefaultTypeConvertProvider : ITypeConvertProvider
         typeof(DateTime?)
     };
 
-    private readonly List<Type> _notNeedSerializeTypes = new()
+    private readonly List<Type> _simpleTypes = new()
     {
-        typeof(String),
-        typeof(Guid),
-        typeof(DateTime),
-        typeof(Decimal),
-        typeof(Guid?),
-        typeof(DateTime?),
-        typeof(Decimal?)
+        typeof(string),
+        typeof(short?),
+        typeof(short),
+        typeof(int),
+        typeof(int?),
+        typeof(long),
+        typeof(long?),
+        typeof(float),
+        typeof(float?),
+        typeof(decimal),
+        typeof(decimal?),
+        typeof(double),
+        typeof(double?),
+        typeof(bool),
+        typeof(bool?)
     };
 
     private readonly IDeserializer? _deserializer;
@@ -38,15 +46,20 @@ public class DefaultTypeConvertProvider : ITypeConvertProvider
 
     public object? ConvertTo(string value, Type type, IDeserializer? deserializer = null)
     {
-        if (_types.Contains(type))
-            return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value)!;
+        if (value.IsNullOrWhiteSpace())
+            return default;
+
+        if (_simpleTypes.Contains(type))
+        {
+            if (type.IsNullableType())
+                return Convert.ChangeType(value, Nullable.GetUnderlyingType(type)!);
 
-        if (!IsSupportDeserialize(type))
             return Convert.ChangeType(value, type);
+        }
+
+        if (_types.Contains(type))
+            return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value)!;
 
         return (deserializer ?? _deserializer)!.Deserialize(value, type);
     }
-
-    private bool IsSupportDeserialize(Type type)
-        => !type.IsPrimitive && !_notNeedSerializeTypes.Contains(type);
 }
diff --git a/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/DefaultTypeConvertProviderTest.cs b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/DefaultTypeConvertProviderTest.cs
new file mode 100644
index 000000000..bc2249264
--- /dev/null
+++ b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/DefaultTypeConvertProviderTest.cs
@@ -0,0 +1,88 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.BuildingBlocks.Data.Tests;
+
+[TestClass]
+public class DefaultTypeConvertProviderTest
+{
+    [TestMethod]
+    public void ConvertToByBaseType()
+    {
+        var defaultTypeConvertProvider = new DefaultTypeConvertProvider();
+        Assert.AreEqual((short)0, defaultTypeConvertProvider.ConvertTo<short>("0"));
+        Assert.AreEqual((short)0, defaultTypeConvertProvider.ConvertTo<short?>("0"));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<short?>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<short>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<int>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<int?>("0"));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<int?>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<int>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<long>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<long?>("0"));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<long?>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<long>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<float>("0"));
+        Assert.AreEqual((float)0, defaultTypeConvertProvider.ConvertTo<float?>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<float>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<float?>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<decimal>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<decimal?>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<decimal>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<decimal?>(""));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<double>("0"));
+        Assert.AreEqual((double)0, defaultTypeConvertProvider.ConvertTo<double?>("0"));
+        Assert.AreEqual(0, defaultTypeConvertProvider.ConvertTo<double>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<double?>(""));
+        Assert.AreEqual(default,
+            defaultTypeConvertProvider.ConvertTo<DateTime>(default(DateTime).ToString(CultureInfo.InvariantCulture)));
+        Assert.AreEqual(default(DateTime),
+            defaultTypeConvertProvider.ConvertTo<DateTime?>(default(DateTime).ToString(CultureInfo.InvariantCulture)));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<DateTime?>(""));
+        Assert.AreEqual(default, defaultTypeConvertProvider.ConvertTo<DateTime>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<string?>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<string>(""));
+        Assert.AreEqual(bool.Parse("False"), defaultTypeConvertProvider.ConvertTo<bool>("False"));
+        Assert.AreEqual(bool.Parse("False"), defaultTypeConvertProvider.ConvertTo<bool?>("False"));
+        Assert.AreEqual(false, defaultTypeConvertProvider.ConvertTo<bool>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<bool?>(""));
+
+        var guid = Guid.NewGuid();
+
+        Assert.AreEqual(guid, defaultTypeConvertProvider.ConvertTo<Guid>(guid.ToString()));
+        Assert.AreEqual(guid, defaultTypeConvertProvider.ConvertTo<Guid?>(guid.ToString()));
+        Assert.AreEqual(Guid.Empty, defaultTypeConvertProvider.ConvertTo<Guid>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<Guid?>(""));
+
+        var date = DateTime.UtcNow;
+
+        Assert.AreEqual(date.ToString(CultureInfo.InvariantCulture),
+            defaultTypeConvertProvider.ConvertTo<DateTime>(date.ToString(CultureInfo.InvariantCulture))
+                .ToString(CultureInfo.InvariantCulture));
+
+        var convertToValue = defaultTypeConvertProvider.ConvertTo<DateTime?>(date.ToString(CultureInfo.InvariantCulture));
+        Assert.IsNotNull(convertToValue);
+        Assert.AreEqual(date.ToString(CultureInfo.InvariantCulture), ((DateTime)convertToValue).ToString(CultureInfo.InvariantCulture));
+        Assert.AreEqual(default(DateTime), defaultTypeConvertProvider.ConvertTo<DateTime>(""));
+        Assert.AreEqual(null, defaultTypeConvertProvider.ConvertTo<DateTime?>(""));
+    }
+
+    [TestMethod]
+    public void ConvertToByComplexType()
+    {
+        var list = new List<int>()
+        {
+            1,
+            3,
+            5
+        };
+        var value = list.ToJson();
+        var defaultTypeConvertProvider = new DefaultTypeConvertProvider();
+        var actualValue = defaultTypeConvertProvider.ConvertTo<List<int>>(value, new DefaultJsonDeserializer());
+        Assert.IsNotNull(actualValue);
+        Assert.AreEqual(3, actualValue.Count);
+        Assert.AreEqual(1, actualValue[0]);
+        Assert.AreEqual(3, actualValue[1]);
+        Assert.AreEqual(5, actualValue[2]);
+    }
+}
diff --git a/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/Masa.BuildingBlocks.Data.Tests.csproj b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/Masa.BuildingBlocks.Data.Tests.csproj
index bedef2897..d5afe2111 100644
--- a/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/Masa.BuildingBlocks.Data.Tests.csproj
+++ b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/Masa.BuildingBlocks.Data.Tests.csproj
@@ -22,6 +22,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\..\..\..\Contrib\Extensions\Masa.Contrib.Extensions.BackgroundJobs.Memory\Masa.Contrib.Extensions.BackgroundJobs.Memory.csproj" />
     <ProjectReference Include="..\..\Masa.BuildingBlocks.Data\Masa.BuildingBlocks.Data.csproj" />
   </ItemGroup>
 
diff --git a/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/_Imports.cs b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/_Imports.cs
index b18d87d12..e787bd452 100644
--- a/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/_Imports.cs
+++ b/src/BuildingBlocks/Data/Tests/Masa.BuildingBlocks.Data.Tests/_Imports.cs
@@ -2,4 +2,7 @@
 // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
 
 global using Masa.BuildingBlocks.Data.Tests.Infrastructure;
+global using Masa.Contrib.Data.Serialization.Json;
 global using Microsoft.VisualStudio.TestTools.UnitTesting;
+global using System.Globalization;
+global using DateTime = System.DateTime;
diff --git a/src/BuildingBlocks/Isolation/Masa.BuildingBlocks.Isolation/IConvertProvider.cs b/src/BuildingBlocks/Isolation/Masa.BuildingBlocks.Isolation/IConvertProvider.cs
deleted file mode 100644
index f9aed3942..000000000
--- a/src/BuildingBlocks/Isolation/Masa.BuildingBlocks.Isolation/IConvertProvider.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) MASA Stack All rights reserved.
-// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
-
-namespace Masa.BuildingBlocks.Isolation;
-
-public interface IConvertProvider
-{
-    object ChangeType(string value, Type conversionType);
-}
diff --git a/src/BuildingBlocks/Service/Masa.BuildingBlocks.Service.Caller/Masa.BuildingBlocks.Service.Caller.csproj b/src/BuildingBlocks/Service/Masa.BuildingBlocks.Service.Caller/Masa.BuildingBlocks.Service.Caller.csproj
index b2fb333ba..3ee1c943e 100644
--- a/src/BuildingBlocks/Service/Masa.BuildingBlocks.Service.Caller/Masa.BuildingBlocks.Service.Caller.csproj
+++ b/src/BuildingBlocks/Service/Masa.BuildingBlocks.Service.Caller/Masa.BuildingBlocks.Service.Caller.csproj
@@ -12,7 +12,6 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Data\Masa.BuildingBlocks.Data.Contracts\Masa.BuildingBlocks.Data.Contracts.csproj" />
     <ProjectReference Include="..\..\Data\Masa.BuildingBlocks.Data.Contracts\Masa.BuildingBlocks.Data.Contracts.csproj" />
     <ProjectReference Include="..\..\Data\Masa.BuildingBlocks.Data\Masa.BuildingBlocks.Data.csproj" />
     <ProjectReference Include="..\..\Exception\Masa.BuildingBlocks.Exceptions\Masa.BuildingBlocks.Exceptions.csproj" />
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DbContextTest.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DbContextTest.cs
index 270317d87..49217e8b1 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DbContextTest.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DbContextTest.cs
@@ -52,7 +52,10 @@ public void TestDbContext4()
     [TestMethod]
     public void TestDbContext5()
     {
-        Services.Configure<AppConfigOptions>(options => { options.DbConnectionString = MemoryConnectionString; });
+        Services.Configure<AppConfigOptions>(options =>
+        {
+            options.DbConnectionString = MemoryConnectionString;
+        });
         var dbContext = CreateDbContext<CustomDbContext5>(null);
         var student = GenerateStudent();
         dbContext.Set<Student>().Add(student);
@@ -76,7 +79,10 @@ public void TestDbContext6()
     [TestMethod]
     public void TestDbContext7()
     {
-        Services.Configure<AppConfigOptions>(options => { options.DbConnectionString = MemoryConnectionString; });
+        Services.Configure<AppConfigOptions>(options =>
+        {
+            options.DbConnectionString = MemoryConnectionString;
+        });
         var dbContext = CreateDbContext<CustomDbContext7>(null);
         var student = GenerateStudent();
         dbContext.Set<Student>().Add(student);
@@ -287,7 +293,10 @@ public async Task TestModifyConnectionString()
 
         IServiceProvider serviceProvider = default!;
         var dbContext =
-            await CreateDbContextAsync<CustomDbContext>(optionsBuilder => { optionsBuilder.UseSqlite(); }, sp => serviceProvider = sp);
+            await CreateDbContextAsync<CustomDbContext>(optionsBuilder =>
+            {
+                optionsBuilder.UseSqlite();
+            }, sp => serviceProvider = sp);
 
         var connectionStringProvider = serviceProvider.GetService<IConnectionStringProvider>();
         Assert.IsNotNull(connectionStringProvider);
@@ -338,20 +347,32 @@ public async Task TestUseConfigurationAndSpecify()
             .Build();
         Services.AddSingleton<IConfiguration>(configuration);
 
-        await CreateDbContextAsync<CustomDbContext>(optionsBuilder => { optionsBuilder.UseSqlite(); });
+        await CreateDbContextAsync<CustomDbContext>(optionsBuilder =>
+        {
+            optionsBuilder.UseSqlite();
+        });
         await Assert.ThrowsExceptionAsync<ArgumentException>(async () =>
         {
-            await CreateDbContextAsync<CustomDbContext2>(optionsBuilder => { optionsBuilder.UseSqlite(SqliteConnectionString); });
+            await CreateDbContextAsync<CustomDbContext2>(optionsBuilder =>
+            {
+                optionsBuilder.UseSqlite(SqliteConnectionString);
+            });
         });
     }
 
     [TestMethod]
     public async Task TestAddMultiDbContextAsync()
     {
-        Services.AddMasaDbContext<CustomDbContext>(dbContextBuilder => { dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString); });
+        Services.AddMasaDbContext<CustomDbContext>(dbContextBuilder =>
+        {
+            dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString);
+        });
         IServiceProvider serviceProvider = default!;
         var customDbContext2 = await CreateDbContextAsync<CustomDbContext2>(
-            dbContextBuilder => { dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString); }, sp => serviceProvider = sp);
+            dbContextBuilder =>
+            {
+                dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString);
+            }, sp => serviceProvider = sp);
 
         var customDbContext = serviceProvider.GetService<CustomDbContext>();
         Assert.IsNotNull(customDbContext);
@@ -572,6 +593,76 @@ public async Task TestAddOrUpdateOrDeleteWhenUserIdIsIntAsyncBySpecifyUserIdAndT
         Assert.AreNotEqual(inputModificationTime.AddDays(2), modificationTimeByUpdate);
     }
 
+    [TestMethod]
+    public async Task TestAddOrUpdateOrDeleteWhenUserIdIsStringAsync()
+    {
+        Services.Configure<AuditEntityOptions>(options => options.UserIdType = typeof(string));
+
+        var creator = "admin";
+        var customUserContext = new CustomUserContext(creator);
+        Services.AddSingleton<IUserContext>(customUserContext);
+
+        var connectionString = MemoryConnectionString;
+        var dbContext = await CreateDbContextAsync<CustomDbContext>(dbContextBuilder =>
+        {
+            dbContextBuilder
+                .UseInMemoryDatabase(connectionString)
+                .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)
+                .UseFilter();
+        });
+        Assert.IsNotNull(dbContext);
+        await dbContext.Set<People2>().AddAsync(new People2()
+        {
+            Name = "masa"
+        });
+        await dbContext.SaveChangesAsync();
+
+        var people = await dbContext.Set<People2>().FirstOrDefaultAsync();
+        Assert.IsNotNull(people);
+        Assert.AreEqual(creator, people.Creator);
+        Assert.AreEqual(creator, people.Modifier);
+    }
+
+    [TestMethod]
+    public async Task TestAddOrUpdateOrDeleteWhenUserIdIsGuidAsync()
+    {
+        Services.Configure<AuditEntityOptions>(options => options.UserIdType = typeof(Guid));
+
+        var customUserContext = new CustomUserContext("");
+        Services.AddSingleton<IUserContext>(customUserContext);
+
+        var connectionString = MemoryConnectionString;
+        var dbContext = await CreateDbContextAsync<CustomDbContext>(dbContextBuilder =>
+        {
+            dbContextBuilder
+                .UseInMemoryDatabase(connectionString)
+                .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)
+                .UseFilter();
+        });
+        Assert.IsNotNull(dbContext);
+        await dbContext.Set<People>().AddAsync(new People()
+        {
+            Name = "masa"
+        });
+        await dbContext.SaveChangesAsync();
+
+        var people = await dbContext.Set<People>().AsTracking().FirstOrDefaultAsync();
+        Assert.IsNotNull(people);
+        Assert.AreEqual(null, people.Creator);
+        Assert.AreEqual(null, people.Modifier);
+
+        var creator = Guid.NewGuid();
+        customUserContext.SetUserId(creator.ToString());
+
+        dbContext.Set<People>().Update(people);
+        await dbContext.SaveChangesAsync();
+
+        var peopleByUpdate = await dbContext.Set<People>().AsTracking().FirstOrDefaultAsync();
+        Assert.IsNotNull(peopleByUpdate);
+        Assert.AreEqual(null, peopleByUpdate.Creator);
+        Assert.AreEqual(creator, peopleByUpdate.Modifier);
+    }
+
     #endregion
 
     #region Test Model Mapping
@@ -579,7 +670,10 @@ public async Task TestAddOrUpdateOrDeleteWhenUserIdIsIntAsyncBySpecifyUserIdAndT
     [TestMethod]
     public void TestCustomTableName()
     {
-        var dbContext = CreateDbContext<CustomDbContext>(dbContext => { dbContext.UseInMemoryDatabase(MemoryConnectionString); });
+        var dbContext = CreateDbContext<CustomDbContext>(dbContext =>
+        {
+            dbContext.UseInMemoryDatabase(MemoryConnectionString);
+        });
         var entityTableName = dbContext.Model.FindEntityType(typeof(Student))?.GetTableName();
 
         Assert.AreEqual("masa_students", entityTableName);
@@ -592,7 +686,10 @@ public void TestCustomTableName()
     [TestMethod]
     public void TestQueryTrackingBehaviorByDefault()
     {
-        var dbContext = CreateDbContext<CustomDbContext>(dbContext => { dbContext.UseInMemoryDatabase(MemoryConnectionString); });
+        var dbContext = CreateDbContext<CustomDbContext>(dbContext =>
+        {
+            dbContext.UseInMemoryDatabase(MemoryConnectionString);
+        });
         Assert.AreEqual(QueryTrackingBehavior.NoTracking, dbContext.ChangeTracker.QueryTrackingBehavior);
     }
 
@@ -628,4 +725,5 @@ public void TestQueryTrackingBehaviorByUseQueryTrackingBehavior()
     }
 
     #endregion
+
 }
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DefaultTypeAndDefaultValueProviderTest.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DefaultTypeAndDefaultValueProviderTest.cs
new file mode 100644
index 000000000..6b7ce8d63
--- /dev/null
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DefaultTypeAndDefaultValueProviderTest.cs
@@ -0,0 +1,85 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Contrib.Data.EFCore.Tests;
+
+[TestClass]
+public class DefaultTypeAndDefaultValueProviderTest
+{
+    [TestMethod]
+    public void TestDefaultValues()
+    {
+        var defaultValueProvider = new DefaultTypeAndDefaultValueProvider();
+        defaultValueProvider.TryAdd(typeof(short));
+        defaultValueProvider.TryAdd(typeof(short?));
+        defaultValueProvider.TryAdd(typeof(int));
+        defaultValueProvider.TryAdd(typeof(int?));
+        defaultValueProvider.TryAdd(typeof(long));
+        defaultValueProvider.TryAdd(typeof(long?));
+        defaultValueProvider.TryAdd(typeof(float));
+        defaultValueProvider.TryAdd(typeof(float?));
+        defaultValueProvider.TryAdd(typeof(decimal));
+        defaultValueProvider.TryAdd(typeof(decimal?));
+        defaultValueProvider.TryAdd(typeof(double));
+        defaultValueProvider.TryAdd(typeof(double?));
+        defaultValueProvider.TryAdd(typeof(Guid));
+        defaultValueProvider.TryAdd(typeof(Guid?));
+        defaultValueProvider.TryAdd(typeof(DateTime));
+        defaultValueProvider.TryAdd(typeof(DateTime?));
+        defaultValueProvider.TryAdd(typeof(string));
+        defaultValueProvider.TryAdd(typeof(bool));
+        defaultValueProvider.TryAdd(typeof(bool?));
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(short), out var defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(short?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(int), out defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(int?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(long), out defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(long?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(float), out defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(float?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(decimal), out defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(decimal?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(double), out defaultValue));
+        Assert.AreEqual("0", defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(double?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(Guid), out defaultValue));
+        Assert.AreEqual(Guid.Empty.ToString(), defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(Guid?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(string), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(bool), out defaultValue));
+        Assert.AreEqual(default(bool).ToString(), defaultValue);
+
+        Assert.AreEqual(true, defaultValueProvider.TryGet(typeof(bool?), out defaultValue));
+        Assert.AreEqual(null, defaultValue);
+
+    }
+}
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Entities/People.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Entities/People.cs
new file mode 100644
index 000000000..b8b3d7c92
--- /dev/null
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Entities/People.cs
@@ -0,0 +1,14 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Contrib.Data.EFCore.Tests.Entities;
+
+public class People : FullAggregateRoot<Guid, Guid?>
+{
+    public string Name { get; set; }
+}
+
+public class People2 : FullAggregateRoot<Guid, string?>
+{
+    public string Name { get; set; }
+}
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Infrastructure/CustomDbContext.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Infrastructure/CustomDbContext.cs
index 9928a351d..b06d41041 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Infrastructure/CustomDbContext.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Infrastructure/CustomDbContext.cs
@@ -20,6 +20,8 @@ protected override void OnModelCreatingExecuting(ModelBuilder modelBuilder)
     {
         modelBuilder.InitializeStudentConfiguration();
         modelBuilder.Entity<User>();
+        modelBuilder.Entity<People>();
+        modelBuilder.Entity<People2>();
     }
 }
 
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/DefaultTypeAndDefaultValueProvider.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/DefaultTypeAndDefaultValueProvider.cs
new file mode 100644
index 000000000..f1481f4d9
--- /dev/null
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/DefaultTypeAndDefaultValueProvider.cs
@@ -0,0 +1,27 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+public class DefaultTypeAndDefaultValueProvider : ITypeAndDefaultValueProvider
+{
+    private readonly MemoryCache<Type, string?> _typeAndDefaultValues;
+
+    public DefaultTypeAndDefaultValueProvider()
+    {
+        _typeAndDefaultValues = new();
+        _typeAndDefaultValues.AddOrUpdate(typeof(string), _ => null);
+    }
+
+    public bool TryAdd(Type type)
+    {
+        return _typeAndDefaultValues.TryAdd(type, t => Activator.CreateInstance(t)?.ToString());
+    }
+
+    public bool TryGet(Type type, [NotNullWhen(true)] out string? defaultValue)
+    {
+        return _typeAndDefaultValues.TryGet(type, out defaultValue);
+    }
+}
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SaveChangeFilter.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SaveChangeFilter.cs
index 065f6ec26..a97ed8b30 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SaveChangeFilter.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SaveChangeFilter.cs
@@ -10,13 +10,21 @@ public class SaveChangeFilter<TDbContext, TUserId> : ISaveChangesFilter<TDbConte
 {
     private readonly Type _userIdType;
     private readonly IUserContext? _userContext;
+    private readonly ITypeAndDefaultValueProvider _typeAndDefaultValueProvider;
+    private readonly ITypeConvertProvider _typeConvertProvider;
 
-    public SaveChangeFilter(IUserContext? userContext = null)
+    public SaveChangeFilter(
+        IUserContext? userContext = null,
+        ITypeAndDefaultValueProvider? typeAndDefaultValueProvider = null,
+        ITypeConvertProvider? typeConvertProvider = null)
     {
         _userIdType = typeof(TUserId);
         _userContext = userContext;
-        Masa.Contrib.Data.EFCore.TypeExtensions.TypeAndDefaultValues.TryAdd(_userIdType, type => Activator.CreateInstance(type)?.ToString());
-        Masa.Contrib.Data.EFCore.TypeExtensions.TypeAndDefaultValues.TryAdd(typeof(DateTime), type => Activator.CreateInstance(type)?.ToString());
+        _typeAndDefaultValueProvider = typeAndDefaultValueProvider ?? new DefaultTypeAndDefaultValueProvider();
+        _typeConvertProvider = typeConvertProvider ?? new DefaultTypeConvertProvider();
+
+        _typeAndDefaultValueProvider.TryAdd(_userIdType);
+        _typeAndDefaultValueProvider.TryAdd(typeof(DateTime));
     }
 
     public void OnExecuting(ChangeTracker changeTracker)
@@ -25,8 +33,8 @@ public void OnExecuting(ChangeTracker changeTracker)
 
         var userId = GetUserId(_userContext?.UserId);
 
-        var defaultUserId = Masa.Contrib.Data.EFCore.TypeExtensions.TypeAndDefaultValues[_userIdType];
-        var defaultDateTime = Masa.Contrib.Data.EFCore.TypeExtensions.TypeAndDefaultValues[typeof(DateTime)];
+        _typeAndDefaultValueProvider.TryGet(_userIdType, out string? defaultUserId);
+        _typeAndDefaultValueProvider.TryGet(typeof(DateTime), out string? defaultDateTime);
 
         foreach (var entity in changeTracker.Entries()
                      .Where(entry => entry.State == EntityState.Added || entry.State == EntityState.Modified))
@@ -71,26 +79,30 @@ private static void AuditEntityHandlerByAdded(EntityEntry entity, object? userId
 
         if (IsDefault(entity.CurrentValues[nameof(IAuditEntity<TUserId>.CreationTime)], defaultDateTime))
         {
-            entity.CurrentValues[nameof(IAuditEntity<TUserId>.CreationTime)] = DateTime.UtcNow; //The current time to change to localization after waiting for localization
+            entity.CurrentValues[nameof(IAuditEntity<TUserId>.CreationTime)] =
+                DateTime.UtcNow; //The current time to change to localization after waiting for localization
         }
 
         if (IsDefault(entity.CurrentValues[nameof(IAuditEntity<TUserId>.ModificationTime)], defaultDateTime))
         {
-            entity.CurrentValues[nameof(IAuditEntity<TUserId>.ModificationTime)] = DateTime.UtcNow; //The current time to change to localization after waiting for localization
+            entity.CurrentValues[nameof(IAuditEntity<TUserId>.ModificationTime)] =
+                DateTime.UtcNow; //The current time to change to localization after waiting for localization
         }
     }
 
     private static bool IsDefault(object? value, string? defaultValue)
         => value == null || value.ToString() == defaultValue;
 
+    /// <summary>
+    /// Get the current user id
+    /// Does not consider user id as DateTime type
+    /// </summary>
+    /// <returns></returns>
     private object? GetUserId(string? userId)
     {
-        if (userId == null)
+        if (string.IsNullOrWhiteSpace(userId))
             return null;
 
-        if (_userIdType == typeof(Guid))
-            return Guid.Parse(userId);
-
-        return Convert.ChangeType(userId, _userIdType);
+        return _typeConvertProvider.ConvertTo(userId, _userIdType);
     }
 }
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SoftDeleteSaveChangesFilter.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SoftDeleteSaveChangesFilter.cs
index e7a7f7caf..daecd6275 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SoftDeleteSaveChangesFilter.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Filters/SoftDeleteSaveChangesFilter.cs
@@ -13,16 +13,19 @@ public sealed class SoftDeleteSaveChangesFilter<TDbContext, TUserId> : ISaveChan
     private readonly IUserContext? _userContext;
     private readonly TDbContext _context;
     private readonly MasaDbContextOptions<TDbContext> _masaDbContextOptions;
+    private readonly ITypeConvertProvider _typeConvertProvider;
 
     public SoftDeleteSaveChangesFilter(
         MasaDbContextOptions<TDbContext> masaDbContextOptions,
         TDbContext dbContext,
-        IUserContext? userContext = null)
+        IUserContext? userContext = null,
+        ITypeConvertProvider? typeConvertProvider = null)
     {
         _userIdType = typeof(TUserId);
         _masaDbContextOptions = masaDbContextOptions;
         _context = dbContext;
         _userContext = userContext;
+        _typeConvertProvider = typeConvertProvider ?? new DefaultTypeConvertProvider();
     }
 
     public void OnExecuting(ChangeTracker changeTracker)
@@ -103,12 +106,9 @@ private void HandleDependent(object dependentEntry)
 
     private object? GetUserId(string? userId)
     {
-        if (userId == null)
+        if (string.IsNullOrWhiteSpace(userId))
             return null;
 
-        if (_userIdType == typeof(Guid))
-            return Guid.Parse(userId);
-
-        return Convert.ChangeType(userId, _userIdType);
+        return _typeConvertProvider.ConvertTo(userId, _userIdType);
     }
 }
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/ITypeAndDefaultValueProvider.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/ITypeAndDefaultValueProvider.cs
new file mode 100644
index 000000000..8781aec57
--- /dev/null
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/ITypeAndDefaultValueProvider.cs
@@ -0,0 +1,18 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+public interface ITypeAndDefaultValueProvider
+{
+    /// <summary>
+    /// Setting type and default value
+    /// </summary>
+    /// <param name="type"></param>
+    /// <returns></returns>
+    bool TryAdd(Type type);
+
+    bool TryGet(Type type, [NotNullWhen(true)] out string? defaultValue);
+}
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Extensions/DbContextExtensions.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Extensions/DbContextExtensions.cs
index 79d4b0443..5d09b89d8 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Extensions/DbContextExtensions.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Extensions/DbContextExtensions.cs
@@ -77,7 +77,10 @@ public static object CreateSaveChangesFilter<TDbContextImplementation>(
             var userIdType = serviceProvider.GetService<IOptions<AuditEntityOptions>>()?.Value.UserIdType ?? typeof(Guid);
             return typeof(SaveChangeFilter<,>).MakeGenericType(type, userIdType);
         });
-        return Activator.CreateInstance(saveChangeFilterType, serviceProvider.GetService<IUserContext>())!;
+        return Activator.CreateInstance(saveChangeFilterType,
+            serviceProvider.GetService<IUserContext>(),
+            serviceProvider.GetService<ITypeAndDefaultValueProvider>(),
+            serviceProvider.GetService<ITypeConvertProvider>())!;
     }
 
     #endregion
@@ -99,7 +102,8 @@ public static object CreateSoftDeleteSaveChangesFilter<TDbContextImplementation>
             softDeleteSaveChangesFilterType,
             serviceProvider.GetRequiredService<MasaDbContextOptions<TDbContextImplementation>>(),
             serviceProvider.GetRequiredService<TDbContextImplementation>(),
-            serviceProvider.GetService<IUserContext>())!;
+            serviceProvider.GetService<IUserContext>(),
+            serviceProvider.GetService<ITypeConvertProvider>())!;
     }
 
     #endregion
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Isolation/IsolationSaveChangesFilter.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Isolation/IsolationSaveChangesFilter.cs
index 0c5fc0772..faff0f2d2 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Isolation/IsolationSaveChangesFilter.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/Isolation/IsolationSaveChangesFilter.cs
@@ -11,13 +11,13 @@ internal class IsolationSaveChangesFilter<TDbContext, TTenantId> : ISaveChangesF
     where TTenantId : IComparable
 {
     private readonly IMultiTenantContext? _tenantContext;
-    private readonly IConvertProvider? _convertProvider;
+    private readonly ITypeConvertProvider _convertProvider;
     private readonly IMultiEnvironmentContext? _environmentContext;
 
     public IsolationSaveChangesFilter(IServiceProvider serviceProvider)
     {
         _tenantContext = serviceProvider.GetService<IMultiTenantContext>();
-        _convertProvider = serviceProvider.GetService<IConvertProvider>();
+        _convertProvider = serviceProvider.GetService<ITypeConvertProvider>() ?? new DefaultTypeConvertProvider();
         _environmentContext = serviceProvider.GetService<IMultiEnvironmentContext>();
     }
 
@@ -53,10 +53,10 @@ public void OnExecuting(ChangeTracker changeTracker)
 
     private object? GetTenantId()
     {
-        if (_tenantContext is { CurrentTenant: not null } && !string.IsNullOrEmpty(_tenantContext.CurrentTenant.Id))
+        if (_tenantContext is { CurrentTenant: not null } && !string.IsNullOrWhiteSpace(_tenantContext.CurrentTenant.Id))
         {
             ArgumentNullException.ThrowIfNull(_convertProvider, nameof(_convertProvider));
-            return _convertProvider.ChangeType(_tenantContext.CurrentTenant.Id, typeof(TTenantId));
+            return _convertProvider.ConvertTo(_tenantContext.CurrentTenant.Id, typeof(TTenantId));
         }
         return null;
     }
diff --git a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/TypeExtensions.cs b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/TypeExtensions.cs
index 247f25c99..9d87d377b 100644
--- a/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/TypeExtensions.cs
+++ b/src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore/Internal/TypeExtensions.cs
@@ -2,6 +2,7 @@
 // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
 
 [assembly: InternalsVisibleTo("Masa.Contrib.Isolation.EFCore")]
+
 // ReSharper disable once CheckNamespace
 
 namespace Masa.Contrib.Data.EFCore;
@@ -13,6 +14,4 @@ internal static class TypeExtensions
     public static bool IsGenericInterfaceAssignableFrom(this Type genericType, Type type) =>
         type.IsConcrete() &&
         type.GetInterfaces().Any(t => t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericType);
-
-    public static readonly MemoryCache<Type, string?> TypeAndDefaultValues = new();
 }
diff --git a/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonDeserializer.cs b/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonDeserializer.cs
similarity index 64%
rename from src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonDeserializer.cs
rename to src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonDeserializer.cs
index d9bb741b7..7e1975cb4 100644
--- a/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonDeserializer.cs
+++ b/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonDeserializer.cs
@@ -1,9 +1,15 @@
 // Copyright (c) MASA Stack All rights reserved.
 // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
 
+[assembly: InternalsVisibleTo("Masa.BuildingBlocks.Data.Tests")]
+[assembly: InternalsVisibleTo("Masa.Contrib.Data.Serialization.Json.Tests")]
+[assembly: InternalsVisibleTo("Masa.Contrib.Extensions.BackgroundJobs.Memory")]
+
+// ReSharper disable once CheckNamespace
+
 namespace Masa.Contrib.Data.Serialization.Json;
 
-public class DefaultJsonDeserializer : IJsonDeserializer
+internal class DefaultJsonDeserializer : IJsonDeserializer
 {
     private readonly JsonSerializerOptions? _options;
 
diff --git a/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonSerializer.cs b/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonSerializer.cs
similarity index 63%
rename from src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonSerializer.cs
rename to src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonSerializer.cs
index 802328d99..3cd823c33 100644
--- a/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/DefaultJsonSerializer.cs
+++ b/src/Contrib/Data/Serialization/Masa.Contrib.Data.Serialization.Json/Internal/DefaultJsonSerializer.cs
@@ -1,9 +1,14 @@
 // Copyright (c) MASA Stack All rights reserved.
 // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
 
+[assembly: InternalsVisibleTo("Masa.Contrib.Data.Serialization.Json.Tests")]
+[assembly: InternalsVisibleTo("Masa.Contrib.Extensions.BackgroundJobs.Memory")]
+
+// ReSharper disable once CheckNamespace
+
 namespace Masa.Contrib.Data.Serialization.Json;
 
-public class DefaultJsonSerializer : IJsonSerializer
+internal class DefaultJsonSerializer : IJsonSerializer
 {
     private readonly JsonSerializerOptions? _options;
 
diff --git a/src/Contrib/Extensions/Masa.Contrib.Extensions.BackgroundJobs.Memory/Masa.Contrib.Extensions.BackgroundJobs.Memory.csproj b/src/Contrib/Extensions/Masa.Contrib.Extensions.BackgroundJobs.Memory/Masa.Contrib.Extensions.BackgroundJobs.Memory.csproj
index 388f9757d..7502d8c50 100644
--- a/src/Contrib/Extensions/Masa.Contrib.Extensions.BackgroundJobs.Memory/Masa.Contrib.Extensions.BackgroundJobs.Memory.csproj
+++ b/src/Contrib/Extensions/Masa.Contrib.Extensions.BackgroundJobs.Memory/Masa.Contrib.Extensions.BackgroundJobs.Memory.csproj
@@ -14,10 +14,10 @@
       <Compile Include="..\..\Data\IdGenerator\NormalGuid\Masa.Contrib.Data.IdGenerator.NormalGuid\NormalGuidGenerator.cs">
         <Link>NormalGuidGenerator.cs</Link>
       </Compile>
-      <Compile Include="..\..\Data\Serialization\Masa.Contrib.Data.Serialization.Json\DefaultJsonDeserializer.cs">
+      <Compile Include="..\..\Data\Serialization\Masa.Contrib.Data.Serialization.Json\Internal\DefaultJsonDeserializer.cs">
         <Link>DefaultJsonDeserializer.cs</Link>
       </Compile>
-      <Compile Include="..\..\Data\Serialization\Masa.Contrib.Data.Serialization.Json\DefaultJsonSerializer.cs">
+      <Compile Include="..\..\Data\Serialization\Masa.Contrib.Data.Serialization.Json\Internal\DefaultJsonSerializer.cs">
         <Link>DefaultJsonSerializer.cs</Link>
       </Compile>
     </ItemGroup>
diff --git a/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/ConvertProvider.cs b/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/ConvertProvider.cs
deleted file mode 100644
index 38f781c2d..000000000
--- a/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/ConvertProvider.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) MASA Stack All rights reserved.
-// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
-
-namespace Masa.Contrib.Isolation.MultiTenant;
-
-public class ConvertProvider : IConvertProvider
-{
-    public object ChangeType(string value, Type conversionType)
-    {
-        var result = conversionType == typeof(Guid) ? Guid.Parse(value) : Convert.ChangeType(value, conversionType);
-        return result;
-    }
-}
diff --git a/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/IsolationBuilderExtensions.cs b/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/IsolationBuilderExtensions.cs
index 5efbeb110..9aec509ca 100644
--- a/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/IsolationBuilderExtensions.cs
+++ b/src/Contrib/Isolation/Masa.Contrib.Isolation.MultiTenant/IsolationBuilderExtensions.cs
@@ -29,7 +29,6 @@ public static IIsolationBuilder UseMultiTenant(this IIsolationBuilder isolationB
             .AddHttpContextAccessor()
             .AddTransient(typeof(IEventMiddleware<>), typeof(IsolationEventMiddleware<>))
             .AddScoped<IIsolationMiddleware>(serviceProvider => new MultiTenantMiddleware(serviceProvider, tenantName, parserProviders));
-        isolationBuilder.Services.TryAddSingleton<IConvertProvider, ConvertProvider>();
         isolationBuilder.Services.TryAddScoped<MultiTenantContext>();
         isolationBuilder.Services.TryAddScoped(typeof(IMultiTenantContext), serviceProvider => serviceProvider.GetRequiredService<MultiTenantContext>());
         isolationBuilder.Services.TryAddScoped(typeof(IMultiTenantSetter), serviceProvider => serviceProvider.GetRequiredService<MultiTenantContext>());
diff --git a/src/Contrib/Isolation/Tests/Masa.Contrib.Isolation.MultiTenant.Tests/TenantTest.cs b/src/Contrib/Isolation/Tests/Masa.Contrib.Isolation.MultiTenant.Tests/TenantTest.cs
index cc0e14215..52a36a8b4 100644
--- a/src/Contrib/Isolation/Tests/Masa.Contrib.Isolation.MultiTenant.Tests/TenantTest.cs
+++ b/src/Contrib/Isolation/Tests/Masa.Contrib.Isolation.MultiTenant.Tests/TenantTest.cs
@@ -21,36 +21,4 @@ public void TestSetTenant()
         serviceProvider.GetRequiredService<IMultiTenantSetter>().SetTenant(tenant);
         Assert.IsTrue(serviceProvider.GetRequiredService<IMultiTenantContext>().CurrentTenant == tenant);
     }
-
-    [TestMethod]
-    public void TestChangeType()
-    {
-        var convertProvider = new ConvertProvider();
-        object result = convertProvider.ChangeType("1", typeof(int));
-        Assert.IsTrue(result.Equals(1));
-
-        var guid = Guid.NewGuid();
-        result = convertProvider.ChangeType(guid.ToString(), typeof(Guid));
-        Assert.IsTrue(result.Equals(guid));
-
-        var str = "dev";
-        result = convertProvider.ChangeType(str, typeof(string));
-        Assert.IsTrue(result.Equals(str));
-
-        result = convertProvider.ChangeType("1.1", typeof(decimal));
-        Assert.IsTrue(result.Equals((decimal)1.1));
-
-        result = convertProvider.ChangeType("1.2", typeof(float));
-        Assert.IsTrue(result.Equals((float)1.2));
-
-        result = convertProvider.ChangeType("1.3", typeof(double));
-        Assert.IsTrue(result.Equals(1.3d));
-
-        result = convertProvider.ChangeType("1", typeof(ushort));
-        Assert.IsTrue(result.Equals((ushort)1));
-
-        bool isProduction = true;
-        result = convertProvider.ChangeType(isProduction.ToString(), typeof(bool));
-        Assert.IsTrue(result.Equals(isProduction));
-    }
 }