diff --git a/.editorconfig b/.editorconfig index e0d8c1a2e..4edb0cdd2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,6 +20,16 @@ indent_size = 4 insert_final_newline = true charset = utf-8 +# Use Upper Case for constant fields +dotnet_naming_style.upper_case_style.capitalization = all_upper +dotnet_naming_style.upper_case_style.word_separator = _ +dotnet_naming_rule.constant_fields_should_be_upper_case.severity = warning +dotnet_naming_rule.constant_fields_should_be_upper_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_upper_case.style = upper_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + # Xml project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 diff --git a/Masa.Contrib.sln b/Masa.Contrib.sln index 401db2919..33a587ed1 100644 --- a/Masa.Contrib.sln +++ b/Masa.Contrib.sln @@ -68,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Ddd.Domain", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Ddd.Domain.Repository.EF", "src\Ddd\Masa.Contrib.Ddd.Domain.Repository.EF\Masa.Contrib.Ddd.Domain.Repository.EF.csproj", "{D375233D-8AAC-4234-BC0D-3D103C600C19}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Data.Contracts.EF", "src\Data\Masa.Contrib.Data.Contracts.EF\Masa.Contrib.Data.Contracts.EF.csproj", "{33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Ddd.Domain.Tests", "test\Masa.Contrib.Ddd.Domain.Tests\Masa.Contrib.Ddd.Domain.Tests.csproj", "{EAC0AA32-AE9B-4908-AF05-927B70A2B8A1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Ddd.Domain.Repository.EF.Tests", "test\Masa.Contrib.Ddd.Domain.Repository.EF.Tests\Masa.Contrib.Ddd.Domain.Repository.EF.Tests.csproj", "{E893C913-98A0-4BB3-A32B-3871BE3C5C53}" @@ -174,12 +172,36 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Configuration. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Data.Mapping", "src\BuildingBlocks\MASA.BuildingBlocks\src\Data\Masa.BuildingBlocks.Data.Mapping\Masa.BuildingBlocks.Data.Mapping.csproj", "{5A3338F1-9963-4CAC-85A3-7AB263CB15B0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.Contracts.EF", "src\Data\Masa.Contrib.Data.Contracts.EF\Masa.Contrib.Data.Contracts.EF.csproj", "{79D639DC-DDE4-45B1-884D-60B6908F528E}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.Mapping.Mapster.Tests", "test\Masa.Contrib.Data.Mapping.Mapster.Tests\Masa.Contrib.Data.Mapping.Mapster.Tests.csproj", "{834A12D0-FBED-45B3-86EA-5EA114C516B5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.BuildingBlocks.Data", "src\BuildingBlocks\MASA.BuildingBlocks\src\Data\Masa.BuildingBlocks.Data\Masa.BuildingBlocks.Data.csproj", "{C3451307-3743-4911-A401-7F28889703D3}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mapping", "Mapping", "{4AC23B67-52F9-44E5-9586-79A1DB73E6F7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore", "src\Data\Masa.Contrib.Data.EntityFrameworkCore\Masa.Contrib.Data.EntityFrameworkCore.csproj", "{87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.Mapping.Mapster", "src\Data\Mapping\Masa.Contrib.Data.Mapping.Mapster\Masa.Contrib.Data.Mapping.Mapster.csproj", "{D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.Cosmos", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.Cosmos\Masa.Contrib.Data.EntityFrameworkCore.Cosmos.csproj", "{3236F880-3DC0-450F-A14D-8C87525845DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.InMemory", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.InMemory\Masa.Contrib.Data.EntityFrameworkCore.InMemory.csproj", "{7E3FAE39-9CD1-46FA-A389-660008035DFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.MySql", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.MySql\Masa.Contrib.Data.EntityFrameworkCore.MySql.csproj", "{3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.Oracle", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.Oracle\Masa.Contrib.Data.EntityFrameworkCore.Oracle.csproj", "{A42CCFB3-1C30-4A58-AB28-E28736BF5378}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql\Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql.csproj", "{32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.PostgreSql", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.PostgreSql\Masa.Contrib.Data.EntityFrameworkCore.PostgreSql.csproj", "{A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.Sqlite", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.Sqlite\Masa.Contrib.Data.EntityFrameworkCore.Sqlite.csproj", "{C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.SqlServer", "src\Data\Masa.Contrib.Data.EntityFrameworkCore.SqlServer\Masa.Contrib.Data.EntityFrameworkCore.SqlServer.csproj", "{E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.EntityFrameworkCore.Tests", "test\Masa.Contrib.Data.EntityFrameworkCore.Tests\Masa.Contrib.Data.EntityFrameworkCore.Tests.csproj", "{E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -308,14 +330,6 @@ Global {D375233D-8AAC-4234-BC0D-3D103C600C19}.Release|Any CPU.Build.0 = Release|Any CPU {D375233D-8AAC-4234-BC0D-3D103C600C19}.Release|x64.ActiveCfg = Release|Any CPU {D375233D-8AAC-4234-BC0D-3D103C600C19}.Release|x64.Build.0 = Release|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Debug|Any CPU.Build.0 = Debug|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Debug|x64.ActiveCfg = Debug|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Debug|x64.Build.0 = Debug|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Release|Any CPU.ActiveCfg = Release|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Release|Any CPU.Build.0 = Release|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Release|x64.ActiveCfg = Release|Any CPU - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012}.Release|x64.Build.0 = Release|Any CPU {EAC0AA32-AE9B-4908-AF05-927B70A2B8A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAC0AA32-AE9B-4908-AF05-927B70A2B8A1}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAC0AA32-AE9B-4908-AF05-927B70A2B8A1}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -708,6 +722,102 @@ Global {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|Any CPU.Build.0 = Release|Any CPU {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|x64.ActiveCfg = Release|Any CPU {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145}.Release|x64.Build.0 = Release|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Debug|x64.ActiveCfg = Debug|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Debug|x64.Build.0 = Debug|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Release|Any CPU.Build.0 = Release|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Release|x64.ActiveCfg = Release|Any CPU + {79D639DC-DDE4-45B1-884D-60B6908F528E}.Release|x64.Build.0 = Release|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Debug|x64.Build.0 = Debug|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Release|Any CPU.Build.0 = Release|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Release|x64.ActiveCfg = Release|Any CPU + {C3451307-3743-4911-A401-7F28889703D3}.Release|x64.Build.0 = Release|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Debug|x64.ActiveCfg = Debug|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Debug|x64.Build.0 = Debug|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Release|Any CPU.Build.0 = Release|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Release|x64.ActiveCfg = Release|Any CPU + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8}.Release|x64.Build.0 = Release|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Debug|x64.Build.0 = Debug|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Release|Any CPU.Build.0 = Release|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Release|x64.ActiveCfg = Release|Any CPU + {3236F880-3DC0-450F-A14D-8C87525845DD}.Release|x64.Build.0 = Release|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Debug|x64.Build.0 = Debug|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Release|Any CPU.Build.0 = Release|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Release|x64.ActiveCfg = Release|Any CPU + {7E3FAE39-9CD1-46FA-A389-660008035DFB}.Release|x64.Build.0 = Release|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Debug|x64.Build.0 = Debug|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Release|Any CPU.Build.0 = Release|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Release|x64.ActiveCfg = Release|Any CPU + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A}.Release|x64.Build.0 = Release|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Debug|x64.ActiveCfg = Debug|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Debug|x64.Build.0 = Debug|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Release|Any CPU.Build.0 = Release|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Release|x64.ActiveCfg = Release|Any CPU + {A42CCFB3-1C30-4A58-AB28-E28736BF5378}.Release|x64.Build.0 = Release|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Debug|x64.ActiveCfg = Debug|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Debug|x64.Build.0 = Debug|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Release|Any CPU.Build.0 = Release|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Release|x64.ActiveCfg = Release|Any CPU + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48}.Release|x64.Build.0 = Release|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Debug|x64.ActiveCfg = Debug|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Debug|x64.Build.0 = Debug|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Release|Any CPU.Build.0 = Release|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Release|x64.ActiveCfg = Release|Any CPU + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1}.Release|x64.Build.0 = Release|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Debug|x64.Build.0 = Debug|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Release|Any CPU.Build.0 = Release|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Release|x64.ActiveCfg = Release|Any CPU + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA}.Release|x64.Build.0 = Release|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Debug|x64.ActiveCfg = Debug|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Debug|x64.Build.0 = Debug|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Release|Any CPU.Build.0 = Release|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Release|x64.ActiveCfg = Release|Any CPU + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE}.Release|x64.Build.0 = Release|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Debug|x64.ActiveCfg = Debug|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Debug|x64.Build.0 = Debug|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Release|Any CPU.Build.0 = Release|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Release|x64.ActiveCfg = Release|Any CPU + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -741,7 +851,6 @@ Global {EA23C277-97E4-4FBC-A53B-37048813E14F} = {DA885E64-C5E2-4C22-8C2A-26E68A593F29} {0FF64D9E-98D7-46B1-90FB-C0364C76D65A} = {21180442-A6A5-4239-A2AD-33FF5BB80E72} {D375233D-8AAC-4234-BC0D-3D103C600C19} = {21180442-A6A5-4239-A2AD-33FF5BB80E72} - {33F3BA7C-5BCB-4FB5-8E7E-2F7E29D92012} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} {EAC0AA32-AE9B-4908-AF05-927B70A2B8A1} = {13EDB361-AF88-4F89-B4AB-46622BCCBC37} {E893C913-98A0-4BB3-A32B-3871BE3C5C53} = {880E8263-AECC-4793-BD28-7CD03650D124} {761C3313-A669-465F-A384-9E118FCE4F89} = {38E6C400-90C0-493E-9266-C1602E229F1B} @@ -793,10 +902,22 @@ Global {97532A33-A591-4DF5-A2C0-72527B78ED82} = {38E6C400-90C0-493E-9266-C1602E229F1B} {165391A5-034E-4894-8084-8DF7D4AA7518} = {42DF7AAC-362C-48F4-B76A-BDEEEFF17CC9} {B8358ED1-C95A-4EC0-9756-FB32C931F204} = {9EEE31DA-3165-4CB3-AAE9-27CC3A4DE669} + {79D639DC-DDE4-45B1-884D-60B6908F528E} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {C3451307-3743-4911-A401-7F28889703D3} = {DC578D74-98F0-4F19-A230-CFA8DAEE0AF1} + {87DD354D-4D48-4918-ACDE-A7FF62FA0DD8} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {3236F880-3DC0-450F-A14D-8C87525845DD} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} {5A3338F1-9963-4CAC-85A3-7AB263CB15B0} = {DC578D74-98F0-4F19-A230-CFA8DAEE0AF1} {834A12D0-FBED-45B3-86EA-5EA114C516B5} = {38E6C400-90C0-493E-9266-C1602E229F1B} {4AC23B67-52F9-44E5-9586-79A1DB73E6F7} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} {D5EA7A25-0FD2-4545-9C1C-FF96E5E35145} = {4AC23B67-52F9-44E5-9586-79A1DB73E6F7} + {7E3FAE39-9CD1-46FA-A389-660008035DFB} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {3F19EBF9-A52E-4D64-8884-ECB16BE6A12A} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {A42CCFB3-1C30-4A58-AB28-E28736BF5378} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {32E7A2E4-EE91-4CEA-9EBD-9EFE1C03CF48} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {A50DC3FB-FD2A-4EF8-8333-4782DFFF0BF1} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {C8CE5B92-96A6-4FA1-9EEB-43A524DC54AA} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {E337EE1A-F6DB-4276-9CD6-DEBF224D7FDE} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} + {E1096C8E-6EC1-45CB-9CA6-0ADD830CAAA3} = {38E6C400-90C0-493E-9266-C1602E229F1B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {40383055-CC50-4600-AD9A-53C14F620D03} diff --git a/src/BuildingBlocks/MASA.BuildingBlocks b/src/BuildingBlocks/MASA.BuildingBlocks index 0abbf7f2a..d3b46ae3d 160000 --- a/src/BuildingBlocks/MASA.BuildingBlocks +++ b/src/BuildingBlocks/MASA.BuildingBlocks @@ -1 +1 @@ -Subproject commit 0abbf7f2aab54d8cbb7b8be7fb064c7749f30c05 +Subproject commit d3b46ae3d38a9892672725aec7e46af528cdeb01 diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/DataFilter.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/DataFilter.cs new file mode 100644 index 000000000..1e365be22 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/DataFilter.cs @@ -0,0 +1,71 @@ +// 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.Contracts.EF.DataFiltering; + +public class DataFilter : IDataFilter +{ + private readonly IServiceProvider _serviceProvider; + private readonly MemoryCache _cache; + + public DataFilter(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _cache = new(); + } + + public IDisposable Enable() where TFilter : class + => GetFilter().Enable(); + + public IDisposable Disable() where TFilter : class + => GetFilter().Disable(); + + public bool IsEnabled() where TFilter : class + => GetFilter().Enabled; + + private DataFilter GetFilter() + where TFilter : class + { + return (_cache.GetOrAdd( + typeof(TFilter), + _ => _serviceProvider.GetRequiredService>() + ) as DataFilter)!; + } +} + +public class DataFilter where TFilter : class +{ + private readonly AsyncLocal _filter; + + public DataFilter() => _filter = new AsyncLocal(); + + public bool Enabled + { + get + { + _filter.Value ??= new DataFilterState(true); + + return _filter.Value!.Enabled; + } + } + + public IDisposable Enable() + { + if (Enabled) + return NullDisposable.Instance; + + _filter.Value!.Enabled = true; + + return new DisposeAction(() => Disable()); + } + + public IDisposable Disable() + { + if (!Enabled) + return NullDisposable.Instance; + + _filter.Value!.Enabled = false; + + return new DisposeAction(() => Enable()); + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/SoftDeleteSaveChangesFilter.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/SoftDeleteSaveChangesFilter.cs new file mode 100644 index 000000000..d0bf90987 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/DataFiltering/SoftDeleteSaveChangesFilter.cs @@ -0,0 +1,65 @@ +// 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.Contracts.EF.DataFiltering; + +public class SoftDeleteSaveChangesFilter : ISaveChangesFilter where TDbContext : DbContext +{ + private readonly TDbContext _context; + private readonly MasaDbContextOptions _masaDbContextOptions; + + public SoftDeleteSaveChangesFilter(MasaDbContextOptions masaDbContextOptions, TDbContext dbContext) + { + _masaDbContextOptions = masaDbContextOptions; + _context = dbContext; + } + + public void OnExecuting(ChangeTracker changeTracker) + { + if (!_masaDbContextOptions.EnableSoftDelete) + return; + + changeTracker.DetectChanges(); + foreach (var entity in changeTracker.Entries().Where(entry => entry.State == EntityState.Deleted)) + { + if (entity.Entity is ISoftDelete) + { + HandleNavigationEntry(entity.Navigations.Where(n => !((IReadOnlyNavigation)n.Metadata).IsOnDependent)); + + entity.State = EntityState.Modified; + entity.CurrentValues[nameof(ISoftDelete.IsDeleted)] = true; + } + } + } + + protected virtual void HandleNavigationEntry(IEnumerable navigationEntries) + { + foreach (var navigationEntry in navigationEntries) + { + if (navigationEntry is CollectionEntry collectionEntry) + { + foreach (var dependentEntry in collectionEntry.CurrentValue ?? new List()) + { + HandleDependent(dependentEntry); + } + } + else + { + var dependentEntry = navigationEntry.CurrentValue; + if (dependentEntry != null) + { + HandleDependent(dependentEntry); + } + } + } + } + + protected virtual void HandleDependent(object dependentEntry) + { + var entityEntry = _context.Entry(dependentEntry); + entityEntry.State = EntityState.Modified; + + if (entityEntry.Entity is ISoftDelete) + entityEntry.CurrentValues[nameof(ISoftDelete.IsDeleted)] = true; + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DataFilterState.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DataFilterState.cs new file mode 100644 index 000000000..85364dfa7 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DataFilterState.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.Contracts.EF.Internal; + +internal class DataFilterState +{ + public bool Enabled { get; set; } + + public DataFilterState(bool enabled) + { + Enabled = enabled; + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DisposeAction.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DisposeAction.cs new file mode 100644 index 000000000..2c179575d --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/DisposeAction.cs @@ -0,0 +1,13 @@ +// 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.Contracts.EF.Internal; + +internal class DisposeAction : IDisposable +{ + private readonly Action _action; + + public DisposeAction(Action action) => _action = action; + + public void Dispose() => _action.Invoke(); +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/InstanceBuilder.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/InstanceBuilder.cs new file mode 100644 index 000000000..f4a9606fe --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/InstanceBuilder.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. + +namespace Masa.Contrib.Data.Contracts.EF.Internal; + +internal class InstanceBuilder +{ + internal delegate object InvokeDelegate(params object[] parameters); + + public static InvokeDelegate CreateInstanceDelegate(ConstructorInfo constructorInfo) + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + var parameterParameterExpression = Expression.Parameter(typeof(object[]), "parameters"); + + var parameterCast = new List(parameters.Length); + for (int i = 0; i < parameters.Length; i++) + { + var paramInfo = parameters[i]; + var valueObj = Expression.ArrayIndex(parameterParameterExpression, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + parameterCast.Add(valueCast); + } + NewExpression newExp = Expression.New(constructorInfo, parameterCast); + var lambdaExp = Expression.Lambda(newExp, parameterParameterExpression); + return lambdaExp.Compile(); + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/NullDisposable.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/NullDisposable.cs new file mode 100644 index 000000000..ad67a3355 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Internal/NullDisposable.cs @@ -0,0 +1,13 @@ +// 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.Contracts.EF.Internal; + +internal class NullDisposable : IDisposable +{ + public static NullDisposable Instance { get; } = new(); + + public void Dispose() + { + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Masa.Contrib.Data.Contracts.EF.csproj b/src/Data/Masa.Contrib.Data.Contracts.EF/Masa.Contrib.Data.Contracts.EF.csproj index 9878dff58..88848785d 100644 --- a/src/Data/Masa.Contrib.Data.Contracts.EF/Masa.Contrib.Data.Contracts.EF.csproj +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Masa.Contrib.Data.Contracts.EF.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -7,13 +7,13 @@ - - - + + - - + + + diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..997e47aa7 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,65 @@ +// 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.Contracts.EF; + +public static class MasaDbContextOptionsBuilderExtensions +{ + private static readonly List _types = new(); + + public static MasaDbContextOptionsBuilder UseFilter( + this MasaDbContextOptionsBuilder masaDbContextOptionsBuilder, + Action? options = null) + => masaDbContextOptionsBuilder.UseFilterCore(false, options); + + public static MasaDbContextOptionsBuilder UseTestFilter( + this MasaDbContextOptionsBuilder masaDbContextOptionsBuilder, + Action? options = null) + => masaDbContextOptionsBuilder.UseFilterCore(true, options); + + private static MasaDbContextOptionsBuilder UseFilterCore( + this MasaDbContextOptionsBuilder masaDbContextOptionsBuilder, + bool isTest, + Action? options = null) + { + var filterOptions = new FilterOptions(); + options?.Invoke(filterOptions); + + masaDbContextOptionsBuilder.Services.TryAddScoped(typeof(DataFilter<>)); + masaDbContextOptionsBuilder.Services.TryAddScoped(); + + if (filterOptions.EnableSoftDelete) masaDbContextOptionsBuilder.UseSoftDelete(isTest); + + return masaDbContextOptionsBuilder; + } + + private static void UseSoftDelete(this MasaDbContextOptionsBuilder masaDbContextOptionsBuilder, bool isTest = false) + { + if (!isTest) + { + if (_types.Any(type => masaDbContextOptionsBuilder.DbContextType == type)) + return; + + _types.Add(masaDbContextOptionsBuilder.DbContextType); + } + + var masaDbContextOptionsType = typeof(MasaDbContextOptions<>).MakeGenericType(masaDbContextOptionsBuilder.DbContextType); + var softDeleteSaveChangesFilterType = + typeof(SoftDeleteSaveChangesFilter<>).MakeGenericType(masaDbContextOptionsBuilder.DbContextType); + var constructorInfo = softDeleteSaveChangesFilterType.GetConstructors().FirstOrDefault()!; + var invokeDelegate = InstanceBuilder.CreateInstanceDelegate(constructorInfo); + + masaDbContextOptionsBuilder.Services.TryAdd( + new ServiceDescriptor(typeof(ISaveChangesFilter), + serviceProvider => + { + var instance = invokeDelegate.Invoke( + serviceProvider.GetRequiredService(masaDbContextOptionsType), + serviceProvider.GetRequiredService(masaDbContextOptionsBuilder.DbContextType)); + return instance; + }, + ServiceLifetime.Scoped)); + + masaDbContextOptionsBuilder.EnableSoftDelete = true; + } +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/Options/FilterOptions.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/Options/FilterOptions.cs new file mode 100644 index 000000000..d9d92f32d --- /dev/null +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/Options/FilterOptions.cs @@ -0,0 +1,15 @@ +// 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.Contracts.EF.Options; + +public class FilterOptions +{ + /// + /// enable soft delete + /// default: true + /// If you are sure that you do not need to use soft delete in the project, you can change to false + /// IDataFilter does not support ISoftDelete when soft delete is disabled + /// + public bool EnableSoftDelete { get; set; } = true; +} diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/README.md b/src/Data/Masa.Contrib.Data.Contracts.EF/README.md deleted file mode 100644 index 053f9760a..000000000 --- a/src/Data/Masa.Contrib.Data.Contracts.EF/README.md +++ /dev/null @@ -1,3 +0,0 @@ -[中](README.zh-CN.md) | EN - -## Contracts.EF \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/README.zh-CN.md b/src/Data/Masa.Contrib.Data.Contracts.EF/README.zh-CN.md deleted file mode 100644 index 2d2ba01b0..000000000 --- a/src/Data/Masa.Contrib.Data.Contracts.EF/README.zh-CN.md +++ /dev/null @@ -1,3 +0,0 @@ -中 | [EN](README.md) - -## Contracts.EF \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.Contracts.EF/_Imports.cs b/src/Data/Masa.Contrib.Data.Contracts.EF/_Imports.cs index e69de29bb..9ce9652cb 100644 --- a/src/Data/Masa.Contrib.Data.Contracts.EF/_Imports.cs +++ b/src/Data/Masa.Contrib.Data.Contracts.EF/_Imports.cs @@ -0,0 +1,17 @@ +// 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.Contracts.DataFiltering; +global using Masa.Contrib.Data.Contracts.EF.DataFiltering; +global using Masa.Contrib.Data.Contracts.EF.Internal; +global using Masa.Contrib.Data.Contracts.EF.Options; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Filters; +global using Masa.Utils.Caching.Memory; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.ChangeTracking; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using System.Linq.Expressions; +global using System.Reflection; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Internal/Parser.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Internal/Parser.cs new file mode 100644 index 000000000..f734442a5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Internal/Parser.cs @@ -0,0 +1,26 @@ +// 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.EntityFrameworkCore.Cosmos.Internal; + +internal static class Parser +{ + public static Dictionary ToDictionary(this string connectionString) + { + if (string.IsNullOrEmpty(connectionString)) + throw new ArgumentException("Cosmos: empty database connection string", nameof(connectionString)); + + Dictionary dictionary = new(); + foreach (var item in connectionString.Split(';')) + { + if (string.IsNullOrEmpty(item)) + continue; + + if (item.Split('=').Length != 2) + throw new ArgumentException("Cosmos: Bad database connection string"); + + dictionary.Add(item.Split('=')[0], item.Split('=')[1]); + } + return dictionary; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Masa.Contrib.Data.EntityFrameworkCore.Cosmos.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Masa.Contrib.Data.EntityFrameworkCore.Cosmos.csproj new file mode 100644 index 000000000..16a6815c5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/Masa.Contrib.Data.EntityFrameworkCore.Cosmos.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..42702bb35 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,105 @@ +// 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.EntityFrameworkCore.Cosmos; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseCosmos( + this MasaDbContextOptionsBuilder builder, + Action? cosmosOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var connectionStringProvider = serviceProvider.GetRequiredService(); + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var configurationDic = connectionStringProvider.GetConnectionString(name).ToDictionary(); + + if (!configurationDic.TryGetValue("Database", out string? databaseName)) + throw new ArgumentException("Cosmos: Bad database connection string, Failed to get [Database] name"); + + if (configurationDic.TryGetValue("ConnectionString", out string? connectionString)) + { + dbContextOptionsBuilder.UseCosmos(connectionString, databaseName, cosmosOptionsAction); + return; + } + + if (!configurationDic.TryGetValue("AccountKey", out string? accountKey) || + !configurationDic.TryGetValue("AccountEndpoint", out string? accountEndpoint)) + throw new ArgumentException( + "Cosmos: Bad database connection string, Failed to get [AccountKey] name or [AccountEndpoint] name"); + + dbContextOptionsBuilder.UseCosmos(accountEndpoint, accountKey, databaseName, cosmosOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseCosmos( + this MasaDbContextOptionsBuilder builder, + string accountEndpoint, + string accountKey, + string databaseName, + Action? cosmosOptionsAction = null) + => builder.UseCosmosCore(accountEndpoint, accountKey, databaseName, false, cosmosOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestCosmos( + this MasaDbContextOptionsBuilder builder, + string accountEndpoint, + string accountKey, + string databaseName, + Action? cosmosOptionsAction = null) + => builder.UseCosmosCore(accountEndpoint, accountKey, databaseName, true, cosmosOptionsAction); + + private static MasaDbContextOptionsBuilder UseCosmosCore( + this MasaDbContextOptionsBuilder builder, + string accountEndpoint, + string accountKey, + string databaseName, + bool isTest, + Action? cosmosOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseCosmos(accountEndpoint, accountKey, databaseName, cosmosOptionsAction); + return builder.UseCosmosCore($"AccountEndpoint={accountEndpoint};AccountKey={accountKey};Database={databaseName};"); + } + + public static MasaDbContextOptionsBuilder UseCosmos( + this MasaDbContextOptionsBuilder builder, + string connectionString, + string databaseName, + Action? cosmosOptionsAction = null) + => builder.UseCosmosCore(connectionString, databaseName, false, cosmosOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestCosmos( + this MasaDbContextOptionsBuilder builder, + string connectionString, + string databaseName, + Action? cosmosOptionsAction = null) + => builder.UseCosmosCore(connectionString, databaseName, true, cosmosOptionsAction); + + private static MasaDbContextOptionsBuilder UseCosmosCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + string databaseName, + bool isTest, + Action? cosmosOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseCosmos(connectionString, databaseName, cosmosOptionsAction); + return builder.UseCosmosCore($"{connectionString};Database={databaseName};", isTest); + } + + private static MasaDbContextOptionsBuilder UseCosmosCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.md new file mode 100644 index 000000000..de4021a5f --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.md @@ -0,0 +1,34 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.Cosmos + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Cosmos +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "AccountKey=AccountKey;AccountEndpoint=AccountEndpoint;Database=Database"// or "ConnectionString=ConnectionString;Database=Database" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos($"{accountEndpoint}",$"{accountKey}",$"{databaseName}")); +//builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos($"{connectionString}",$"{databaseName}")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.zh-CN.md new file mode 100644 index 000000000..fba9c83bf --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/README.zh-CN.md @@ -0,0 +1,34 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.Cosmos + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Cosmos +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "AccountKey=AccountKey;AccountEndpoint=AccountEndpoint;Database=Database"//或"ConnectionString=ConnectionString;Database=Database" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos($"{accountEndpoint}",$"{accountKey}",$"{databaseName}")); +//builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseCosmos($"{connectionString}",$"{databaseName}")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/_Imports.cs new file mode 100644 index 000000000..aab4fdb17 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Cosmos/_Imports.cs @@ -0,0 +1,9 @@ +// 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.Contrib.Data.EntityFrameworkCore.Cosmos.Internal; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/Masa.Contrib.Data.EntityFrameworkCore.InMemory.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/Masa.Contrib.Data.EntityFrameworkCore.InMemory.csproj new file mode 100644 index 000000000..e459bfda5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/Masa.Contrib.Data.EntityFrameworkCore.InMemory.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..26e934224 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,59 @@ +// 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.EntityFrameworkCore.InMemory; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseInMemoryDatabase( + this MasaDbContextOptionsBuilder builder, + Action? inMemoryOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseInMemoryDatabase( + connectionStringProvider.GetConnectionString(name), + inMemoryOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseInMemoryDatabase( + this MasaDbContextOptionsBuilder builder, + string databaseName, + Action? inMemoryOptionsAction = null) + => builder.UseInMemoryDatabaseCore(databaseName, false, inMemoryOptionsAction); + + public static MasaDbContextOptionsBuilder UseInMemoryTestDatabase( + this MasaDbContextOptionsBuilder builder, + string databaseName, + Action? inMemoryOptionsAction = null) + => builder.UseInMemoryDatabaseCore(databaseName, true, inMemoryOptionsAction); + + private static MasaDbContextOptionsBuilder UseInMemoryDatabaseCore( + this MasaDbContextOptionsBuilder builder, + string databaseName, + bool isTest, + Action? inMemoryOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseInMemoryDatabase(databaseName, inMemoryOptionsAction); + return builder.UseInMemoryDatabaseCore(databaseName, isTest); + } + + private static MasaDbContextOptionsBuilder UseInMemoryDatabaseCore( + this MasaDbContextOptionsBuilder builder, + string databaseName, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, databaseName); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.md new file mode 100644 index 000000000..b4293b730 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.InMemory + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.InMemory +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "identity" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseInMemoryDatabase()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseInMemoryDatabase("identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.zh-CN.md new file mode 100644 index 000000000..e28c40a0e --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.InMemory + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.InMemory +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "identity" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseInMemoryDatabase()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseInMemoryDatabase("identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/_Imports.cs new file mode 100644 index 000000000..4ec3d59f9 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.InMemory/_Imports.cs @@ -0,0 +1,8 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/Masa.Contrib.Data.EntityFrameworkCore.MySql.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/Masa.Contrib.Data.EntityFrameworkCore.MySql.csproj new file mode 100644 index 000000000..b78b4b775 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/Masa.Contrib.Data.EntityFrameworkCore.MySql.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..cfe8f851c --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,81 @@ +// 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.EntityFrameworkCore.MySql; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseMySQL( + this MasaDbContextOptionsBuilder builder, + Action? mySqlOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseMySQL( + connectionStringProvider.GetConnectionString(name), + mySqlOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseMySQL( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? mySqlOptionsAction = null) + => builder.UseMySQLCore(connectionString, false, mySqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestMySQL( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? mySqlOptionsAction = null) + => builder.UseMySQLCore(connectionString, true, mySqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseMySQLCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest, + Action? mySqlOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseMySQL(connectionString, mySqlOptionsAction); + return builder.UseMySQLCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseMySQL( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? mySqlOptionsAction = null) + => builder.UseMySQLCore(connection, false, mySqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestMySQL( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? mySqlOptionsAction = null) + => builder.UseMySQLCore(connection, true, mySqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseMySQLCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + bool isTest, + Action? mySqlOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseMySQL(connection, mySqlOptionsAction); + return builder.UseMySQLCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseMySQLCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.md new file mode 100644 index 000000000..967a2674f --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.MySql + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.MySql +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySQL()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySQL("Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.zh-CN.md new file mode 100644 index 000000000..f19a31586 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.MySql + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.MySql +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySQL()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySQL("Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/_Imports.cs new file mode 100644 index 000000000..686d0e2ae --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.MySql/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using MySql.EntityFrameworkCore.Infrastructure; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/Masa.Contrib.Data.EntityFrameworkCore.Oracle.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/Masa.Contrib.Data.EntityFrameworkCore.Oracle.csproj new file mode 100644 index 000000000..ef7797dc1 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/Masa.Contrib.Data.EntityFrameworkCore.Oracle.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..f9afa2a6b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,81 @@ +// 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.EntityFrameworkCore.Oracle; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseOracle( + this MasaDbContextOptionsBuilder builder, + Action? oracleOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseOracle( + connectionStringProvider.GetConnectionString(name), + oracleOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseOracle( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? oracleOptionsAction = null) + => builder.UseOracleCore(connectionString, false, oracleOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestOracle( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? oracleOptionsAction = null) + => builder.UseOracleCore(connectionString, true, oracleOptionsAction); + + private static MasaDbContextOptionsBuilder UseOracleCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest, + Action? oracleOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseOracle(connectionString, oracleOptionsAction); + return builder.UseOracleCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseOracle( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? oracleOptionsAction = null) + => builder.UseOracleCore(connection, false, oracleOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestOracle( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? oracleOptionsAction = null) + => builder.UseOracleCore(connection, true, oracleOptionsAction); + + private static MasaDbContextOptionsBuilder UseOracleCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + bool isTest = false, + Action? oracleOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseOracle(connection, oracleOptionsAction); + return builder.UseOracleCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseOracleCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.md new file mode 100644 index 000000000..97f1a32b5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.Oracle + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Oracle +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=MyOracleDB;Integrated Security=yes;" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseOracle()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseOracle("Data Source=MyOracleDB;Integrated Security=yes;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.zh-CN.md new file mode 100644 index 000000000..fd3328d07 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.Oracle + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Oracle +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=MyOracleDB;Integrated Security=yes;" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseOracle()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseOracle("Data Source=MyOracleDB;Integrated Security=yes;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/_Imports.cs new file mode 100644 index 000000000..b9b2a72c4 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Oracle/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Oracle.EntityFrameworkCore.Infrastructure; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql.csproj new file mode 100644 index 000000000..44591917a --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..3202486db --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,89 @@ +// 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.EntityFrameworkCore.Pomelo.MySql; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseMySql( + this MasaDbContextOptionsBuilder builder, + ServerVersion serverVersion, + Action? mySqlOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseMySql( + connectionStringProvider.GetConnectionString(name), + serverVersion, + mySqlOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseMySql( + this MasaDbContextOptionsBuilder builder, + string connectionString, + ServerVersion serverVersion, + Action? mySqlOptionsAction = null) + => builder.UseMySqlCore(connectionString, serverVersion, false, mySqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestMySql( + this MasaDbContextOptionsBuilder builder, + string connectionString, + ServerVersion serverVersion, + Action? mySqlOptionsAction = null) + => builder.UseMySqlCore(connectionString, serverVersion, true, mySqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseMySqlCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + ServerVersion serverVersion, + bool isTest, + Action? mySqlOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseMySql(connectionString, serverVersion, mySqlOptionsAction); + return builder.UseMySqlCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseMySql( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + ServerVersion serverVersion, + Action? mySqlOptionsAction = null) + => builder.UseMySqlCore(connection, serverVersion, false, mySqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestMySql( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + ServerVersion serverVersion, + Action? mySqlOptionsAction = null) + => builder.UseMySqlCore(connection, serverVersion, true, mySqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseMySqlCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + ServerVersion serverVersion, + bool isTest, + Action? mySqlOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseMySql(connection, serverVersion, mySqlOptionsAction); + return builder.UseMySqlCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseMySqlCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.md new file mode 100644 index 000000000..5d170c32a --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySql(Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"))); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySql("Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"))); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.zh-CN.md new file mode 100644 index 000000000..9113e1bb1 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySql(Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"))); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseMySql("Server=localhost;Database=identity;Uid=myUsername;Pwd=P@ssw0rd;", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"))); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/_Imports.cs new file mode 100644 index 000000000..4bc00c11b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql.csproj new file mode 100644 index 000000000..127784087 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..cdde02ef1 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,81 @@ +// 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.EntityFrameworkCore.PostgreSql; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseNpgsql( + this MasaDbContextOptionsBuilder builder, + Action? npgsqlOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseNpgsql( + connectionStringProvider.GetConnectionString(name), + npgsqlOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseNpgsql( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? npgsqlOptionsAction = null) + => builder.UseNpgsqlCore(connectionString, false, npgsqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestNpgsql( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? npgsqlOptionsAction = null) + => builder.UseNpgsqlCore(connectionString, true, npgsqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseNpgsqlCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest, + Action? npgsqlOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseNpgsql(connectionString, npgsqlOptionsAction); + return builder.UseNpgsqlCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseNpgsql( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? npgsqlOptionsAction = null) + => builder.UseNpgsqlCore(connection, false, npgsqlOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestNpgsql( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? npgsqlOptionsAction = null) + => builder.UseNpgsqlCore(connection, true, npgsqlOptionsAction); + + private static MasaDbContextOptionsBuilder UseNpgsqlCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + bool isTest, + Action? npgsqlOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseNpgsql(connection, npgsqlOptionsAction); + return builder.UseNpgsqlCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseNpgsqlCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.md new file mode 100644 index 000000000..88af3796e --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.PostgreSql + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.PostgreSql +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Host=myserver;Username=sa;Password=P@ssw0rd;Database=identity" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseNpgsql()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseNpgsql("Host=myserver;Username=sa;Password=P@ssw0rd;Database=identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.zh-CN.md new file mode 100644 index 000000000..99af59623 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.PostgreSql + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.PostgreSql +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Host=myserver;Username=sa;Password=P@ssw0rd;Database=identity" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseNpgsql()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseNpgsql("Host=myserver;Username=sa;Password=P@ssw0rd;Database=identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/_Imports.cs new file mode 100644 index 000000000..4e2f40e11 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.PostgreSql/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/Masa.Contrib.Data.EntityFrameworkCore.SqlServer.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/Masa.Contrib.Data.EntityFrameworkCore.SqlServer.csproj new file mode 100644 index 000000000..d3de9d140 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/Masa.Contrib.Data.EntityFrameworkCore.SqlServer.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..84c4eb556 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,81 @@ +// 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.EntityFrameworkCore.SqlServer; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseSqlServer( + this MasaDbContextOptionsBuilder builder, + Action? sqlServerOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseSqlServer( + connectionStringProvider.GetConnectionString(name), + sqlServerOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseSqlServer( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? sqlServerOptionsAction) + => builder.UseSqlServerCore(connectionString, false, sqlServerOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestSqlServer( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? sqlServerOptionsAction) + => builder.UseSqlServerCore(connectionString, true, sqlServerOptionsAction); + + private static MasaDbContextOptionsBuilder UseSqlServerCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest, + Action? sqlServerOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseSqlServer(connectionString, sqlServerOptionsAction); + return builder.UseSqlServerCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseSqlServer( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? sqlServerOptionsAction = null) + => builder.UseSqlServerCore(connection, false, sqlServerOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestSqlServer( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? sqlServerOptionsAction = null) + => builder.UseSqlServerCore(connection, true, sqlServerOptionsAction); + + private static MasaDbContextOptionsBuilder UseSqlServerCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + bool isTest, + Action? sqlServerOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseSqlServer(connection, sqlServerOptionsAction); + return builder.UseSqlServerCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseSqlServerCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.md new file mode 100644 index 000000000..3bb2223f2 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.SqlServer + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.SqlServer +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlServer()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlServer("server=localhost;uid=sa;pwd=P@ssw0rd;database=identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.zh-CN.md new file mode 100644 index 000000000..baca04f2f --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.SqlServer + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.SqlServer +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlServer()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlServer("server=localhost;uid=sa;pwd=P@ssw0rd;database=identity")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/_Imports.cs new file mode 100644 index 000000000..4bc00c11b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.SqlServer/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/Masa.Contrib.Data.EntityFrameworkCore.Sqlite.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/Masa.Contrib.Data.EntityFrameworkCore.Sqlite.csproj new file mode 100644 index 000000000..ef57aefe8 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/Masa.Contrib.Data.EntityFrameworkCore.Sqlite.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/MasaDbContextOptionsBuilderExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/MasaDbContextOptionsBuilderExtensions.cs new file mode 100644 index 000000000..09f838c5b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/MasaDbContextOptionsBuilderExtensions.cs @@ -0,0 +1,81 @@ +// 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.EntityFrameworkCore.Sqlite; + +public static class MasaDbContextOptionsBuilderExtensions +{ + public static MasaDbContextOptionsBuilder UseSqlite( + this MasaDbContextOptionsBuilder builder, + Action? sqliteOptionsAction = null) + { + builder.Builder = (serviceProvider, dbContextOptionsBuilder) => + { + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + var connectionStringProvider = serviceProvider.GetRequiredService(); + dbContextOptionsBuilder.UseSqlite( + connectionStringProvider.GetConnectionString(name), + sqliteOptionsAction); + }; + return builder; + } + + public static MasaDbContextOptionsBuilder UseSqlite( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? sqliteOptionsAction = null) + => builder.UseSqliteCore(connectionString, false, sqliteOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestSqlite( + this MasaDbContextOptionsBuilder builder, + string connectionString, + Action? sqliteOptionsAction = null) + => builder.UseSqliteCore(connectionString, true, sqliteOptionsAction); + + private static MasaDbContextOptionsBuilder UseSqliteCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest, + Action? sqliteOptionsAction) + { + builder.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseSqlite(connectionString, sqliteOptionsAction); + return builder.UseSqliteCore(connectionString, isTest); + } + + public static MasaDbContextOptionsBuilder UseSqlite( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? sqliteOptionsAction = null) + => builder.UseSqliteCore(connection, false, sqliteOptionsAction); + + public static MasaDbContextOptionsBuilder UseTestSqlite( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + Action? sqliteOptionsAction = null) + => builder.UseSqliteCore(connection, true, sqliteOptionsAction); + + private static MasaDbContextOptionsBuilder UseSqliteCore( + this MasaDbContextOptionsBuilder builder, + DbConnection connection, + bool isTest, + Action? sqliteOptionsAction = null) + { + builder.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseSqlite(connection, sqliteOptionsAction); + return builder.UseSqliteCore(connection.ConnectionString, isTest); + } + + private static MasaDbContextOptionsBuilder UseSqliteCore( + this MasaDbContextOptionsBuilder builder, + string connectionString, + bool isTest = false) + { + var dbConnectionOptions = builder.ServiceProvider.GetRequiredService>().CurrentValue; + var name = ConnectionStringNameAttribute.GetConnStringName(builder.DbContextType); + if (!isTest && dbConnectionOptions.ConnectionStrings.ContainsKey(name)) + throw new ArgumentException($"The [{builder.DbContextType.Name}] Database Connection String already exists"); + + dbConnectionOptions.TryAddConnectionString(name, connectionString); + return builder; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.md new file mode 100644 index 000000000..f67058740 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.md @@ -0,0 +1,33 @@ +[中](README.zh-CN.md) | EN + +## Masa.Contrib.Data.EntityFrameworkCore.Sqlite + +## Example: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Sqlite +``` + +##### Usage 1: + +1. Configure appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=c:\mydb.db;Version=3;Password=P@ssw0rd;" + } +} +``` + +2. Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlite()); +``` + +##### Usage 2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlite("Data Source=c:\mydb.db;Version=3;Password=P@ssw0rd;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.zh-CN.md new file mode 100644 index 000000000..bcef8183c --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/README.zh-CN.md @@ -0,0 +1,33 @@ +中 | [EN](README.md) + +## Masa.Contrib.Data.EntityFrameworkCore.Sqlite + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore.Sqlite +``` + +#### 用法1: + +1. 配置appsettings.json + +``` appsettings.json +{ + "ConnectionStrings": { + "DefaultConnection": "Data Source=c:\mydb.db;Version=3;Password=P@ssw0rd;" + } +} +``` + +2. 使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlite()); +``` + +#### 用法2: + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => optionsBuilder.UseFilter().UseSqlite("Data Source=c:\mydb.db;Version=3;Password=P@ssw0rd;")); +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/_Imports.cs new file mode 100644 index 000000000..4bc00c11b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore.Sqlite/_Imports.cs @@ -0,0 +1,9 @@ +// 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 Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using System.Data.Common; diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/DbConnectionStringProvider.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/DbConnectionStringProvider.cs similarity index 52% rename from src/Data/Masa.Contrib.Data.UoW.EF/DbConnectionStringProvider.cs rename to src/Data/Masa.Contrib.Data.EntityFrameworkCore/DbConnectionStringProvider.cs index de8408e60..243a36930 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/DbConnectionStringProvider.cs +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/DbConnectionStringProvider.cs @@ -1,7 +1,7 @@ -// Copyright (c) MASA Stack All rights reserved. +// 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.UoW.EF; +namespace Masa.Contrib.Data.EntityFrameworkCore; public class DbConnectionStringProvider : BaseDbConnectionStringProvider { @@ -9,8 +9,6 @@ public class DbConnectionStringProvider : BaseDbConnectionStringProvider public DbConnectionStringProvider(IOptionsMonitor options) => _options = options; - protected override List GetDbContextOptionsList() - { - return new() { new(_options.CurrentValue.DefaultConnection) }; - } + protected override List GetDbContextOptionsList() + => _options.CurrentValue.ConnectionStrings.Select(item => new MasaDbContextConfigurationOptions(item.Value)).Distinct().ToList(); } diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/DefaultConnectionStringProvider.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/DefaultConnectionStringProvider.cs new file mode 100644 index 000000000..d351e6dce --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/DefaultConnectionStringProvider.cs @@ -0,0 +1,21 @@ +// 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.EntityFrameworkCore; + +public class DefaultConnectionStringProvider : IConnectionStringProvider +{ + private readonly IOptionsMonitor _options; + + public DefaultConnectionStringProvider(IOptionsMonitor options) => _options = options; + + public Task GetConnectionStringAsync(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) => Task.FromResult(GetConnectionString(name)); + + public string GetConnectionString(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) + { + if (string.IsNullOrEmpty(name)) + return _options.CurrentValue.ConnectionStrings.DefaultConnection; + + return _options.CurrentValue.ConnectionStrings.GetConnectionString(name); + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder.cs new file mode 100644 index 000000000..99dc3c4fb --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder.cs @@ -0,0 +1,23 @@ +// 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.EntityFrameworkCore; + +public abstract class EFDbContextOptionsBuilder +{ + public readonly DbContextOptionsBuilder DbContextOptionsBuilder; + + internal bool EnableSoftDelete { get; private set; } + + protected EFDbContextOptionsBuilder(DbContextOptions options, bool enableSoftDelete) + { + DbContextOptionsBuilder = new DbContextOptionsBuilder(options); + EnableSoftDelete = enableSoftDelete; + } + + public EFDbContextOptionsBuilder UseSoftDelete() + { + EnableSoftDelete = true; + return this; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder`.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder`.cs new file mode 100644 index 000000000..ab650aa58 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/EFDbContextOptionsBuilder`.cs @@ -0,0 +1,13 @@ +// 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.EntityFrameworkCore; + +public class EFDbContextOptionsBuilder : EFDbContextOptionsBuilder + where TDbContext : MasaDbContext, IMasaDbContext +{ + public EFDbContextOptionsBuilder() + : base(new DbContextOptions(), false) + { + } +} diff --git a/src/Isolation/Masa.Contrib.Isolation/Internal/Const.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Filters/ISaveChangesFilter.cs similarity index 51% rename from src/Isolation/Masa.Contrib.Isolation/Internal/Const.cs rename to src/Data/Masa.Contrib.Data.EntityFrameworkCore/Filters/ISaveChangesFilter.cs index 2c62b3694..ac9e73fc4 100644 --- a/src/Isolation/Masa.Contrib.Isolation/Internal/Const.cs +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Filters/ISaveChangesFilter.cs @@ -1,9 +1,9 @@ // 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.Internal; +namespace Masa.Contrib.Data.EntityFrameworkCore.Filters; -internal class Const +public interface ISaveChangesFilter { - public const string DEFAULT_SECTION = "ConnectionStrings"; + void OnExecuting(ChangeTracker changeTracker); } diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/IModelCreatingProvider.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/IModelCreatingProvider.cs new file mode 100644 index 000000000..0fa3ebcb5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/IModelCreatingProvider.cs @@ -0,0 +1,13 @@ +// 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.EntityFrameworkCore; + +public interface IModelCreatingProvider +{ + /// + /// For building DbContext models and their mappings + /// + /// + void Configure(ModelBuilder modelBuilder); +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Contrib.Data.EntityFrameworkCore.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Contrib.Data.EntityFrameworkCore.csproj new file mode 100644 index 000000000..da99ba079 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Contrib.Data.EntityFrameworkCore.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Utils.Data.EntityFrameworkCore.csproj b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Utils.Data.EntityFrameworkCore.csproj new file mode 100644 index 000000000..efd5302c9 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/Masa.Utils.Data.EntityFrameworkCore.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContext.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContext.cs new file mode 100644 index 000000000..8fae11b58 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContext.cs @@ -0,0 +1,155 @@ +// 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.EntityFrameworkCore; + +public abstract class MasaDbContext : DbContext, IMasaDbContext +{ + protected readonly IDataFilter? DataFilter; + protected readonly MasaDbContextOptions? Options; + + public MasaDbContext(MasaDbContextOptions options) : base(options) + { + Options = options; + DataFilter = options.ServiceProvider.GetService(); + } + + /// + /// Automatic filter soft delete data. + /// When you override this method,you should call base.. + /// + /// + /// + protected sealed override void OnModelCreating(ModelBuilder modelBuilder) + { + OnModelCreatingExecuting(modelBuilder); + + OnModelCreatingConfigureGlobalFilters(modelBuilder); + + // null when run dotnet ef cli + if (Options == null) + { + base.OnModelCreating(modelBuilder); + return; + } + + foreach (var provider in Options.ModelCreatingProviders) + provider.Configure(modelBuilder); + } + + /// + /// Use this method instead of OnModelCreating + /// + /// + protected virtual void OnModelCreatingExecuting(ModelBuilder modelBuilder) + { + + } + + protected virtual void OnModelCreatingConfigureGlobalFilters(ModelBuilder modelBuilder) + { + var methodInfo = typeof(MasaDbContext).GetMethod(nameof(ConfigureGlobalFilters), BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + methodInfo!.MakeGenericMethod(entityType.ClrType).Invoke(this, new object?[] { modelBuilder, entityType }); + } + } + + protected virtual void ConfigureGlobalFilters(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) + where TEntity : class + { + if (mutableEntityType.BaseType == null) + { + var filterExpression = CreateFilterExpression(); + if (filterExpression != null) + modelBuilder.Entity().HasQueryFilter(filterExpression); + } + } + + protected virtual Expression>? CreateFilterExpression() + where TEntity : class + { + Expression>? expression = null; + + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) + { + expression = entity => !IsSoftDeleteFilterEnabled || !EF.Property(entity, nameof(ISoftDelete.IsDeleted)); + } + + return expression; + } + + protected virtual bool IsSoftDeleteFilterEnabled + => (Options?.EnableSoftDelete ?? false) && (DataFilter?.IsEnabled() ?? false); + + /// + /// Automatic soft delete. + /// + /// + /// + public override int SaveChanges() => SaveChanges(true); + + public sealed override int SaveChanges(bool acceptAllChangesOnSuccess) + { + OnBeforeSaveChanges(); + return base.SaveChanges(acceptAllChangesOnSuccess); + } + + protected virtual void OnBeforeSaveChanges() + { + if (Options != null) + { + foreach (var filter in Options.SaveChangesFilters) + { + try + { + filter.OnExecuting(ChangeTracker); + } + catch (Exception ex) + { + throw new Exception("An error occured when intercept SaveChanges() or SaveChangesAsync()()", ex); + } + } + } + } + + /// + /// Automatic soft delete. + /// + /// + /// + /// + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) => SaveChangesAsync(true, cancellationToken); + + /// + /// Automatic soft delete. + /// + /// + /// + /// + /// + public sealed override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) + { + OnBeforeSaveChanges(); + return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } +} + +public abstract class MasaDbContext : MasaDbContext + where TDbContext : DbContext, IMasaDbContext +{ + public MasaDbContext(MasaDbContextOptions options) : base(options) + { + } + + protected override void OnModelCreatingConfigureGlobalFilters(ModelBuilder modelBuilder) + { + var methodInfo = typeof(MasaDbContext).GetMethod(nameof(ConfigureGlobalFilters), BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + methodInfo!.MakeGenericMethod(entityType.ClrType).Invoke(this, new object?[] { modelBuilder, entityType }); + } + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions.cs new file mode 100644 index 000000000..3794d591b --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions.cs @@ -0,0 +1,24 @@ +// 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.EntityFrameworkCore; + +public abstract class MasaDbContextOptions : DbContextOptions +{ + public readonly IServiceProvider ServiceProvider; + + public abstract IEnumerable ModelCreatingProviders { get; } + + /// + /// Can be used to intercept SaveChanges(Async) method + /// + public abstract IEnumerable SaveChangesFilters { get; } + + public bool EnableSoftDelete { get; } + + protected MasaDbContextOptions(IServiceProvider serviceProvider, bool enableSoftDelete) + { + ServiceProvider = serviceProvider; + EnableSoftDelete = enableSoftDelete; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptionsBuilder.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptionsBuilder.cs new file mode 100644 index 000000000..c886014cb --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptionsBuilder.cs @@ -0,0 +1,25 @@ +// 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.EntityFrameworkCore; + +public class MasaDbContextOptionsBuilder +{ + private IServiceProvider? _serviceProvider; + + public IServiceProvider ServiceProvider => _serviceProvider ??= Services.BuildServiceProvider(); + + public IServiceCollection Services { get; } + + public Type DbContextType { get; } + + public Action Builder { get; set; } = default!; + + public bool EnableSoftDelete { get; set; } + + public MasaDbContextOptionsBuilder(IServiceCollection services, Type dbContextType) + { + Services = services; + DbContextType = dbContextType; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions`.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions`.cs new file mode 100644 index 000000000..4d2c7c61e --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/MasaDbContextOptions`.cs @@ -0,0 +1,83 @@ +// 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.EntityFrameworkCore; + +public class MasaDbContextOptions : MasaDbContextOptions + where TContext : DbContext +{ + private readonly DbContextOptions _originOptions; + + public MasaDbContextOptions( + IServiceProvider serviceProvider, + DbContextOptions originOptions, + bool enableSoftDelete) : base(serviceProvider, enableSoftDelete) => _originOptions = originOptions; + + private IEnumerable? _modelCreatingProviders; + + /// + /// Can be used to filter data + /// + public override IEnumerable ModelCreatingProviders => _modelCreatingProviders ??= ServiceProvider.GetServices(); + + private IEnumerable? _saveChangesFilters; + + /// + /// Can be used to intercept SaveChanges(Async) method + /// + public override IEnumerable SaveChangesFilters => _saveChangesFilters ??= ServiceProvider.GetServices(); + + /// + /// + /// + public override Type ContextType => _originOptions.ContextType; + + /// + /// + /// + public override bool IsFrozen => _originOptions.IsFrozen; + + /// + /// + /// + public override IEnumerable Extensions => _originOptions.Extensions; + + /// + /// + /// + /// + /// + /// + public override DbContextOptions WithExtension(TExtension extension) + { + return _originOptions.WithExtension(extension); + } + + /// + /// + /// + /// + /// + public override TExtension FindExtension() + { + return _originOptions.GetExtension(); + } + + /// + /// + /// + public override void Freeze() + { + _originOptions.Freeze(); + } + + /// + /// + /// + /// + /// + public override TExtension GetExtension() + { + return _originOptions.GetExtension(); + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.md new file mode 100644 index 000000000..b462e4894 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.md @@ -0,0 +1,47 @@ +[中](README.zh-CN.md) | EN + +## Masa.Utils.Data.EntityFrameworkCore + +## Example: + +```c# +Install-Package Masa.Utils.Data.EntityFrameworkCore +Install-Package Microsoft.EntityFrameworkCore.SqlServer +``` + +#### Basic usage: + +Using MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => +{ + optionsBuilder.UseFilter();//enable filtering + optionsBuilder.DbContextOptionsBuilder.UseSqlServer("server=localhost;uid=sa;pwd=P@ssw0rd;database=identity"); +}); +``` + +Recommended usage: + +- [SqlServer](../Masa.Utils.Data.EntityFrameworkCore.SqlServer/README.md) +- [MySql](../Masa.Utils.Data.EntityFrameworkCore.MySql/README.md) +- [Pomelo.MySql](../Masa.Utils.Data.EntityFrameworkCore.Pomelo.MySql/README.md) +- [Sqlite](../Masa.Utils.Data.EntityFrameworkCore.Sqlite/README.md) +- [Cosmos](../Masa.Utils.Data.EntityFrameworkCore.Cosmos/README.md) +- [InMemory](../Masa.Utils.Data.EntityFrameworkCore.InMemory/README.md) +- [Oracle](../Masa.Utils.Data.EntityFrameworkCore.Oracle/README.md) +- [PostgreSql](../Masa.Utils.Data.EntityFrameworkCore.PostgreSql/README.md) + +#### data filter + +``` C# +public async Task GetAllAsync([FromServices] IRepository repository, [FromServices] IDataFilter dataFilter) +{ + // Temporarily disable soft delete filtering + using (dataFilter.Disable()) + { + var list = (await repository.GetListAsync()).ToList(); + return System.Text.Json.JsonSerializer.Serialize(list); + } +} +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.zh-CN.md b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.zh-CN.md new file mode 100644 index 000000000..00fd800be --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/README.zh-CN.md @@ -0,0 +1,48 @@ +中 | [EN](README.md) + +## Masa.Utils.Data.EntityFrameworkCore + +## 用例: + +```c# +Install-Package Masa.Contrib.Data.EntityFrameworkCore +Install-Package Masa.Contrib.Data.Contracts.EF +Install-Package Microsoft.EntityFrameworkCore.SqlServer//这里以SqlServer举例 +``` + +#### 基本用法: + +使用MasaDbContext + +``` C# +builder.Services.AddMasaDbContext(optionsBuilder => +{ + optionsBuilder.UseFilter();//启用过滤 + optionsBuilder.DbContextOptionsBuilder.UseSqlServer("server=localhost;uid=sa;pwd=P@ssw0rd;database=identity"); +}); +``` + +推荐用法: + +- [SqlServer](../Masa.Utils.Data.EntityFrameworkCore.SqlServer/README.zh-CN.md) +- [MySql](../Masa.Utils.Data.EntityFrameworkCore.MySql/README.zh-CN.md) +- [Pomelo.MySql](../Masa.Utils.Data.EntityFrameworkCore.Pomelo.MySql/README.zh-CN.md) +- [Sqlite](../Masa.Utils.Data.EntityFrameworkCore.Sqlite/README.zh-CN.md) +- [Cosmos](../Masa.Utils.Data.EntityFrameworkCore.Cosmos/README.zh-CN.md) +- [InMemory](../Masa.Utils.Data.EntityFrameworkCore.InMemory/README.zh-CN.md) +- [Oracle](../Masa.Utils.Data.EntityFrameworkCore.Oracle/README.zh-CN.md) +- [PostgreSql](../Masa.Utils.Data.EntityFrameworkCore.PostgreSql/README.zh-CN.md) + +#### 数据过滤器 + +``` C# +public async Task GetAllAsync([FromServices] IRepository repository, [FromServices] IDataFilter dataFilter) +{ + // 临时禁用软删除过滤 + using (dataFilter.Disable()) + { + var list = (await repository.GetListAsync()).ToList(); + return System.Text.Json.JsonSerializer.Serialize(list); + } +} +``` \ No newline at end of file diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/ServiceCollectionExtensions.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..d2d7b7bd5 --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/ServiceCollectionExtensions.cs @@ -0,0 +1,118 @@ +// 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.EntityFrameworkCore; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddMasaDbContext( + this IServiceCollection services, + Action? optionsBuilder = null, + ServiceLifetime contextLifetime = ServiceLifetime.Scoped, + ServiceLifetime optionsLifetime = ServiceLifetime.Scoped) + where TDbContextImplementation : MasaDbContext, IMasaDbContext + => services + .AddDbContext(contextLifetime, optionsLifetime) + .AddCoreServices(optionsBuilder, optionsLifetime); + + private static IServiceCollection AddCoreServices( + this IServiceCollection services, + Action? optionsBuilder, + ServiceLifetime optionsLifetime) + where TDbContextImplementation : MasaDbContext, IMasaDbContext + { + services.TryAddConfigure(); + + MasaDbContextOptionsBuilder masaBuilder = new MasaDbContextOptionsBuilder(services, typeof(TDbContextImplementation)); + optionsBuilder?.Invoke(masaBuilder); + return services.AddCoreServices((serviceProvider, efDbContextOptionsBuilder) => + { + if (masaBuilder.EnableSoftDelete) + efDbContextOptionsBuilder.UseSoftDelete(); + + masaBuilder.Builder.Invoke(serviceProvider, efDbContextOptionsBuilder.DbContextOptionsBuilder); + }, optionsLifetime); + } + + private static IServiceCollection AddCoreServices( + this IServiceCollection services, + Action? optionsBuilder, + ServiceLifetime optionsLifetime) + where TDbContextImplementation : MasaDbContext, IMasaDbContext + { + services.TryAddScoped(); + services.TryAddSingleton(); + + services.TryAdd( + new ServiceDescriptor( + typeof(MasaDbContextOptions), + serviceProvider => CreateMasaDbContextOptions(serviceProvider, optionsBuilder), + optionsLifetime)); + + services.TryAdd( + new ServiceDescriptor( + typeof(MasaDbContextOptions), + serviceProvider => serviceProvider.GetRequiredService>(), + optionsLifetime)); + return services; + } + + private static MasaDbContextOptions CreateMasaDbContextOptions( + IServiceProvider serviceProvider, + Action? optionsBuilder) + where TDbContextImplementation : MasaDbContext, IMasaDbContext + { + var efDbContextOptionsBuilder = new EFDbContextOptionsBuilder(); + optionsBuilder?.Invoke(serviceProvider, efDbContextOptionsBuilder); + + return CreateMasaDbContextOptions( + serviceProvider, + efDbContextOptionsBuilder.DbContextOptionsBuilder.Options, + efDbContextOptionsBuilder.EnableSoftDelete); + } + + private static MasaDbContextOptions CreateMasaDbContextOptions( + IServiceProvider serviceProvider, + DbContextOptions options, bool enableSoftDelete) + where TDbContextImplementation : MasaDbContext, IMasaDbContext + => new(serviceProvider, options, enableSoftDelete); + + private static IServiceCollection TryAddConfigure( + this IServiceCollection services) + where TOptions : class + => services.TryAddConfigure(ConnectionStrings.DEFAULT_SECTION); + + /// + /// Only consider using MasaConfiguration and database configuration using local configuration + /// When using MasaConfiguration and the database configuration is stored in ConfigurationAPI, you need to specify the mapping relationship in Configuration by yourself + /// + /// + /// + /// + /// + private static IServiceCollection TryAddConfigure( + this IServiceCollection services, + string sectionName) + where TOptions : class + { + services.AddOptions(); + var serviceProvider = services.BuildServiceProvider(); + IConfiguration? configuration = serviceProvider.GetService()?.GetConfiguration(SectionTypes.Local) ?? + serviceProvider.GetService(); + if (configuration == null) + return services; + + string name = Options.DefaultName; + var configurationSection = configuration.GetSection(sectionName); + if (!configurationSection.Exists()) + return services; + + services.TryAddSingleton>( + new ConfigurationChangeTokenSource(name, configuration)); + services.TryAddSingleton>(new NamedConfigureFromConfigurationOptions(name, + configuration, _ => + { + })); + return services; + } +} diff --git a/src/Data/Masa.Contrib.Data.EntityFrameworkCore/_Imports.cs b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/_Imports.cs new file mode 100644 index 000000000..dee832c0a --- /dev/null +++ b/src/Data/Masa.Contrib.Data.EntityFrameworkCore/_Imports.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. + +global using Masa.BuildingBlocks.Configuration; +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Contracts.DataFiltering; +global using Masa.BuildingBlocks.Data.Options; +global using Masa.Contrib.Data.EntityFrameworkCore.Filters; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.ChangeTracking; +global using Microsoft.EntityFrameworkCore.Infrastructure; +global using Microsoft.EntityFrameworkCore.Metadata; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Options; +global using System.Linq.Expressions; +global using System.Reflection; diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/DefaultConnectionStringProvider.cs b/src/Data/Masa.Contrib.Data.UoW.EF/DefaultConnectionStringProvider.cs index 9e51daaec..b6d21ebc7 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/DefaultConnectionStringProvider.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/DefaultConnectionStringProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// 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.UoW.EF; @@ -6,12 +6,12 @@ namespace Masa.Contrib.Data.UoW.EF; public class DefaultConnectionStringProvider : IConnectionStringProvider { private readonly IUnitOfWorkAccessor _unitOfWorkAccessor; - private readonly IOptionsSnapshot _options; + private readonly IOptionsMonitor _options; private readonly ILogger? _logger; public DefaultConnectionStringProvider( IUnitOfWorkAccessor unitOfWorkAccessor, - IOptionsSnapshot options, + IOptionsMonitor options, ILogger? logger = null) { _unitOfWorkAccessor = unitOfWorkAccessor; @@ -19,14 +19,15 @@ public DefaultConnectionStringProvider( _logger = logger; } - public Task GetConnectionStringAsync() => Task.FromResult(GetConnectionString()); + public Task GetConnectionStringAsync(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) => Task.FromResult(GetConnectionString(name)); - public string GetConnectionString() + public string GetConnectionString(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) { if (_unitOfWorkAccessor.CurrentDbContextOptions != null) return _unitOfWorkAccessor.CurrentDbContextOptions.ConnectionString; - var connectionString = _options.Value.DefaultConnection; + var connectionStrings = _options.CurrentValue.ConnectionStrings; + var connectionString = string.IsNullOrEmpty(name) ? connectionStrings.DefaultConnection : connectionStrings.GetConnectionString(name); if (string.IsNullOrEmpty(connectionString)) _logger?.LogError("Failed to get database connection string, please check whether the configuration of IOptionsSnapshot is abnormal"); diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/DispatcherOptionsExtensions.cs b/src/Data/Masa.Contrib.Data.UoW.EF/DispatcherOptionsExtensions.cs index d012b11d9..c04f6b1ed 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/DispatcherOptionsExtensions.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/DispatcherOptionsExtensions.cs @@ -10,9 +10,10 @@ public static IEventBusBuilder UseUoW( Action? optionsBuilder = null, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext { - eventBusBuilder.Services.UseUoW(nameof(eventBusBuilder.Services), optionsBuilder, disableRollbackOnFailure, useTransaction); + eventBusBuilder.Services.UseUoW(nameof(eventBusBuilder.Services), optionsBuilder, disableRollbackOnFailure, + useTransaction); return eventBusBuilder; } @@ -21,7 +22,7 @@ public static IDispatcherOptions UseUoW( Action? optionsBuilder = null, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext { options.Services.UseUoW(nameof(options.Services), optionsBuilder, disableRollbackOnFailure, useTransaction); return options; @@ -33,7 +34,7 @@ private static IServiceCollection UseUoW( Action? optionsBuilder = null, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext { if (services == null) throw new ArgumentNullException(paramName); @@ -45,7 +46,6 @@ private static IServiceCollection UseUoW( services.TryAddScoped(); services.TryAddSingleton>(); services.TryAddScoped(); - services.TryAddSingleton(); services.AddScoped(serviceProvider => new UnitOfWork(serviceProvider) { @@ -53,44 +53,12 @@ private static IServiceCollection UseUoW( UseTransaction = useTransaction }); if (services.All(service => service.ServiceType != typeof(MasaDbContextOptions))) - { - services.TryAddConfigure(Const.DEFAULT_SECTION); services.AddMasaDbContext(optionsBuilder); - } services.AddScoped(); return services; } - /// - /// Only consider using MasaConfiguration and database configuration using local configuration - /// When using MasaConfiguration and the database configuration is stored in ConfigurationAPI, you need to specify the mapping relationship in Configuration by yourself - /// - /// - /// - /// - /// - private static IServiceCollection TryAddConfigure( - this IServiceCollection services, - string sectionName) - where TOptions : class - { - IMasaConfiguration? masaConfiguration = services.BuildServiceProvider().GetService(); - if (masaConfiguration == null) - return services; - - string name = Options.DefaultName; - services.AddOptions(); - var configurationSection = masaConfiguration.GetConfiguration(SectionTypes.Local).GetSection(sectionName); - if (!configurationSection.Exists()) - return services; - - services.TryAddSingleton>( - new ConfigurationChangeTokenSource(name, configurationSection)); - services.TryAddSingleton>(new NamedConfigureFromConfigurationOptions(name, configurationSection, _ => { })); - return services; - } - private class UoWProvider { } diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/Internal/Const.cs b/src/Data/Masa.Contrib.Data.UoW.EF/Internal/Const.cs index a835594ff..7c0b9d177 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/Internal/Const.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/Internal/Const.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// 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.UoW.EF.Internal; diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/Masa.Contrib.Data.UoW.EF.csproj b/src/Data/Masa.Contrib.Data.UoW.EF/Masa.Contrib.Data.UoW.EF.csproj index 2da76e10f..350ff23e2 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/Masa.Contrib.Data.UoW.EF.csproj +++ b/src/Data/Masa.Contrib.Data.UoW.EF/Masa.Contrib.Data.UoW.EF.csproj @@ -7,15 +7,15 @@ - - + + diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs index 928a72a63..9a65a05d4 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs @@ -3,7 +3,7 @@ namespace Masa.Contrib.Data.UoW.EF; -public class UnitOfWork : IUnitOfWork where TDbContext : MasaDbContext +public class UnitOfWork : IUnitOfWork where TDbContext : MasaDbContext, IMasaDbContext { public IServiceProvider ServiceProvider { get; } diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkAccessor.cs b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkAccessor.cs index eb0814c88..9f25b6e13 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkAccessor.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkAccessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// 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.UoW.EF; diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkManager.cs b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkManager.cs index fdec51399..39b9a2ad0 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkManager.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWorkManager.cs @@ -1,9 +1,9 @@ -// Copyright (c) MASA Stack All rights reserved. +// 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.UoW.EF; -public class UnitOfWorkManager : IUnitOfWorkManager where TDbContext : MasaDbContext +public class UnitOfWorkManager : IUnitOfWorkManager where TDbContext : MasaDbContext, IMasaDbContext { private readonly IServiceProvider _serviceProvider; diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/_Imports.cs b/src/Data/Masa.Contrib.Data.UoW.EF/_Imports.cs index 42ce36d59..bfc1f48c3 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/_Imports.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/_Imports.cs @@ -1,14 +1,13 @@ // 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.Configuration; +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Options; global using Masa.BuildingBlocks.Data.UoW; -global using Masa.BuildingBlocks.Data.UoW.Options; global using Masa.BuildingBlocks.Dispatcher.Events; -global using Masa.Utils.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Storage; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Logging; @@ -16,5 +15,3 @@ global using System.Data.Common; global using System.Text.Json.Serialization; global using EntityState = Masa.BuildingBlocks.Data.UoW.EntityState; -global using DbContextOptions = Masa.BuildingBlocks.Data.UoW.Options.MasaDbContextConfigurationOptions; -global using Masa.Contrib.Data.UoW.EF.Internal; diff --git a/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs b/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs index ac366a67d..a85c819ec 100644 --- a/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs +++ b/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs @@ -126,7 +126,7 @@ public override async Task> GetListAsync( /// public override Task> GetPaginatedListAsync(int skip, int take, string sortField, bool isDescending, CancellationToken cancellationToken = default) - => Context.Set().OrderBy(sortField, isDescending).Skip(skip).Take(take).ToListAsync(cancellationToken: cancellationToken); + => Context.Set().OrderBy(sortField, isDescending).Skip(skip).Take(take).ToListAsync(cancellationToken); /// /// Get a paginated list after sorting according to skip and take @@ -159,7 +159,7 @@ public override Task> GetPaginatedListAsync( /// public override Task> GetPaginatedListAsync(Expression> predicate, int skip, int take, string sortField, bool isDescending = true, CancellationToken cancellationToken = default) - => Context.Set().Where(predicate).OrderBy(sortField, isDescending).Skip(skip).Take(take).ToListAsync(cancellationToken: cancellationToken); + => Context.Set().Where(predicate).OrderBy(sortField, isDescending).Skip(skip).Take(take).ToListAsync(cancellationToken); /// /// Get a paginated list after sorting by condition diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs index 5d7e3f684..7efd8b51f 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs @@ -19,3 +19,4 @@ global using System.Collections.Concurrent; global using System.Reflection; global using System.Text.Json.Serialization; +global using Masa.BuildingBlocks.Data; diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/DispatcherOptionsExtensions.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/DispatcherOptionsExtensions.cs index 02d3fee61..2031ad298 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/DispatcherOptionsExtensions.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/DispatcherOptionsExtensions.cs @@ -13,7 +13,7 @@ public static class DispatcherOptionsExtensions /// /// public static IDispatcherOptions UseEventLog( - this IDispatcherOptions options) where TDbContext : MasaDbContext + this IDispatcherOptions options) where TDbContext : MasaDbContext, IMasaDbContext { if (options.Services == null) throw new ArgumentNullException(nameof(options.Services)); diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.csproj b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.csproj index a92295422..e9c553948 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.csproj +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.csproj @@ -7,7 +7,6 @@ - @@ -15,6 +14,7 @@ + diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/_Imports.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/_Imports.cs index b1cf34ded..4abb3581e 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/_Imports.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF/_Imports.cs @@ -1,12 +1,13 @@ // 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.Dispatcher.Events; global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents.Logs; +global using Masa.Contrib.Data.EntityFrameworkCore; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Internal; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Internal.Options; -global using Masa.Utils.Data.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Metadata.Builders; diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DefaultConnectionStringProvider.cs b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DefaultConnectionStringProvider.cs deleted file mode 100644 index 22d40734f..000000000 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DefaultConnectionStringProvider.cs +++ /dev/null @@ -1,16 +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.UoW.EF; - -public class DefaultConnectionStringProvider : IConnectionStringProvider -{ - private readonly IIsolationDbConnectionStringProvider _isolationConnectionStringProvider; - - public DefaultConnectionStringProvider(IIsolationDbConnectionStringProvider isolationConnectionStringProvider) - => _isolationConnectionStringProvider = isolationConnectionStringProvider; - - public Task GetConnectionStringAsync() => _isolationConnectionStringProvider.GetConnectionStringAsync(); - - public string GetConnectionString() => _isolationConnectionStringProvider.GetConnectionString(); -} diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DispatcherOptionsExtensions.cs b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DispatcherOptionsExtensions.cs index 7e3868087..c7075b2c2 100644 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DispatcherOptionsExtensions.cs +++ b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/DispatcherOptionsExtensions.cs @@ -11,7 +11,7 @@ public static IEventBusBuilder UseIsolationUoW( Action? optionsBuilder, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext => eventBusBuilder.UseIsolationUoW(isolationBuilder, optionsBuilder, disableRollbackOnFailure, useTransaction); public static IEventBusBuilder UseIsolationUoW( @@ -20,7 +20,7 @@ public static IEventBusBuilder UseIsolationUoW( Action? optionsBuilder, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext where TKey : IComparable { eventBusBuilder.Services.UseIsolationUoW(); @@ -34,7 +34,7 @@ public static IDispatcherOptions UseIsolationUoW( Action? optionsBuilder, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext => options.UseIsolationUoW(isolationBuilder, optionsBuilder, disableRollbackOnFailure, useTransaction); public static IDispatcherOptions UseIsolationUoW( @@ -43,7 +43,7 @@ public static IDispatcherOptions UseIsolationUoW( Action? optionsBuilder, bool disableRollbackOnFailure = false, bool useTransaction = true) - where TDbContext : MasaDbContext + where TDbContext : MasaDbContext, IMasaDbContext where TKey : IComparable { options.Services.UseIsolationUoW(); @@ -52,19 +52,5 @@ public static IDispatcherOptions UseIsolationUoW( } private static void UseIsolationUoW(this IServiceCollection services) where TKey : IComparable - => services.UseIsolationUoW() - .TryAddEnumerable(new ServiceDescriptor(typeof(ISaveChangesFilter), typeof(IsolationSaveChangesFilter), ServiceLifetime.Scoped)); - - private static IServiceCollection UseIsolationUoW(this IServiceCollection services) - { - if (services.Any(service => service.ServiceType == typeof(IConnectionStringProvider))) - services.Replace(new ServiceDescriptor(typeof(IConnectionStringProvider), typeof(DefaultConnectionStringProvider), ServiceLifetime.Scoped)); - else - services.TryAddScoped(); - return services; - } - - private class IsolationUoWProvider - { - } + => services.TryAddEnumerable(new ServiceDescriptor(typeof(ISaveChangesFilter), typeof(IsolationSaveChangesFilter), ServiceLifetime.Scoped)); } diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/IsolationDbContext.cs b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/IsolationDbContext.cs index dadf28946..3a41a6fc7 100644 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/IsolationDbContext.cs +++ b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/IsolationDbContext.cs @@ -3,6 +3,24 @@ namespace Masa.Contrib.Isolation.UoW.EF; +/// +/// DbContext providing isolation +/// +/// tenant id type +/// +public abstract class IsolationDbContext : IsolationDbContext + where TKey : IComparable + where TDbContext : DbContext, IMasaDbContext +{ + protected IsolationDbContext(MasaDbContextOptions options) : base(options) + { + } +} + +/// +/// DbContext providing isolation +/// +/// tenant id type public abstract class IsolationDbContext : IsolationDbContext { protected IsolationDbContext(MasaDbContextOptions options) : base(options) diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.md b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.md index 1b51a1331..b00debb19 100644 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.md +++ b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.md @@ -6,6 +6,7 @@ Example: ```C# Install-Package Masa.Contrib.Isolation.UoW.EF +Install-Package Masa.Contrib.Data.Contracts.EF Install-Package Masa.Contrib.Isolation.MultiEnvironment // Environmental isolation Quote on demand Install-Package Masa.Contrib.Isolation.MultiTenant // Multi-tenant isolation On-demand reference Install-Package Masa.Utils.Data.EntityFrameworkCore.SqlServer @@ -15,21 +16,21 @@ Install-Package Masa.Utils.Data.EntityFrameworkCore.SqlServer ``` appsettings.json { "ConnectionStrings": { - "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity;", - "Isolations": [ - { - "TenantId": "*",// match all tenants - "Environment": "development", - "ConnectionString": "server=localhost,1672;uid=sa;pwd=P@ssw0rd;database=identity;", - "Score": 99 // When multiple environments are matched according to the conditions, the highest one is selected as the link address of the current DbContext according to the descending order of scores. The default Score is 100. - }, - { - "TenantId": "00000000-0000-0000-0000-000000000002", - "Environment": "development", - "ConnectionString": "server=localhost,1674;uid=sa;pwd=P@ssw0rd;database=identity;" - } - ] - } + "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity;" + }, + "IsolationConnectionStrings": [ + { + "TenantId": "*",//match all tenants + "Environment": "development", + "ConnectionString": "server=localhost,1434;uid=sa;pwd=P@ssw0rd;database=identity;", + "Score": 99 //When multiple environments are matched according to the conditions, the highest one is selected as the link address of the current DbContext according to the descending order of scores. The default Score is 100. + }, + { + "TenantId": "00000000-0000-0000-0000-000000000002", + "Environment": "development", + "ConnectionString": "server=localhost,1435;uid=sa;pwd=P@ssw0rd;database=identity;" + } + ] } ``` @@ -46,7 +47,7 @@ builder.Services.AddEventBus(eventBusBuilder => { eventBusBuilder.UseIsolationUoW( isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiTenant(),// Select usage environment or tenant isolation as needed - dbOptions => dbOptions.UseSqlServer()); + dbOptions => dbOptions.UseFilter().UseSqlServer()); }); ``` diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.zh-CN.md b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.zh-CN.md index d699472bf..d6e79f64b 100644 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.zh-CN.md +++ b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/README.zh-CN.md @@ -6,6 +6,7 @@ ```C# Install-Package Masa.Contrib.Isolation.UoW.EF +Install-Package Masa.Contrib.Data.Contracts.EF Install-Package Masa.Contrib.Isolation.MultiEnvironment // 环境隔离 按需引用 Install-Package Masa.Contrib.Isolation.MultiTenant // 多租户隔离 按需引用 Install-Package Masa.Utils.Data.EntityFrameworkCore.SqlServer @@ -15,21 +16,21 @@ Install-Package Masa.Utils.Data.EntityFrameworkCore.SqlServer ``` appsettings.json { "ConnectionStrings": { - "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity;", - "Isolations": [ - { - "TenantId": "*",//匹配所有租户 - "Environment": "development", - "ConnectionString": "server=localhost,1672;uid=sa;pwd=P@ssw0rd;database=identity;", - "Score": 99 //当根据条件匹配到多个环境时,根据分值降序选择其中最高的作为当前DbContext的链接地址,Score默认为100 - }, - { - "TenantId": "00000000-0000-0000-0000-000000000002", - "Environment": "development", - "ConnectionString": "server=localhost,1674;uid=sa;pwd=P@ssw0rd;database=identity;" - } - ] - } + "DefaultConnection": "server=localhost;uid=sa;pwd=P@ssw0rd;database=identity;" + }, + "IsolationConnectionStrings": [ + { + "TenantId": "*",//匹配所有租户 + "Environment": "development", + "ConnectionString": "server=localhost,1434;uid=sa;pwd=P@ssw0rd;database=identity;", + "Score": 99 //当根据条件匹配到多个环境时,根据分值降序选择其中最高的作为当前DbContext的链接地址,Score默认为100 + }, + { + "TenantId": "00000000-0000-0000-0000-000000000002", + "Environment": "development", + "ConnectionString": "server=localhost,1435;uid=sa;pwd=P@ssw0rd;database=identity;" + } + ] } ``` @@ -46,7 +47,7 @@ builder.Services.AddEventBus(eventBusBuilder => { eventBusBuilder.UseIsolationUoW( isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiTenant(),// 按需选择使用环境或者租户隔离 - dbOptions => dbOptions.UseSqlServer()); + dbOptions => dbOptions.UseFilter().UseSqlServer()); }); ``` diff --git a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/_Imports.cs b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/_Imports.cs index 8236526f1..0c716833b 100644 --- a/src/Isolation/Masa.Contrib.Isolation.UoW.EF/_Imports.cs +++ b/src/Isolation/Masa.Contrib.Isolation.UoW.EF/_Imports.cs @@ -1,14 +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.Dispatcher.Events; global using Masa.BuildingBlocks.Isolation; global using Masa.BuildingBlocks.Isolation.Environment; global using Masa.BuildingBlocks.Isolation.MultiTenant; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Filters; global using Masa.Contrib.Data.UoW.EF; global using Masa.Contrib.Isolation.UoW.EF.Internal; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Filters; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.ChangeTracking; global using Microsoft.Extensions.DependencyInjection; diff --git a/src/Isolation/Masa.Contrib.Isolation/DefaultIsolationConnectionStringProvider.cs b/src/Isolation/Masa.Contrib.Isolation/DefaultIsolationConnectionStringProvider.cs index 35e106cb2..c7d630b9c 100644 --- a/src/Isolation/Masa.Contrib.Isolation/DefaultIsolationConnectionStringProvider.cs +++ b/src/Isolation/Masa.Contrib.Isolation/DefaultIsolationConnectionStringProvider.cs @@ -3,7 +3,7 @@ namespace Masa.Contrib.Isolation; -public class DefaultDbIsolationConnectionStringProvider : IIsolationDbConnectionStringProvider +public class DefaultDbIsolationConnectionStringProvider : IConnectionStringProvider { private readonly IUnitOfWorkAccessor _unitOfWorkAccessor; private readonly IOptionsSnapshot _options; @@ -25,46 +25,54 @@ public DefaultDbIsolationConnectionStringProvider( _logger = logger; } - public Task GetConnectionStringAsync() => Task.FromResult(GetConnectionString()); + public Task GetConnectionStringAsync(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) => Task.FromResult(GetConnectionString(name)); - public string GetConnectionString() + public string GetConnectionString(string name = ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME) { if (_unitOfWorkAccessor.CurrentDbContextOptions != null) - return _unitOfWorkAccessor.CurrentDbContextOptions.ConnectionString; + return _unitOfWorkAccessor.CurrentDbContextOptions.ConnectionString; //todo: UnitOfWork does not currently support multi-context versions - Expression> condition = option => true; + Expression> condition = option + => name != ConnectionStrings.DEFAULT_CONNECTION_STRING_NAME ? option.Name == name : option.Name == name || option.Name == string.Empty; if (_tenantContext != null) { if (_tenantContext.CurrentTenant == null) - _logger?.LogDebug($"Tenant resolution failed, the currently used ConnectionString is [{nameof(_options.Value.DefaultConnection)}]"); + _logger?.LogDebug( + $"Tenant resolution failed, the currently used ConnectionString is [{nameof(_options.Value.ConnectionStrings.DefaultConnection)}]"); - condition = condition.And(option => option.TenantId == "*" || (_tenantContext.CurrentTenant != null && _tenantContext.CurrentTenant.Id.Equals(option.TenantId, StringComparison.CurrentCultureIgnoreCase))); + condition = condition.And(option => option.TenantId == "*" || (_tenantContext.CurrentTenant != null && + _tenantContext.CurrentTenant.Id.Equals(option.TenantId, StringComparison.CurrentCultureIgnoreCase))); } if (_environmentContext != null) { if (string.IsNullOrEmpty(_environmentContext.CurrentEnvironment)) { - _logger?.LogDebug($"Environment resolution failed, the currently used ConnectionString is [{nameof(_options.Value.DefaultConnection)}]"); + _logger?.LogDebug( + $"Environment resolution failed, the currently used ConnectionString is [{nameof(_options.Value.ConnectionStrings.DefaultConnection)}]"); } condition = condition.And(option - => option.Environment == "*" || option.Environment.Equals(_environmentContext.CurrentEnvironment, StringComparison.CurrentCultureIgnoreCase)); + => option.Environment == "*" || + option.Environment.Equals(_environmentContext.CurrentEnvironment, StringComparison.CurrentCultureIgnoreCase)); } string? connectionString; - var list = _options.Value.Isolations.Where(condition.Compile()).ToList(); + var list = _options.Value.IsolationConnectionStrings.Where(condition.Compile()).ToList(); if (list.Count >= 1) { connectionString = list.OrderByDescending(option => option.Score).Select(option => option.ConnectionString).FirstOrDefault()!; if (list.Count > 1) - _logger?.LogInformation("{Message}, Matches multiple available database link strings, the currently used ConnectionString is [{ConnectionString}]", GetMessage(), connectionString); + _logger?.LogInformation( + "{Message}, Matches multiple available database link strings, the currently used ConnectionString is [{ConnectionString}]", + GetMessage(), connectionString); } else { - connectionString = _options.Value.DefaultConnection; - _logger?.LogDebug("{Message}, the currently used ConnectionString is [{ConnectionString}]", GetMessage(), nameof(_options.Value.DefaultConnection)); + connectionString = _options.Value.ConnectionStrings.DefaultConnection; + _logger?.LogDebug("{Message}, the currently used ConnectionString is [{ConnectionString}]", GetMessage(), + nameof(_options.Value.ConnectionStrings.DefaultConnection)); } return SetConnectionString(connectionString); } @@ -72,7 +80,7 @@ public string GetConnectionString() private string SetConnectionString(string? connectionString = null) { _unitOfWorkAccessor.CurrentDbContextOptions = - new MasaDbContextConfigurationOptions(connectionString ?? _options.Value.DefaultConnection); + new MasaDbContextConfigurationOptions(connectionString ?? _options.Value.ConnectionStrings.DefaultConnection); return _unitOfWorkAccessor.CurrentDbContextOptions.ConnectionString; } diff --git a/src/Isolation/Masa.Contrib.Isolation/DispatcherOptionsExtensions.cs b/src/Isolation/Masa.Contrib.Isolation/DispatcherOptionsExtensions.cs index 461430e88..c3ef19ea9 100644 --- a/src/Isolation/Masa.Contrib.Isolation/DispatcherOptionsExtensions.cs +++ b/src/Isolation/Masa.Contrib.Isolation/DispatcherOptionsExtensions.cs @@ -42,38 +42,42 @@ private static void AddIsolation(this IServiceCollection services, Action service.ServiceType == typeof(ITenantContext) || service.ServiceType == typeof(IEnvironmentContext)) < - 1) + if (services.Count(service => + service.ServiceType == typeof(ITenantContext) || + service.ServiceType == typeof(IEnvironmentContext)) < 1) throw new NotSupportedException("Tenant isolation and environment isolation use at least one"); services.AddHttpContextAccessor(); services - .TryAddConfigure(Const.DEFAULT_SECTION) + .TryAddConfigure() .AddTransient(typeof(IMiddleware<>), typeof(IsolationMiddleware<>)) .TryAddSingleton(); - services.TryAddScoped(typeof(IIsolationDbConnectionStringProvider), typeof(DefaultDbIsolationConnectionStringProvider)); + + if (services.Any(service => service.ServiceType == typeof(IConnectionStringProvider))) + services.Replace(new ServiceDescriptor(typeof(IConnectionStringProvider), typeof(DefaultDbIsolationConnectionStringProvider), ServiceLifetime.Scoped)); + else + services.TryAddScoped(); } private static IServiceCollection TryAddConfigure( - this IServiceCollection services, - string sectionName) + this IServiceCollection services) where TOptions : class { + services.AddOptions(); var serviceProvider = services.BuildServiceProvider(); IConfiguration? configuration = serviceProvider.GetService()?.GetConfiguration(SectionTypes.Local) ?? serviceProvider.GetService(); + if (configuration == null) return services; string name = Options.DefaultName; - services.AddOptions(); - var configurationSection = configuration.GetSection(sectionName); services.TryAddSingleton>( - new ConfigurationChangeTokenSource(name, configurationSection)); + new ConfigurationChangeTokenSource(name, configuration)); services.TryAddSingleton>(new NamedConfigureFromConfigurationOptions( name, - configurationSection, _ => + configuration, _ => { })); return services; diff --git a/src/Isolation/Masa.Contrib.Isolation/IsolationDbContextProvider.cs b/src/Isolation/Masa.Contrib.Isolation/IsolationDbContextProvider.cs index 4cc2de95e..6f13962b9 100644 --- a/src/Isolation/Masa.Contrib.Isolation/IsolationDbContextProvider.cs +++ b/src/Isolation/Masa.Contrib.Isolation/IsolationDbContextProvider.cs @@ -11,12 +11,12 @@ public class IsolationDbContextProvider : BaseDbConnectionStringProvider protected override List GetDbContextOptionsList() { - var connectionStrings = _options.CurrentValue.Isolations + var connectionStrings = _options.CurrentValue.IsolationConnectionStrings .Select(connectionString => connectionString.ConnectionString) .Distinct() .ToList(); - if (!connectionStrings.Contains(_options.CurrentValue.DefaultConnection)) - connectionStrings.Add(_options.CurrentValue.DefaultConnection); + if (!connectionStrings.Contains(_options.CurrentValue.ConnectionStrings.DefaultConnection)) + connectionStrings.Add(_options.CurrentValue.ConnectionStrings.DefaultConnection); return connectionStrings.Select(connectionString => new MasaDbContextConfigurationOptions(connectionString)).ToList(); } diff --git a/src/Isolation/Masa.Contrib.Isolation/Masa.Contrib.Isolation.csproj b/src/Isolation/Masa.Contrib.Isolation/Masa.Contrib.Isolation.csproj index 7b2d2648b..e92dd5fb0 100644 --- a/src/Isolation/Masa.Contrib.Isolation/Masa.Contrib.Isolation.csproj +++ b/src/Isolation/Masa.Contrib.Isolation/Masa.Contrib.Isolation.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Isolation/Masa.Contrib.Isolation/_Imports.cs b/src/Isolation/Masa.Contrib.Isolation/_Imports.cs index 7040fc4c0..cb06356dd 100644 --- a/src/Isolation/Masa.Contrib.Isolation/_Imports.cs +++ b/src/Isolation/Masa.Contrib.Isolation/_Imports.cs @@ -2,15 +2,15 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Masa.BuildingBlocks.Configuration; +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Options; global using Masa.BuildingBlocks.Data.UoW; -global using Masa.BuildingBlocks.Data.UoW.Options; global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.BuildingBlocks.Isolation; global using Masa.BuildingBlocks.Isolation.Environment; global using Masa.BuildingBlocks.Isolation.Middleware; global using Masa.BuildingBlocks.Isolation.MultiTenant; global using Masa.BuildingBlocks.Isolation.Options; -global using Masa.Contrib.Isolation.Internal; global using Masa.Contrib.Isolation.Middleware; global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Http; diff --git a/test/Masa.Contrib.Data.Contracts.EF.Tests/DataFilterTest.cs b/test/Masa.Contrib.Data.Contracts.EF.Tests/DataFilterTest.cs new file mode 100644 index 000000000..afc0310fe --- /dev/null +++ b/test/Masa.Contrib.Data.Contracts.EF.Tests/DataFilterTest.cs @@ -0,0 +1,47 @@ +// 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.Contracts.EF.Tests; + +[TestClass] +public class DataFilterTest +{ + private IServiceCollection _services; + private IDataFilter _dataFilter; + + [TestInitialize] + public void Initialize() + { + _services = new ServiceCollection(); + _services.AddSingleton(); + _services.AddSingleton(typeof(DataFilter<>)); + _dataFilter = new DataFilter(_services.BuildServiceProvider()); + } + + [TestMethod] + public void TestDataFilterReturnTrue() + { + Assert.IsTrue(_dataFilter.IsEnabled()); + + using (_dataFilter.Disable()) + { + Assert.IsFalse(_dataFilter.IsEnabled()); + } + + Assert.IsTrue(_dataFilter.IsEnabled()); + } + + [TestMethod] + public void TestDataFilterReturnFalse() + { + _dataFilter.Disable(); + Assert.IsFalse(_dataFilter.IsEnabled()); + + using (_dataFilter.Enable()) + { + Assert.IsTrue(_dataFilter.IsEnabled()); + } + + Assert.IsFalse(_dataFilter.IsEnabled()); + } +} diff --git a/test/Masa.Contrib.Data.Contracts.EF.Tests/Masa.Contrib.Data.Contracts.EF.Tests.csproj b/test/Masa.Contrib.Data.Contracts.EF.Tests/Masa.Contrib.Data.Contracts.EF.Tests.csproj index b8ab51962..7627e5fd8 100644 --- a/test/Masa.Contrib.Data.Contracts.EF.Tests/Masa.Contrib.Data.Contracts.EF.Tests.csproj +++ b/test/Masa.Contrib.Data.Contracts.EF.Tests/Masa.Contrib.Data.Contracts.EF.Tests.csproj @@ -20,11 +20,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - diff --git a/test/Masa.Contrib.Data.Contracts.EF.Tests/_Imports.cs b/test/Masa.Contrib.Data.Contracts.EF.Tests/_Imports.cs index 2b37cc14c..05c2ff359 100644 --- a/test/Masa.Contrib.Data.Contracts.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Data.Contracts.EF.Tests/_Imports.cs @@ -1,2 +1,7 @@ // 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.Contracts.DataFiltering; +global using Masa.Contrib.Data.Contracts.EF.DataFiltering; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/CustomizeDbContext.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/CustomizeDbContext.cs new file mode 100644 index 000000000..5f8997920 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/CustomizeDbContext.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. + +namespace Masa.Contrib.Data.EntityFrameworkCore.Tests; + +public class CustomizeDbContext : MasaDbContext +{ + public CustomizeDbContext(MasaDbContextOptions options) : base(options) + { + } + + protected override void OnModelCreatingExecuting(ModelBuilder modelBuilder) + { + modelBuilder.Entity(); + modelBuilder.Entity().OwnsOne(x => x.Address); + modelBuilder.Entity().OwnsMany(t => t.Hobbies); + } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DbContextTest.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DbContextTest.cs new file mode 100644 index 000000000..30fd9d841 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DbContextTest.cs @@ -0,0 +1,193 @@ +// 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.EntityFrameworkCore.Tests; + +[TestClass] +public class DbContextTest : TestBase +{ + [TestMethod] + public async Task TestAddAsync() + { + await using var dbContext = CreateDbContext(true, out _); + await dbContext.Set().AddAsync(new Student() + { + Id = 1, + Name = "Jim", + Age = 18, + Address = new Address() + { + City = "ShangHai", + Street = "PuDong", + }, + Hobbies = new List() + { + new() + { + Name = "Sing", + Description = "loves singing" + }, + new() + { + Name = "Game", + Description = "mobile game" + } + } + }); + await dbContext.SaveChangesAsync(); + Assert.IsTrue(await dbContext.Set().CountAsync() == 1); + } + + [TestMethod] + public async Task TestSoftDeleteAsync() + { + Services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = $"data source=soft-delete-db-{Guid.NewGuid()}" + }; + }); + await using var dbContext = CreateDbContext(true, out IServiceProvider serviceProvider, false); + var student = new Student() + { + Id = 1, + Name = "Jim", + Age = 18, + Address = new Address() + { + City = "ShangHai", + Street = "PuDong", + }, + Hobbies = new List() + { + new() + { + Name = "Sing", + Description = "loves singing" + }, + new() + { + Name = "Game", + Description = "mobile game" + } + } + }; + await dbContext.Set().AddAsync(student); + await dbContext.SaveChangesAsync(); + Assert.IsTrue(await dbContext.Set().CountAsync() == 1); + + student = await dbContext.Set().Include(s => s.Address).Include(s => s.Hobbies).FirstAsync(); + dbContext.Set().Remove(student); + await dbContext.SaveChangesAsync(); + + Assert.IsTrue(await dbContext.Set().CountAsync() == 0); + + var dataFilter = serviceProvider.GetRequiredService(); + using (dataFilter.Disable()) + { + Assert.IsTrue(await dbContext.Set().CountAsync() == 1); + + student = (await dbContext.Set().Include(s => s.Address).FirstOrDefaultAsync())!; + Assert.IsTrue(student.Id == 1); + Assert.IsTrue(student.Name == "Jim"); + Assert.IsTrue(student.Age == 18); + Assert.IsTrue(student.IsDeleted); + Assert.IsTrue(student.Address.City == "ShangHai"); + Assert.IsTrue(student.Address.Street == "PuDong"); + + Assert.IsTrue(student.Hobbies.Count == 2); + Assert.IsTrue(student.Hobbies.Any(h => h.Name == "Sing")); + Assert.IsTrue(student.Hobbies.Any(h => h.Name == "Game")); + } + } + + [TestMethod] + public async Task TestDisabledSoftDelete() + { + Services.AddMasaDbContext(options + => options.UseTestFilter().UseTestSqlite($"data source=disabled-soft-delete-db-{Guid.NewGuid()}")); + var serviceProvider = Services.BuildServiceProvider(); + var dbContext = serviceProvider.GetRequiredService(); + await dbContext.Database.EnsureCreatedAsync(); + var student = new Student + { + Id = 1, + Name = "Jim", + Age = 18, + Address = new Address() + { + City = "ShangHai", + Street = "PuDong", + } + }; + await dbContext.Set().AddAsync(student); + await dbContext.SaveChangesAsync(); + Assert.IsTrue(await dbContext.Set().CountAsync() == 1); + + dbContext.Set().Remove(student); + await dbContext.SaveChangesAsync(); + + Assert.IsTrue(await dbContext.Set().CountAsync() == 0); + + var dataFilter = serviceProvider.GetRequiredService(); + using (dataFilter.Disable()) + { + var count = await dbContext.Set().IgnoreQueryFilters().CountAsync(); + Assert.IsTrue(count == 1); + } + } + + [TestMethod] + public async Task TestGetPaginatedListAsyncReturnCountEqualResultCount() + { + Services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = $"data source=soft-delete-db-{Guid.NewGuid()}" + }; + }); + await using var dbContext = CreateDbContext(true, out IServiceProvider serviceProvider); + var students = new List() + { + new() + { + Id = 1, + Name = "Jim", + Age = 18, + Address = new Address() + { + City = "ShangHai", + Street = "PuDong", + } + }, + new() + { + Id = 2, + Name = "Tom", + Age = 20, + Address = new Address() + { + City = "ShangHai", + Street = "PuDong", + } + } + }; + await dbContext.Set().AddRangeAsync(students); + await dbContext.SaveChangesAsync(); + Assert.IsTrue(await dbContext.Set().CountAsync() == 2); + + var student = await dbContext.Set().FirstAsync(); + dbContext.Set().Remove(student); + await dbContext.SaveChangesAsync(); + + var result = await new Repository(dbContext).GetPaginatedListAsync(new PaginatedOptions() + { + Page = 1, + PageSize = 10 + }); + + Assert.IsTrue(result.Result.Count == result.Total); + } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DefaultConnectionStringProviderTest.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DefaultConnectionStringProviderTest.cs new file mode 100644 index 000000000..b14e73e0e --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/DefaultConnectionStringProviderTest.cs @@ -0,0 +1,44 @@ +// 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.EntityFrameworkCore.Tests; + +[TestClass] +public class DefaultConnectionStringProviderTest +{ + [TestMethod] + public async Task TestGetConnectionStringAsyncReturnTest1() + { + IServiceCollection services = new ServiceCollection(); + services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = "Test1" + }; + }); + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetRequiredService>(); + var defaultConnectionStringProvider = new DefaultConnectionStringProvider(options); + var connectionString = await defaultConnectionStringProvider.GetConnectionStringAsync(); + Assert.AreEqual("Test1", connectionString); + } + + [TestMethod] + public async Task TestGetConnectionStringAsyncAndNameIsEmptyReturnTest1() + { + IServiceCollection services = new ServiceCollection(); + services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = "Test1" + }; + }); + var serviceProvider = services.BuildServiceProvider(); + var options = serviceProvider.GetRequiredService>(); + var defaultConnectionStringProvider = new DefaultConnectionStringProvider(options); + var connectionString = await defaultConnectionStringProvider.GetConnectionStringAsync(string.Empty); + Assert.AreEqual("Test1", connectionString); + } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/PaginatedOptions.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/PaginatedOptions.cs new file mode 100644 index 000000000..14a21d266 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/PaginatedOptions.cs @@ -0,0 +1,11 @@ +// 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.EntityFrameworkCore.Tests.Internal; + +internal class PaginatedOptions +{ + public int Page { get; set; } + + public int PageSize { get; set; } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/Repository.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/Repository.cs new file mode 100644 index 000000000..82bde9172 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Internal/Repository.cs @@ -0,0 +1,35 @@ +// 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.EntityFrameworkCore.Tests.Internal; + +internal class Repository +{ + private readonly CustomizeDbContext _testDbContext; + + public Repository(CustomizeDbContext testDbContext) => _testDbContext = testDbContext; + + public Task> GetPaginatedListAsync(int skip, int take, CancellationToken cancellationToken = default) + => _testDbContext.Set().Skip(skip).Take(take).ToListAsync(cancellationToken); + + public virtual async Task> GetPaginatedListAsync(PaginatedOptions options, CancellationToken cancellationToken = default) + { + var result = await GetPaginatedListAsync( + (options.Page - 1) * options.PageSize, + options.PageSize <= 0 ? int.MaxValue : options.PageSize, + cancellationToken + ); + + var total = await GetCountAsync(cancellationToken); + + return new BasePaginatedList() + { + Total = total, + Result = result, + TotalPages = (int)Math.Ceiling(total / (decimal)options.PageSize) + }; + } + + public async Task GetCountAsync(CancellationToken cancellationToken = default) + => await _testDbContext.Set().LongCountAsync(cancellationToken); +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Masa.Contrib.Data.EntityFrameworkCore.Tests.csproj b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Masa.Contrib.Data.EntityFrameworkCore.Tests.csproj new file mode 100644 index 000000000..70bbf7ce0 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Masa.Contrib.Data.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + false + enable + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Address.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Address.cs new file mode 100644 index 000000000..2909884f3 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Address.cs @@ -0,0 +1,11 @@ +// 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.EntityFrameworkCore.Tests.Models; + +public class Address +{ + public string City { get; set; } = default!; + + public string Street { get; set; } = default!; +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Hobby.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Hobby.cs new file mode 100644 index 000000000..366e9a9b9 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Hobby.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. + +namespace Masa.Contrib.Data.EntityFrameworkCore.Tests.Models; + +public class Hobby +{ + public Guid Id { get; set; } + + public string Name { get; set; } = default!; + + public string Description { get; set; } = default!; + + public Hobby() + { + Id = Guid.NewGuid(); + } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Student.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Student.cs new file mode 100644 index 000000000..9850f1531 --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/Models/Student.cs @@ -0,0 +1,19 @@ +// 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.EntityFrameworkCore.Tests.Models; + +public class Student : ISoftDelete +{ + public int Id { get; set; } + + public string Name { get; set; } = default!; + + public int Age { get; set; } + + public bool IsDeleted { get; private set; } = default!; + + public Address Address { get; set; } = default!; + + public List Hobbies { get; set; } = default!; +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/TestBase.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/TestBase.cs new file mode 100644 index 000000000..d25be19df --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/TestBase.cs @@ -0,0 +1,34 @@ +// 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.EntityFrameworkCore.Tests; + +public class TestBase +{ + protected IServiceCollection Services; + + [TestInitialize] + public void Initialize() + { + Services = new ServiceCollection(); + } + + protected CustomizeDbContext CreateDbContext(bool enableSoftDelete, out IServiceProvider serviceProvider, + bool initConnectionString = true) + { + Services.AddMasaDbContext(options => + { + if (enableSoftDelete) + options.UseTestFilter(); + + if (initConnectionString) + options.UseTestSqlite($"data source=test-{Guid.NewGuid()}"); + else + options.UseSqlite(); + }); + serviceProvider = Services.BuildServiceProvider(); + var dbContext = serviceProvider.GetRequiredService(); + dbContext.Database.EnsureCreated(); + return dbContext; + } +} diff --git a/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/_Imports.cs b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/_Imports.cs new file mode 100644 index 000000000..f2c8afcbc --- /dev/null +++ b/test/Masa.Contrib.Data.EntityFrameworkCore.Tests/_Imports.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. + +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Contracts.DataFiltering; +global using Masa.BuildingBlocks.Data.Contracts.Paginated; +global using Masa.Contrib.Data.Contracts.EF; +global using Masa.Contrib.Data.EntityFrameworkCore.Tests.Internal; +global using Masa.Contrib.Data.EntityFrameworkCore.Tests.Models; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Options; +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/Masa.Contrib.Data.UoW.EF.Tests.csproj b/test/Masa.Contrib.Data.UoW.EF.Tests/Masa.Contrib.Data.UoW.EF.Tests.csproj index d5628d898..ec650a403 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/Masa.Contrib.Data.UoW.EF.Tests.csproj +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/Masa.Contrib.Data.UoW.EF.Tests.csproj @@ -12,7 +12,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -33,6 +32,7 @@ + diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/TestBase.cs b/test/Masa.Contrib.Data.UoW.EF.Tests/TestBase.cs index dc0872534..8b06e74be 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/TestBase.cs +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/TestBase.cs @@ -3,7 +3,7 @@ namespace Masa.Contrib.Data.UoW.EF.Tests; -public class TestBase : IDisposable +public class TestBase { protected readonly string _connectionString = "DataSource=:memory:"; protected readonly SqliteConnection Connection; diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs b/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs index 6db3079ac..259c054f6 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs @@ -1,6 +1,8 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using Microsoft.Extensions.Options; + namespace Masa.Contrib.Data.UoW.EF.Tests; [TestClass] @@ -25,7 +27,7 @@ public void TestAddUoWAndNullServices() [TestMethod] public void TestAddUoWAndUseSqlLite() { - _options.Object.UseUoW(options => options.UseSqlite(_connectionString)); + _options.Object.UseUoW(options => options.UseTestSqlite(_connectionString)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); Assert.IsNotNull(serviceProvider.GetRequiredService()); } @@ -34,8 +36,8 @@ public void TestAddUoWAndUseSqlLite() public void TestAddMultUoW() { _options.Object - .UseUoW(options => options.UseSqlite(_connectionString)) - .UseUoW(options => options.UseSqlite(_connectionString)); + .UseUoW(options => options.UseTestSqlite(_connectionString)) + .UseUoW(options => options.UseTestSqlite(_connectionString)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -51,7 +53,7 @@ public void TestTransaction() [TestMethod] public async Task TestUseTranscationAsync() { - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -72,7 +74,7 @@ public async Task TestUseTranscationAsync() [TestMethod] public async Task TestNotUseTranscationAsync() { - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -90,7 +92,7 @@ public async Task TestNotUseTranscationAsync() [TestMethod] public async Task TestNotTransactionCommitAsync() { - _options.Object.UseUoW(options => options.UseSqlite(_connectionString)); + _options.Object.UseUoW(options => options.UseTestSqlite(_connectionString)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -101,7 +103,7 @@ public async Task TestNotTransactionCommitAsync() [TestMethod] public async Task TestCommitAsync() { - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -121,7 +123,7 @@ public async Task TestCommitAsync() [TestMethod] public async Task TestOpenRollbackAsync() { - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -138,7 +140,7 @@ public async Task TestOpenRollbackAsync() public async Task TestAddLoggerAndOpenRollbackAsync() { _options.Object.Services.AddLogging(); - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); @@ -156,17 +158,17 @@ public void TestDataConnectionString() { IConfiguration configuration = new ConfigurationManager(); _options.Object.Services.AddSingleton(_ => configuration); - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var dataConnectionStringProvider = serviceProvider.GetRequiredService(); Assert.IsTrue(dataConnectionStringProvider.DbContextOptionsList.Count == 1 && - dataConnectionStringProvider.DbContextOptionsList.Any(option => option.ConnectionString == null)); + dataConnectionStringProvider.DbContextOptionsList.Any(option => option.ConnectionString == _connectionString)); } [TestMethod] public void TestUnitOfWorkManager() { - _options.Object.UseUoW(options => options.UseSqlite(Connection)); + _options.Object.UseUoW(options => options.UseTestSqlite(Connection)); var serviceProvider = _options.Object.Services.BuildServiceProvider(); var unitOfWorkManager = serviceProvider.GetRequiredService(); var unitOfWork = serviceProvider.GetRequiredService(); @@ -176,24 +178,26 @@ public void TestUnitOfWorkManager() var newUnitOfWork = unitOfWorkManager.CreateDbContext( - new Masa.BuildingBlocks.Data.UoW.Options.MasaDbContextConfigurationOptions(_connectionString)); + new MasaDbContextConfigurationOptions(_connectionString)); Assert.IsFalse(newUnitOfWork.Equals(unitOfWork)); var newDbContext = newUnitOfWork.ServiceProvider.GetRequiredService(); Assert.IsFalse(dbContext.Equals(newDbContext)); Assert.ThrowsException(() - => unitOfWorkManager.CreateDbContext(new BuildingBlocks.Data.UoW.Options.MasaDbContextConfigurationOptions(""))); + => unitOfWorkManager.CreateDbContext(new MasaDbContextConfigurationOptions(""))); } [TestMethod] public async Task TestUnitOfWorkAccessorAsync() { var services = new ServiceCollection(); - var configurationRoot = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", true, true) - .Build(); - services.AddSingleton(configurationRoot); + services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = _connectionString + }; + }); _options.Setup(option => option.Services).Returns(services).Verifiable(); _options.Object.UseUoW(options => options.UseSqlite()); var serviceProvider = _options.Object.Services.BuildServiceProvider(); @@ -204,17 +208,17 @@ public async Task TestUnitOfWorkAccessorAsync() Assert.IsTrue(!unitOfWork.TransactionHasBegun); unitOfWorkAccessor = serviceProvider.GetService(); Assert.IsTrue(unitOfWorkAccessor!.CurrentDbContextOptions != null && unitOfWorkAccessor.CurrentDbContextOptions.ConnectionString == - configurationRoot["ConnectionStrings:DefaultConnection"].ToString()); + _connectionString); var unitOfWorkManager = serviceProvider.GetRequiredService(); var unitOfWorkNew = unitOfWorkManager.CreateDbContext(false); var unitOfWorkAccessorNew = unitOfWorkNew.ServiceProvider.GetService(); Assert.IsTrue(unitOfWorkAccessorNew!.CurrentDbContextOptions != null && unitOfWorkAccessorNew.CurrentDbContextOptions.ConnectionString == - configurationRoot["ConnectionStrings:DefaultConnection"].ToString()); + _connectionString); var unitOfWorkNew2 = - unitOfWorkManager.CreateDbContext(new BuildingBlocks.Data.UoW.Options.MasaDbContextConfigurationOptions("test")); + unitOfWorkManager.CreateDbContext(new MasaDbContextConfigurationOptions("test")); var unitOfWorkAccessorNew2 = unitOfWorkNew2.ServiceProvider.GetService(); Assert.IsTrue(unitOfWorkAccessorNew2!.CurrentDbContextOptions != null && unitOfWorkAccessorNew2.CurrentDbContextOptions.ConnectionString == "test"); @@ -228,11 +232,13 @@ public async Task TestUnitOfWorkAccessorAsync() public void TestUnitOfWorkByEventBusBuilder() { var services = new ServiceCollection(); - var configurationRoot = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", true, true) - .Build(); - services.AddSingleton(configurationRoot); + services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = _connectionString + }; + }); Mock eventBuilder = new(); eventBuilder.Setup(builder => builder.Services).Returns(services).Verifiable(); eventBuilder.Object.UseUoW(options => options.UseSqlite()); @@ -258,7 +264,39 @@ public void TestUnitOfWorkAndAddMasaConfiguationReturnUnitOfWorkIsNotNull() Assert.IsNotNull(serviecProvider.GetService()); var customDbContext = serviecProvider.GetRequiredService(); - Assert.IsTrue(GetDataBaseConnectionString(customDbContext) == serviecProvider.GetRequiredService().GetConfiguration(SectionTypes.Local)["ConnectionStrings:DefaultConnection"]); + Assert.IsTrue(GetDataBaseConnectionString(customDbContext) == + serviecProvider.GetRequiredService().GetConfiguration(SectionTypes.Local)[ + "ConnectionStrings:DefaultConnection"]); + } + + [TestMethod] + public async Task TestGetConnectionStringAndCurrentDbContextOptionsAsyncReturnTest1() + { + Mock unitOfWorkAccessor = new(); + string connectionString = "Test1"; + unitOfWorkAccessor.Setup(accessor => accessor.CurrentDbContextOptions) + .Returns(new MasaDbContextConfigurationOptions(connectionString)); + var connectionStringProvider = new DefaultConnectionStringProvider(unitOfWorkAccessor.Object, null!); + Assert.IsTrue(await connectionStringProvider.GetConnectionStringAsync() == connectionString); + } + + [TestMethod] + public async Task TestGetConnectionStringAsyncReturnTest1() + { + Mock unitOfWorkAccessor = new(); + string connectionString = "Test1"; + IServiceCollection services = new ServiceCollection(); + services.Configure(options => + { + options.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = connectionString + }; + }); + var serviceProvider = services.BuildServiceProvider(); + var connectionStringProvider = new DefaultConnectionStringProvider(unitOfWorkAccessor.Object, + serviceProvider.GetRequiredService>()); + Assert.IsTrue(await connectionStringProvider.GetConnectionStringAsync() == connectionString); } private string GetDataBaseConnectionString(CustomDbContext dbContext) => dbContext.Database.GetConnectionString()!; diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs b/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs index 980fd9e19..f535daad0 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs @@ -2,11 +2,13 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Masa.BuildingBlocks.Configuration; +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Options; global using Masa.BuildingBlocks.Data.UoW; global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.Contrib.Configuration; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Sqlite; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Microsoft.AspNetCore.Builder; global using Microsoft.Data.Sqlite; global using Microsoft.EntityFrameworkCore; diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/BaseRepositoryTest.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/BaseRepositoryTest.cs index 358baf7f7..742cbc573 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/BaseRepositoryTest.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/BaseRepositoryTest.cs @@ -88,7 +88,7 @@ public void TestEntityRepositoryShouldBeExist() { _dispatcherOptions.Setup(option => option.Assemblies).Returns(_assemblies).Verifiable(); _services.AddScoped(typeof(IUnitOfWork), _ => _uoW.Object); - _services.AddMasaDbContext(options => options.UseSqlite(Connection)); + _services.AddMasaDbContext(options => options.UseTestSqlite(Connection)); _dispatcherOptions.Object.UseRepository().UseRepository(); var serviceProvider = _services.BuildServiceProvider(); diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Masa.Contrib.Ddd.Domain.Repository.EF.Tests.csproj b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Masa.Contrib.Ddd.Domain.Repository.EF.Tests.csproj index bf0ff6e29..3453d12ad 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Masa.Contrib.Ddd.Domain.Repository.EF.Tests.csproj +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Masa.Contrib.Ddd.Domain.Repository.EF.Tests.csproj @@ -12,7 +12,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -21,10 +20,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs index 855a1b3ab..d8fc76fc8 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs @@ -29,7 +29,7 @@ public async Task InitializeAsync(Action? action) _dispatcherOptions.Setup(options => options.Services).Returns(() => _services); _dispatcherOptions.Setup(options => options.Assemblies).Returns(() => _assemblies); if (action == null) - _services.AddMasaDbContext(options => options.UseSqlite(Connection)); + _services.AddMasaDbContext(options => options.UseTestSqlite(Connection)); else action.Invoke(_services); @@ -248,8 +248,9 @@ public async Task TestUpdateAsync() await InitializeAsync(services => services.AddMasaDbContext(options => { - options.DbContextOptionsBuilder.UseSqlite(Connection) - .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + options.Builder = (_, dbContextOptionsBuilder) + => dbContextOptionsBuilder.UseSqlite(Connection) + .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); })); _dispatcherOptions.Object.UseRepository(); @@ -388,7 +389,7 @@ public async Task TestDbTransactionAsync() public async Task TestServiceLifeAsync() { var services = new ServiceCollection(); - services.AddMasaDbContext(options => options.UseSqlite(Connection)); + services.AddMasaDbContext(options => options.UseTestSqlite(Connection)); var serviceProvider = services.BuildServiceProvider(); await using (var scope = serviceProvider.CreateAsyncScope()) diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/_Imports.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/_Imports.cs index e21570961..1c78b0762 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/_Imports.cs @@ -7,13 +7,13 @@ global using Masa.BuildingBlocks.Ddd.Domain.Repositories; global using Masa.BuildingBlocks.Ddd.Domain.Values; global using Masa.BuildingBlocks.Dispatcher.Events; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Data.UoW.EF; global using Masa.Contrib.Ddd.Domain.Repository.EF.CustomRepository.Tests.Repositories; global using Masa.Contrib.Ddd.Domain.Repository.EF.Tests.Domain.Entities; global using Masa.Contrib.Ddd.Domain.Repository.EF.Tests.Domain.Repositories; global using Masa.Contrib.Ddd.Domain.Repository.EF.Tests.Infrastructure; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Sqlite; global using Microsoft.Data.Sqlite; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Storage; diff --git a/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/ProcessorTest.cs b/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/ProcessorTest.cs index 08b013017..893dc776e 100644 --- a/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/ProcessorTest.cs +++ b/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/ProcessorTest.cs @@ -79,11 +79,11 @@ public async Task RetryByDataProcessorExecuteTestAsync() services.AddScoped(_ => uoW.Object); Mock unitOfWorkManager = new(); - unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); + unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); services.AddSingleton(_ => unitOfWorkManager.Object); Mock dataConnectionStringProvider = new(); - dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List + dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List { new(string.Empty) }).Verifiable(); @@ -157,11 +157,11 @@ public async Task RetryByDataProcessorExecute2TestAsync() services.AddScoped(_ => uoW.Object); Mock unitOfWorkManager = new(); - unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); + unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); services.AddSingleton(_ => unitOfWorkManager.Object); Mock dataConnectionStringProvider = new(); - dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List + dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List { new(string.Empty) }).Verifiable(); @@ -242,11 +242,11 @@ public async Task RetryByDataProcessorExecute2AndNotUseLoggerTestAsync() services.AddScoped(_ => uoW.Object); Mock unitOfWorkManager = new(); - unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); + unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); services.AddSingleton(_ => unitOfWorkManager.Object); Mock dataConnectionStringProvider = new(); - dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List() + dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List() { new(string.Empty) }).Verifiable(); @@ -283,11 +283,11 @@ public async Task DeletePublishedExpireEventProcessorExecuteTestAsync() uoW.Setup(uow => uow.ServiceProvider).Returns(_options.Value.Services.BuildServiceProvider()).Verifiable(); Mock unitOfWorkManager = new(); - unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); + unitOfWorkManager.Setup(uoWManager => uoWManager.CreateDbContext(It.IsAny())).Returns(uoW.Object).Verifiable(); _options.Value.Services.AddSingleton(_ => unitOfWorkManager.Object); Mock dataConnectionStringProvider = new(); - dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List() + dataConnectionStringProvider.Setup(provider => provider.DbContextOptionsList).Returns(new List() { new(string.Empty) }).Verifiable(); diff --git a/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/_Imports.cs b/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/_Imports.cs index 5e3534597..3f1363bbc 100644 --- a/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/_Imports.cs +++ b/test/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Tests/_Imports.cs @@ -19,3 +19,5 @@ global using Moq; global using System.Data.Common; global using System.Reflection; +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Data.Options; diff --git a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/IntegrationEventLogServiceTest.cs b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/IntegrationEventLogServiceTest.cs index 782c7356e..71c610d92 100644 --- a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/IntegrationEventLogServiceTest.cs +++ b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/IntegrationEventLogServiceTest.cs @@ -10,7 +10,7 @@ public class IntegrationEventLogServiceTest : TestBase public async Task TestNullDbTransactionAsync() { var services = new ServiceCollection(); - services.AddMasaDbContext(builder => builder.UseSqlite(ConnectionString)) + services.AddMasaDbContext(builder => builder.UseTestSqlite(ConnectionString)) .AddScoped(); IDispatcherOptions dispatcherOptions = CreateDispatcherOptions(services); dispatcherOptions.UseEventLog(); @@ -53,7 +53,7 @@ public async Task TestRetrieveEventLogsFailedToPublishAsync() { var dispatcherOptions = CreateDispatcherOptions(new ServiceCollection()); dispatcherOptions.UseEventLog(); - dispatcherOptions.Services.AddMasaDbContext(option => option.UseSqlite(Connection)); + dispatcherOptions.Services.AddMasaDbContext(option => option.UseTestSqlite(Connection)); dispatcherOptions.Services.AddScoped(); var serviceProvider = dispatcherOptions.Services.BuildServiceProvider(); await serviceProvider.GetRequiredService().Database.EnsureCreatedAsync(); @@ -279,7 +279,7 @@ public async Task TestMarkEventAsFailed2Async() var dispatcherOptions = CreateDispatcherOptions(new ServiceCollection()); dispatcherOptions.UseEventLog(); dispatcherOptions.Services.AddMasaDbContext(option => - option.UseSqlite(Connection)); + option.UseTestSqlite(Connection)); dispatcherOptions.Services.AddScoped(); Mock integrationEventBus = new(); var types = AppDomain.CurrentDomain.GetAssemblies() diff --git a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.csproj b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.csproj index a4259aa5d..a79a2b295 100644 --- a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.csproj +++ b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.csproj @@ -16,7 +16,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -25,6 +24,7 @@ + diff --git a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/_Imports.cs b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/_Imports.cs index cafaac975..b64233a57 100644 --- a/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests/_Imports.cs @@ -5,11 +5,11 @@ global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents.Logs; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.Domain.Entities; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.Events; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF.Tests.Infrastructure; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Sqlite; global using Microsoft.Data.Sqlite; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Storage; diff --git a/test/Masa.Contrib.Isolation.Tests/TestDbIsolationConnectionStringProvider.cs b/test/Masa.Contrib.Isolation.Tests/TestDbIsolationConnectionStringProvider.cs index e0047429b..67e394dd9 100644 --- a/test/Masa.Contrib.Isolation.Tests/TestDbIsolationConnectionStringProvider.cs +++ b/test/Masa.Contrib.Isolation.Tests/TestDbIsolationConnectionStringProvider.cs @@ -22,32 +22,38 @@ public async Task TestGetConnectionStringAsync() var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; unitOfWorkAccessor.CurrentDbContextOptions = new MasaDbContextConfigurationOptions(defaultConnectionString); var provider = new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, null!); + Assert.IsTrue(await provider.GetConnectionStringAsync() == defaultConnectionString); } [TestMethod] public async Task TestGetConnectionString2Async() { - string defaultConnectionString = "data source=test1;"; - - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; var options = _services.BuildServiceProvider().GetRequiredService>(); var provider = new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, options); - Assert.IsTrue(await provider.GetConnectionStringAsync() == defaultConnectionString); + Assert.IsTrue(await provider.GetConnectionStringAsync() == connectionStrings.DefaultConnection); } [TestMethod] public async Task TestGetConnectionString3Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -72,13 +78,16 @@ public async Task TestGetConnectionString3Async() [TestMethod] public async Task TestGetConnectionString4Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -103,13 +112,16 @@ public async Task TestGetConnectionString4Async() [TestMethod] public async Task TestGetConnectionString5Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -134,13 +146,16 @@ public async Task TestGetConnectionString5Async() [TestMethod] public async Task TestGetConnectionString6Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -165,13 +180,16 @@ public async Task TestGetConnectionString6Async() [TestMethod] public async Task TestGetConnectionString7Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -196,13 +214,16 @@ public async Task TestGetConnectionString7Async() [TestMethod] public async Task TestGetConnectionString8Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -246,13 +267,16 @@ public async Task TestGetConnectionString8Async() [TestMethod] public async Task TestGetConnectionString9Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -296,13 +320,16 @@ public async Task TestGetConnectionString9Async() [TestMethod] public async Task TestGetConnectionString10Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -346,13 +373,16 @@ public async Task TestGetConnectionString10Async() [TestMethod] public async Task TestGetConnectionString11Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -388,7 +418,8 @@ public async Task TestGetConnectionString11Async() Mock tenantContext = new(); tenantContext.Setup(context => context.CurrentTenant).Returns(new Tenant("2")).Verifiable(); - var provider = new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, options, environmentContext.Object, tenantContext.Object); + var provider = + new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, options, environmentContext.Object, tenantContext.Object); Assert.IsTrue(await provider.GetConnectionStringAsync() == "data source=test4;"); } @@ -396,13 +427,16 @@ public async Task TestGetConnectionString11Async() public async Task TestGetConnectionString12Async() { _services.AddLogging(); - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { @@ -438,20 +472,24 @@ public async Task TestGetConnectionString12Async() Mock tenantContext = new(); tenantContext.Setup(context => context.CurrentTenant).Returns(new Tenant("2")).Verifiable(); - var provider = new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, options, environmentContext.Object, tenantContext.Object); + var provider = + new DefaultDbIsolationConnectionStringProvider(unitOfWorkAccessor, options, environmentContext.Object, tenantContext.Object); Assert.IsTrue(await provider.GetConnectionStringAsync() == "data source=test4;"); } [TestMethod] public async Task TestGetConnectionString13Async() { - string defaultConnectionString = "data source=test1;"; - _services.Configure(option => option.DefaultConnection = defaultConnectionString); + var connectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test1;" + }; + _services.Configure(option => option.ConnectionStrings = connectionStrings); var unitOfWorkAccessor = _services.BuildServiceProvider().GetService()!; _services.Configure(option => { - option.DefaultConnection = defaultConnectionString; - option.Isolations = new List + option.ConnectionStrings = connectionStrings; + option.IsolationConnectionStrings = new List { new() { diff --git a/test/Masa.Contrib.Isolation.Tests/TestIsolation.cs b/test/Masa.Contrib.Isolation.Tests/TestIsolation.cs index 01f512dba..a9061928c 100644 --- a/test/Masa.Contrib.Isolation.Tests/TestIsolation.cs +++ b/test/Masa.Contrib.Isolation.Tests/TestIsolation.cs @@ -12,8 +12,11 @@ public void TestGetDbContextOptionsList() var services = new ServiceCollection(); services.Configure(option => { - option.DefaultConnection = "data source=test2"; - option.Isolations = new() + option.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test2" + }; + option.IsolationConnectionStrings = new() { new() { @@ -107,7 +110,7 @@ public void TestUseIsolation6() options.Setup(option => option.Services).Returns(services).Verifiable(); options.Object.UseIsolation(isolationBuilder => isolationBuilder.UseMultiEnvironment()); var serviceProvider = services.BuildServiceProvider(); - Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationDbConnectionStringProvider)) == 1); + Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationMiddleware)) == 1); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); Assert.IsTrue(!serviceProvider.GetServices().Any()); @@ -123,7 +126,7 @@ public void TestUseIsolation7() options.Object.UseIsolation(isolationBuilder => isolationBuilder.UseMultiTenant()); var serviceProvider = services.BuildServiceProvider(); - Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationDbConnectionStringProvider)) == 1); + Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationMiddleware)) == 1); Assert.IsTrue(!serviceProvider.GetServices().Any()); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -141,7 +144,7 @@ public void TestUseIsolation8() => isolationBuilder.UseMultiTenant().UseMultiEnvironment()); var serviceProvider = services.BuildServiceProvider(); - Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationDbConnectionStringProvider)) == 1); + Assert.IsTrue(services.Count(service => service.ServiceType == typeof(IIsolationMiddleware)) == 2); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); diff --git a/test/Masa.Contrib.Isolation.Tests/TestParserProvider.cs b/test/Masa.Contrib.Isolation.Tests/TestParserProvider.cs index a88c61e87..6e49fdb35 100644 --- a/test/Masa.Contrib.Isolation.Tests/TestParserProvider.cs +++ b/test/Masa.Contrib.Isolation.Tests/TestParserProvider.cs @@ -374,23 +374,4 @@ public async Task TestEnvironmentVariablesParserAsync() Assert.IsTrue(environmentVariablesParserProvider.Name == "EnvironmentVariables"); Assert.IsTrue(handler); } - - [TestMethod] - public async Task TestEnvironmentVariablesParser2Async() - { - var services = new ServiceCollection(); - Mock environmentSetter = new(); - string environmentKey = "env"; - System.Environment.SetEnvironmentVariable(environmentKey, ""); - environmentSetter.Setup(setter => setter.SetEnvironment(It.IsAny())).Verifiable(); - services.AddScoped(_ => environmentSetter.Object); - services.Configure(option => - { - option.EnvironmentKey = environmentKey; - }); - var serviceProvider = services.BuildServiceProvider(); - var environmentVariablesParserProvider = new EnvironmentVariablesParserProvider(); - var handler = await environmentVariablesParserProvider.ResolveAsync(serviceProvider, environmentKey, _ => { }); - Assert.IsFalse(handler); - } } diff --git a/test/Masa.Contrib.Isolation.Tests/UnitOfWorkAccessor.cs b/test/Masa.Contrib.Isolation.Tests/UnitOfWorkAccessor.cs index e16da621f..2b9d6c1d2 100644 --- a/test/Masa.Contrib.Isolation.Tests/UnitOfWorkAccessor.cs +++ b/test/Masa.Contrib.Isolation.Tests/UnitOfWorkAccessor.cs @@ -1,6 +1,8 @@ // 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.Options; + namespace Masa.Contrib.Isolation.Tests; public class UnitOfWorkAccessor: IUnitOfWorkAccessor diff --git a/test/Masa.Contrib.Isolation.Tests/_Imports.cs b/test/Masa.Contrib.Isolation.Tests/_Imports.cs index 241d1e6a5..8dc426a07 100644 --- a/test/Masa.Contrib.Isolation.Tests/_Imports.cs +++ b/test/Masa.Contrib.Isolation.Tests/_Imports.cs @@ -1,11 +1,12 @@ // 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.Options; global using Masa.BuildingBlocks.Data.UoW; -global using Masa.BuildingBlocks.Data.UoW.Options; global using Masa.BuildingBlocks.Dispatcher.Events; -global using Masa.BuildingBlocks.Isolation; global using Masa.BuildingBlocks.Isolation.Environment; +global using Masa.BuildingBlocks.Isolation.Middleware; global using Masa.BuildingBlocks.Isolation.MultiTenant; global using Masa.BuildingBlocks.Isolation.Options; global using Masa.BuildingBlocks.Isolation.Parser; 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 fd4f337c4..0ff528134 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 @@ -20,11 +20,11 @@ - + diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs b/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs index 3e7b9b40f..e4c8cd870 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/TestIsolation.cs @@ -1,6 +1,8 @@ // 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] @@ -23,7 +25,7 @@ public void TestUseIsolationUoW() { eventBuilder.Object.UseIsolationUoW(_ => { - }, dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + }, dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); }, "Tenant isolation and environment isolation use at least one"); } @@ -34,7 +36,7 @@ public void TestUseIsolationUoW2() eventBuilder.Setup(builder => builder.Services).Returns(_services).Verifiable(); Assert.ThrowsException(() => { - eventBuilder.Object.UseIsolationUoW(null!, dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + eventBuilder.Object.UseIsolationUoW(null!, dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); }); } @@ -47,7 +49,7 @@ public void TestUseIsolationUoW3() { dispatcherOption.Object.UseIsolationUoW(_ => { - }, dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + }, dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); }, "Tenant isolation and environment isolation use at least one"); } @@ -67,7 +69,7 @@ public void TestUseIsolationUoWByUseEnvironment() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsNotNull(serviceProvider.GetService()); @@ -79,7 +81,7 @@ public void TestUseIsolationUoWByUseMultiEnvironment() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiEnvironment().UseMultiEnvironment(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -91,7 +93,7 @@ public void TestUseIsolationUoWByUseTenant() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsNotNull(serviceProvider.GetService()); @@ -103,7 +105,7 @@ public void TestUseIsolationUoWByUseMultiTenant() { Mock dispatcherOption = new(); dispatcherOption.Setup(builder => builder.Services).Returns(_services).Verifiable(); - dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseSqlite(_connectionString)); + dispatcherOption.Object.UseIsolationUoW(isolationBuilder => isolationBuilder.UseMultiTenant().UseMultiTenant(), dbOptionBuilder => dbOptionBuilder.UseTestSqlite(_connectionString)); var serviceProvider = dispatcherOption.Object.Services.BuildServiceProvider(); Assert.IsTrue(serviceProvider.GetServices().Count() == 1); @@ -178,8 +180,11 @@ public void TestUseMultiEnvironment() { _services.Configure(option => { - option.DefaultConnection = "data source=test4"; - option.Isolations = new List + option.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test4" + }; + option.IsolationConnectionStrings = new List { new() { @@ -229,8 +234,11 @@ public void TestUseMultiTenant() { _services.Configure(option => { - option.DefaultConnection = "data source=test7"; - option.Isolations = new List + option.ConnectionStrings = new ConnectionStrings() + { + DefaultConnection = "data source=test7" + }; + option.IsolationConnectionStrings = new List { new() { diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs b/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs index a2134c240..8f68488b0 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/_Imports.cs @@ -7,10 +7,10 @@ global using Masa.BuildingBlocks.Isolation.MultiTenant; global using Masa.BuildingBlocks.Isolation.Options; global using Masa.Contrib.Configuration; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Isolation.MultiEnvironment; global using Masa.Contrib.Isolation.MultiTenant; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Sqlite; global using Microsoft.AspNetCore.Builder; global using Microsoft.Data.Sqlite; global using Microsoft.EntityFrameworkCore; @@ -22,4 +22,3 @@ global using System; global using System.IO; global using System.Linq; - diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Tests/appsettings.json b/test/Masa.Contrib.Isolation.UoW.EF.Tests/appsettings.json index d60224647..dbbc90933 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Tests/appsettings.json +++ b/test/Masa.Contrib.Isolation.UoW.EF.Tests/appsettings.json @@ -1,18 +1,18 @@ { "ConnectionStrings": { - "DefaultConnection": "data source=test1", - "Isolations": [ - { - "TenantId": "*", - "Environment": "development", - "ConnectionString": "data source=test2", - "Score": 99 - }, - { - "TenantId": "00000000-0000-0000-0000-000000000002", - "Environment": "production", - "ConnectionString": "data source=test3" - } - ] - } + "DefaultConnection": "data source=test1" + }, + "IsolationConnectionStrings": [ + { + "TenantId": "*", + "Environment": "development", + "ConnectionString": "data source=test2", + "Score": 99 + }, + { + "TenantId": "00000000-0000-0000-0000-000000000002", + "Environment": "production", + "ConnectionString": "data source=test3" + } + ] } \ No newline at end of file diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/EdgeDriverTest.cs b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/EdgeDriverTest.cs index 8c96a6b11..1e4407e15 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/EdgeDriverTest.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/EdgeDriverTest.cs @@ -18,7 +18,7 @@ public void Initialize() _services = new ServiceCollection(); _services.AddSingleton(configurationRoot); _services.AddEventBus(eventBusBuilder => eventBusBuilder.UseIsolationUoW( - isolationBuilder => isolationBuilder.UseMultiTenant("tenant").UseMultiEnvironment("env"), dbOptions => dbOptions.UseSqlite())); + isolationBuilder => isolationBuilder.UseMultiTenant("tenant").UseMultiEnvironment("env"), dbOptions => dbOptions.UseFilter().UseSqlite())); System.Environment.SetEnvironmentVariable("env", "pro"); } diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/Masa.Contrib.Isolation.UoW.EF.Web.Tests.csproj b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/Masa.Contrib.Isolation.UoW.EF.Web.Tests.csproj index b585f216c..ca00f31a4 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/Masa.Contrib.Isolation.UoW.EF.Web.Tests.csproj +++ b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/Masa.Contrib.Isolation.UoW.EF.Web.Tests.csproj @@ -13,10 +13,11 @@ - + + diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/_Imports.cs b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/_Imports.cs index 96e1bd51e..0417473dd 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/_Imports.cs +++ b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/_Imports.cs @@ -1,16 +1,17 @@ // 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.Contracts.DataFiltering; global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.BuildingBlocks.Isolation; global using Masa.BuildingBlocks.Isolation.Environment; +global using Masa.Contrib.Data.Contracts.EF; +global using Masa.Contrib.Data.EntityFrameworkCore; +global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Dispatcher.Events; global using Masa.Contrib.Isolation.MultiEnvironment; global using Masa.Contrib.Isolation.MultiTenant; global using Masa.Contrib.Isolation.UoW.EF.Web.Tests.Events; -global using Masa.Utils.Data.EntityFrameworkCore; -global using Masa.Utils.Data.EntityFrameworkCore.Filters; -global using Masa.Utils.Data.EntityFrameworkCore.Sqlite; global using Masa.Utils.Security.Cryptography; global using Microsoft.AspNetCore.Http; global using Microsoft.Data.Sqlite; @@ -23,4 +24,3 @@ global using System.Collections.Generic; global using System.IO; global using System.Threading.Tasks; - diff --git a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/appsettings.json b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/appsettings.json index 460845f83..478264b26 100644 --- a/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/appsettings.json +++ b/test/Masa.Contrib.Isolation.UoW.EF.Web.Tests/appsettings.json @@ -1,18 +1,18 @@ { "ConnectionStrings": { - "DefaultConnection": "data source=test1", - "Isolations": [ - { - "TenantId": "1", - "Environment": "dev", - "ConnectionString": "data source=test2", - "Score": 99 - }, - { - "TenantId": "2", - "Environment": "pro", - "ConnectionString": "data source=test3" - } - ] - } + "DefaultConnection": "data source=test1" + }, + "IsolationConnectionStrings": [ + { + "TenantId": "1", + "Environment": "dev", + "ConnectionString": "data source=test2", + "Score": 99 + }, + { + "TenantId": "2", + "Environment": "pro", + "ConnectionString": "data source=test3" + } + ] } \ No newline at end of file