Browse Source

Merge branch 'dev' into Mapperly

pull/23277/head
Halil İbrahim Kalkan 6 months ago
committed by GitHub
parent
commit
39e97fb54f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      Directory.Packages.props
  2. 53
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  4. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/1752664190317-min.jpeg
  5. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15924-min.jpg
  6. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15933-min.jpg
  7. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15934-min.jpg
  8. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15941-min.jpg
  9. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15944-min.jpg
  10. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15946-min.jpg
  11. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15947-min.jpg
  12. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15948-min.jpg
  13. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15949-min.jpg
  14. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15956-min.jpg
  15. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15959-min.jpg
  16. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15963-min.jpg
  17. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15964-min.jpg
  18. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15966-min.jpg
  19. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15968-min.jpg
  20. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15969-min.jpg
  21. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15970-min.jpg
  22. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15971-min.JPG
  23. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15972-min.JPG
  24. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15973-min.JPG
  25. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15974-min.JPG
  26. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15975-min.JPG
  27. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15976-min.JPG
  28. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15977-min.JPG
  29. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15979-min.JPG
  30. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15980-min.JPG
  31. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15981-min.JPG
  32. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15982-min.JPG
  33. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15983-min.JPG
  34. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15984-min.jpg
  35. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15985-min.JPG
  36. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15986-min.JPG
  37. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15987-min.JPG
  38. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15989-min.jpg
  39. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15994-min.jpg
  40. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15995-min.jpg
  41. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15996-min.jpg
  42. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15998-min.jpg
  43. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15999-min.jpg
  44. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16001-min.jpg
  45. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16002-min.jpg
  46. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16003-min.jpg
  47. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16006-min.JPG
  48. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16007-min.jpg
  49. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16008-min.jpg
  50. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16009-min.jpg
  51. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16011-min.jpg
  52. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16012-min.jpg
  53. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16013-min.jpg
  54. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16019-min.JPG
  55. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16021-min.JPG
  56. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16022-min.JPG
  57. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16023-min.JPG
  58. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16024-min.JPG
  59. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16025-min.JPG
  60. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16026-min.JPG
  61. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16027-min.JPG
  62. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16028-min.JPG
  63. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16029-min.JPG
  64. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16030-min.JPG
  65. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16031-min.JPG
  66. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16032-min.jpg
  67. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16040-min.JPG
  68. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16041-min.JPG
  69. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/cover.png
  70. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/image-20250722203102576.png
  71. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-1.JPG
  72. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-2.JPG
  73. BIN
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-3.JPG
  74. 101
      docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/post.md
  75. 2
      docs/en/Community-Articles/2024-11-25-Global-Assets/POST.md
  76. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/Drawings.pptx
  77. 480
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md
  78. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-saas-tenants-page.png
  79. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration-select-dbcontext.png
  80. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration-set-name.png
  81. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration.png
  82. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-browse.png
  83. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-context-selection.png
  84. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-open-with-terminal.png
  85. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-solution-runner.png
  86. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/acme-tenant-screen.png
  87. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/added-product-entity-migration-main-context.png
  88. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/added-product-entity-migration-tenant-context.png
  89. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/builder-check-tenant-side.png
  90. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/dbcontext-factories.png
  91. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/dbmigrator-logs.png
  92. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/hybrid-database-multi-tenancy.png
  93. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/multi-tenancy-dbcontext-structure.png
  94. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-1.png
  95. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-2.png
  96. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-conn-string-1.png
  97. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-conn-string-2.png
  98. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/product-database-table.png
  99. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/separate-database.png
  100. BIN
      docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/separate-tenant-database-multi-tenancy.png

5
Directory.Packages.props

@ -136,7 +136,8 @@
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.80" />
<PackageVersion Include="Polly" Version="8.5.2" />
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-rc.1.efcore.9.0.0" />
<PackageVersion Include="MySql.EntityFrameworkCore" Version="9.0.6" />
<PackageVersion Include="Quartz" Version="3.14.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.14.0" />
<PackageVersion Include="Quartz.Plugins.TimeZoneConverter" Version="3.14.0" />
@ -186,4 +187,4 @@
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.9.2" />
</ItemGroup>
</Project>
</Project>

53
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -723,6 +723,57 @@
"MakeAnnouncement": "Make Announcement",
"MakeAnnouncementInfo": "Check it if you want to make an announcement for this post",
"Permission:ViewCounts": "View counts",
"ReadCount": "Read Count"
"ReadCount": "Read Count",
"Menu:Solution": "Solution",
"Enum:LicenseType:1": "Personal",
"Enum:LicenseType:2": "Team",
"Enum:LicenseType:3": "Business",
"Enum:LicenseType:4": "Enterprise",
"Enum:Template:0": "Unknown",
"Enum:Template:1": "App No Layers",
"Enum:Template:2": "App Layered",
"Enum:Template:3": "Microservice",
"Enum:UiFramework:0": "Unknown",
"Enum:UiFramework:1": "None",
"Enum:UiFramework:2": "Mvc Razor Pages",
"Enum:UiFramework:3": "Angular",
"Enum:UiFramework:4": "Blazor Wasm",
"Enum:UiFramework:5": "Blazor Server",
"Enum:UiFramework:6": "Blazor Web App",
"Enum:UiFramework:7": "Blazor MaUI",
"Enum:DatabaseProvider:0": "Unknown",
"Enum:DatabaseProvider:1": "None",
"Enum:DatabaseProvider:2": "EfCore",
"Enum:DatabaseProvider:3": "MongoDb",
"Enum:Dbms:0": "Unknown",
"Enum:Dbms:1": "None",
"Enum:Dbms:2": "SqlServer",
"Enum:Dbms:3": "PostgreSql",
"Enum:Dbms:4": "Oracle",
"Enum:Dbms:5": "OracleDevart",
"Enum:Dbms:6": "MySql",
"Enum:Dbms:7": "Sqlite",
"Enum:UiTheme:0": "Unknown",
"Enum:UiTheme:1": "None",
"Enum:UiTheme:2": "Basic",
"Enum:UiTheme:3": "LeptonX",
"Enum:UiTheme:4": "LeptonX Lite",
"Enum:UiThemeStyle:0": "Unknown",
"Enum:UiThemeStyle:1": "System",
"Enum:UiThemeStyle:2": "Dim",
"Enum:UiThemeStyle:3": "Dark",
"Enum:UiThemeStyle:4": "Light",
"Enum:MobileApp:0": "Unknown",
"Enum:MobileApp:1": "None",
"Enum:MobileApp:2": "Maui",
"Enum:MobileApp:3": "ReactNative",
"Enum:CreationTool:0": "Unknown",
"Enum:CreationTool:1": "StudioUI",
"Enum:CreationTool:2": "StudioCli",
"Enum:CreationTool:3": "OldCli",
"Menu:TelemetryMenu": "Telemetry Reports",
"Menu:Studio": "Studio",
"Menu:Solutions": "Solutions",
"Menu:Users": "Users"
}
}

4
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -1059,7 +1059,7 @@
"BuyNow": "Buy Now",
"PayViaAmexCard": "How can I pay via my AMEX card?",
"PayViaAmexCardDescription": "The default payment gateway 'Iyzico' may decline some AMEX credit cards due to security measures. In this case, you can pay through the alternative payment gateway '2Checkout'.",
"InvalidReCaptchaErrorMessage": "There was an error verifying reCAPTCHA. Please try again.",
"InvalidReCaptchaErrorMessage": "There was an error verifying reCAPTCHA.",
"YourCompanyName": "Your company name",
"FirstName": "First name",
"LastName": "Last name",
@ -1795,7 +1795,7 @@
"SpecialDiscount": "Special Discount",
"YourOrganizationOverview": "Your Organization Overview",
"TrainingDetailsHeaderInfo_TrainingHourSingular": "{0} hour",
"ContactPageError": "Please send your message via email to <a href=\"{0}\"> info@abp.io </a><br /> Here's what you wrote :",
"ContactPageError": "You can also send your message via email. Copy your message below and send to <a href=\"{0}\"> info@abp.io </a>",
"GoBack": "Go back",
"HereWhatYouWrote": "Here's what you wrote :",
"Sales": "Sales",

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/1752664190317-min.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15924-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15933-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15934-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15941-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15944-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15946-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15947-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15948-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15949-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15956-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15959-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15963-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15964-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15966-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15968-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15969-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15970-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15971-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15972-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15973-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15974-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15975-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15976-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15977-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15979-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15980-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15981-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15982-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15983-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15984-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15985-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15986-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15987-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15989-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15994-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15995-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15996-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15998-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_15999-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16001-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16002-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16003-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16006-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16007-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16008-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16009-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16011-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16012-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16013-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16019-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16021-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16022-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16023-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16024-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16025-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16026-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16027-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16028-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16029-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16030-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16031-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16032-min.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16040-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/IMG_16041-min.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/image-20250722203102576.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-1.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-2.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/my-talk-3.JPG

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

101
docs/en/Blog-Posts/2025-07-22-My-Impressionf-at-WeAreDevelopers/post.md

@ -0,0 +1,101 @@
# WeAreDevelopers 2025: A Speaker’s Impressions
![Conference Opening](IMG_15924-min.jpg)
After speaking at DotNext Moscow, I had high expectations for WeAreDevelopers 2025—and the event delivered on all fronts. Held in Berlin / Germany, it brought together a truly global crowd of developers, tech leaders, and innovators. As a speaker and software architect , I’m sharing my first-hand highlights, favorite moments, and candid scenes from this 2025’s conference.
## 🗣 My Talk
We have a good experience on multi-tenancy topic in SaaS development. My talk's topic was "Building Multi-Tenant ASP.NET Core Applications: Best Practices and Real-World Solutions". It was on the stage 4, 11 July Friday 10:20 am - 10:50 am and [this my presentation file](https://github.com/ebicoglu/presentations/blob/main/multi-tenancy-wearedevelopers-2025_30mins.pptx).
![My Talk Info](image-20250722203102576.png)
![Pictures from my talk](my-talk-1.JPG)
![Pictures from my talk](my-talk-2.JPG)
![Pictures from my talk](my-talk-3.JPG)
## 🏛 Huge Venue
![Main Stage](1752664190317-min.jpeg)
*The image is credited to WeAreDevelopers organization*
First of all, I had been in numerous software conferences, I must say that I've never seen such a big software event. The event spanned **500+ sessions across 20+ stages**, including the HR Leaders Summit for **2 full days**.
![Main Stage2](IMG_15933-min.jpg)
![Crowd Energy](IMG_15944-min.jpg)
------
## 🎤 Opening Keynote from GitHub
GitHub CEO Thomas Dohmke initiated the conference on the main stage with a talk on *“Agents for the Sake of Happiness”*. Having introduced Copilot three years ago here, he now launched bold predictions about autonomous AI‍—a fascinating evolution... He demonstrated GitHub Co-Pilot's AI and created a snake game. Altough it didn't work as he planned, we're developers we know live coding is hard. Actually that's because we shouldn't rely on AI. AI is not deterministic even though we set all those temperature, TopP, TopK parameters to minimum.
> AI is a good but not trustable friend!
![Thomas Dohmke on Stage](IMG_15941-min.jpg)
------
## 🧭 11 Parallel Stages: Rush
There were 11 stages where 11 different topics were being explained. And the sessions were 30 minutes. Actually that's the downside of this event. Because there were so nice talks that needs to be minimum 40 minutes. But anyway I understand the organization team because there are many smart speakers whose needs to be included in this event. So as a attendee I was on a hurry to pick the next talk even when I was listening to a talk :)
The venue consists of 3 buildings. So if you pick a talk on another building, you have 10 mins to go to toilet or drink something and catch the next session on that far building...
There was HR track with **3 stages and 2 full days** of HR/Talent Acquisition programming, it attracted a notable overlap of developers and HR pros. Themes included AI‑powered recruiting, remote work culture, mental health, diversity & inclusion, and building AI agents
![Fireside Chat](IMG_15949-min.jpg)
![Panel Discussion](IMG_15948-min.jpg)
------
## 🤖 AI & AI & AI & Others...
I'm one of those AI lovers. I love learning cutting-edge information. And as I see AI is being more trendy everyday. That's why most of the talks were about AI. Everything related to AI. I generaly attended AI related talks because I'm also working on some AI topics in Volosoft at the moment.
------
## 🤝 Expo Floor & Networking
The expo was a developer’s playground—cloud services, open‑source tools, startups, and enterprise platforms. I found new partners and reconnected with peers in a buzzing atmosphere. Everywhere was full of talking's even outside. If you want to get fresh air and drink coffee, you can go out and listen to the outside talks.
![Expo Hall](IMG_15956-min.jpg)
![Booth Visit](IMG_15959-min.jpg)
Networking wasn't just daytime chatter—hallway meetups and evening socials were unforgettable.
![Networking Moments](IMG_15964-min.jpg)
![After Hours](IMG_15972-min.JPG)
------
## 😂 Candid & Fun Moments
Swag stations, sponsor games, “developer selfies”—these lighter moments kept the vibe upbeat and human.
![Fun Moment](IMG_15971-min.JPG)
![Developer Selfie](IMG_15980-min.JPG)
------
## ✅ Final Thoughts & Looking Ahead
WeAreDevelopers 2025 was an unforgettable three-day ride: **15,000 tech minds**, **500+ sessions**, and a true **bridge between developers and HR**
I’m leaving with:
- Fresh strategies in GenAI and SaaS growth
- Stronger HR-tech understanding and crossover potential
- New professional connections—and fun memories
------
![Conference Wrap-Up](IMG_15999-min.jpg)

2
docs/en/Community-Articles/2024-11-25-Global-Assets/POST.md

@ -51,7 +51,7 @@ public class MyBlazorWebAssemblyBundlingModule : AbpModule
options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global).AddContributors(typeof(MyModuleBundleScriptContributor));
// Style Bundles
options.ScriptBundles.Get(BlazorWebAssemblyStandardBundles.Scripts.Global).AddContributors(typeof(MyModuleBundleStyleBundleContributor));
options.StyleBundles.Get(BlazorWebAssemblyStandardBundles.Styles.Global).AddContributors(typeof(MyModuleBundleStyleBundleContributor));
});
}
}

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/Drawings.pptx

Binary file not shown.

480
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md

@ -0,0 +1,480 @@
# Multi-Tenancy with Separate Databases in .NET and ABP Framework
[Multi-tenancy](https://abp.io/architecture/multi-tenancy) is a common architectural concept for modern SaaS applications, enabling a single application to serve multiple customers (each known as a tenant) while maintaining data isolation, scalability, and operational efficiency. The "Separate database per tenant" approach offers the highest level of data isolation, making it ideal for scenarios with strict data privacy, security, and performance requirements.
In this article, we’ll explore how to use this advanced multi-tenancy model using the powerful capabilities of the ABP Framework and the .NET platform.
> In this article, I will use [ABP Studio](https://abp.io/studio) for creating the application. ABP Studio allows to select "separate database per tenant" option only for [commercial licenses](https://abp.io/pricing).
## Understanding Database Models for a Multi-Tenant Application
In the next sections, I will explain various models for database models of a multi-tenant solution:
* Single (shared) Database Model
* Separate Tenant Databases Model
* Hybrid Multi-Tenant Database Model
Let's start with the first one...
### Single (shared) Database Model
In the shared database model, all the application data stored in a single physical database. In the following diagram, you see different kind of users use the application, and the application stored their data in a main database:
![single-shared-database](single-shared-database.png)
This is the default behavior when you [create a new ABP application](https://abp.io/docs/latest/get-started), because it is simple to begin with and proper for must applications.
In this model, a single database table may contain data of multiple tenants. Each row in these tables have a `TenantId` field which is used to distinguish the tenant data and isolate a tenant's data from other tenant users. To make your entities multi-tenant aware, all you have to do is to implement the `IMultiTenant` interface provided by the ABP Framework.
Here, is an example `Product` entity that should support multi-tenancy:
````csharp
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace MtDemoApp
{
public class Product : AggregateRoot<Guid>, IMultiTenant //Implementing the interface
{
public Guid? TenantId { get; set; } //Defined by the IMultiTenant interface
public string Name { get; set; }
public float Price { get; set; }
}
}
````
In this way, ABP Framework automatically isolates data using the `TenantId` property. You don't need to care about how to set `TenantId` or filter data when you need to query from database - all automated.
### Separate Tenant Databases Model
In the separate tenant database model, each tenant has a dedicated physical database (with a separate connection string), as shown below:
![separate-tenant-database-multi-tenancy](separate-tenant-database-multi-tenancy.png)
ABP Framework can automatically select the right database from the current user's tenant context. Again, it is completely automated. You just need to set a connection string for a tenant, as we will do later in this article.
Even each tenant has a separate database, we still need to a main database to store host-side data, like a table of tenants, their connection strings and some other management data for tenants. Also, tenant-independent (or tenant-shared) application data is stored in the main database.
### Hybrid Multi-Tenant Database Model
Lastly, you may want to have a hybrid model, where some tenants shares a single database (they don't have separate databases) but some of them have dedicated databases. In the following figure, Tenant C has its own physical database, but all other tenants data stored in the main database of the application.
![hybrid-database-multi-tenancy](hybrid-database-multi-tenancy.png)
ABP Framework handles the complexity: If a tenant has a separate database it uses that tenant's database, otherwise it filters the tenant data by the `TenantId` field in shared tables.
## Understanding the Separate Tenant Schema Approach
When you create a new ABP solution, it has a single `DbContext` class (for Entity Framework Core) by default. It also includes the necessary EF Core code-first database migrations to create and update the database. As a result of this approach, the main database schema (tables and their fields) will be identical with a tenant database schema. As a drawback of that, tenant databases have some tables that are not meaningful and not used. For example, Tenants table (a list of tenants) will be created in the tenant database, but will never be used (because tenant list is stored in the main database).
As a solution to that problem, ABP Studio provides a "Use separate tenant schema" option on the Multi-Tenancy step of the solution creation wizard:
![separate-tenant-schema-option](separate-tenant-schema-option.png)
This option is only available for the [Layered Monolith (optionally Modular) Solution Template](https://abp.io/docs/latest/get-started/layered-web-application). We don't provide that option in other templates, because:
* [Single-Layer](https://abp.io/docs/latest/get-started/single-layer-web-application) template is recommended for more simpler applications with an easy-to-understand architecture. We don't want to add these kind of complications in that template.
* [Microservice](https://abp.io/docs/latest/get-started/microservice) template already has a separate database for each service. Having multiple database schema (and multiple `DbContext` classes) for each service makes it over complicated without bringing much value.
While you can manually convert your applications so they support separate database schema approach (ABP is flexible), it is not recommended to do it for these solution types.
> Note that "Separate database per tenant" approach is already supported by default for the Single-Layer template too. "Separate tenant schema" is something different as I explained in this section.
## Creating a new Application
Follow the *[Get Started tutorial](https://abp.io/docs/latest/get-started/layered-web-application)* to create a new ABP application. Remember to select the "*Use separate tenant schema*" option since I want to demonstrate it in this article.
## Understanding the DbContext Structure
When you open the solution in your IDE, you will see the following structure under the `.EntityFrameworkCore` project:
![multi-tenancy-dbcontext-structure](multi-tenancy-dbcontext-structure.png)
There are 3 DbContext-related classes here (MtDemoApp is your application name):
* `MtDemoAppDbContext` class is used to map entities for the main (host + shared) database.
* `MtDemoAppTenantDbContext` class is used to map entities for tenant that have separate physical databases.
* `MtDemoAppDbContextBase` is an abstract base class for the classes explained above. In this way, you can configure common mapping logic here.
Let's see these classes a bit closer...
### The Main `DbContext` Class
Here the main `DbContext` class:
````csharp
public class MtDemoAppDbContext : MtDemoAppDbContextBase<MtDemoAppDbContext>
{
public MtDemoAppDbContext(DbContextOptions<MtDemoAppDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.SetMultiTenancySide(MultiTenancySides.Both);
base.OnModelCreating(builder);
}
}
````
* It inherits from the `MtDemoAppDbContextBase` as I mentioned before. So, any configuration made in the base class is also valid here.
* `OnModelCreating` overrides the base method and sets the multi-tenancy side as `MultiTenancySides.Both`. `Both` means this database can store host data as well as tenant data. This is needed because we store data in this database for the tenants who don't have a separate database.
### The Tenant `DbContext` class
Here is the tenant-specific `DbContext` class:
````csharp
public class MtDemoAppTenantDbContext : MtDemoAppDbContextBase<MtDemoAppTenantDbContext>
{
public MtDemoAppTenantDbContext(DbContextOptions<MtDemoAppTenantDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.SetMultiTenancySide(MultiTenancySides.Tenant);
base.OnModelCreating(builder);
}
}
````
The only difference is that we used `MultiTenancySides.Tenant` as the multi-tenancy side here, since this `DbContext` will only have entities/tables for tenants that have separate databases.
### The Base `DbContext` Class
Here is the base `DbContext` class:
````csharp
public abstract class MtDemoAppDbContextBase<TDbContext> : AbpDbContext<TDbContext>
where TDbContext : DbContext
{
public MtDemoAppDbContextBase(DbContextOptions<TDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentityPro();
builder.ConfigureOpenIddictPro();
builder.ConfigureFeatureManagement();
builder.ConfigureLanguageManagement();
builder.ConfigureSaas();
builder.ConfigureTextTemplateManagement();
builder.ConfigureBlobStoring();
builder.ConfigureGdpr();
/* Configure your own tables/entities inside here */
//builder.Entity<YourEntity>(b =>
//{
// b.ToTable(MtDemoAppConsts.DbTablePrefix + "YourEntities", MtDemoAppConsts.DbSchema);
// b.ConfigureByConvention(); //auto configure for the base class props
// //...
//});
//if (builder.IsHostDatabase())
//{
// /* Tip: Configure mappings like that for the entities only
* available in the host side,
// * but should not be in the tenant databases. */
//}
}
}
````
This `DbContext` class configures database mappings for all the [application modules](https://abp.io/docs/latest/modules) used by this application by calling their extension methods, like `builder.ConfigureBackgroundJobs()`. Each of these extension methods are defined as multi-tenancy aware and care about what you've set for the multi-tenancy side.
### Where to Configure Your Entities?
You can configure your entity mappings in the `OnModelCreating` method in any of the `DbContext` classes that was explained:
* If you configure in the main `DbContext` class, these configuration will be valid only for the main database. So, don't configure tenant-related configuration here, otherwise, it won't be applied for the tenants who have separate databases.
* If you configure in the tenant `DbContext` class, it will be valid only for the tenants with separate databases. You rarely need to do that. You typically want to make same configuration in the base `DbContext` to support hybrid scenarios (some tenants use the main (shared) database and some tenants have separate databases).
* If you configure in the base `DbContext` class, it will be valid for the main database and tenant databases. You typically define tenant-related configuration here. That means, if you have a multi-tenant `Product` entity, then you should define its EF Core database mapping configuration here, so the Products table is created in the main database as well as in the tenant databases.
The recommended approach is to configure all the mapping in the base class, but add controls like `builder.IsHostDatabase()` and `builder.IsTenantDatabase()` to conditionally configure the mappings:
![builder-check-tenant-side](builder-check-tenant-side.png)
## Adding Database Migrations
In this section, I will show how to configure your entity mappings, generate database migrations and apply to the database.
### Defining an Entity
Let's define a `Product` entity in the `.Domain` layer of your application:
````csharp
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace MtDemoApp
{
public class Product : AggregateRoot<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public string Name { get; set; }
public float Price { get; set; }
}
}
````
### Configuring the Database Mapping
Open the `MtDemoAppDbContextBase` class and add the following property to the class:
````csharp
public DbSet<Product> Products { get; set; }
````
Then add the following mapping code inside the `OnModelCreating` method (after all other existing code):
````csharp
builder.Entity<Product>(b =>
{
b.ToTable(MtDemoAppConsts.DbTablePrefix + "Products", MtDemoAppConsts.DbSchema);
b.ConfigureByConvention(); //auto-configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(100);
});
````
We made the configuration in the base class since the `Products` table should be created in all databases, not only in the main database.
>`DbTablePrefix` and `DbSchema` are optional and configurable in your application. You can change or remove them.
### Add a New Database Migration for the Main Database
To add a new EF Core database migration, we can use ABP Studio UI or EF Core command-line commands. I will show both of these approaches here.
#### Using the ABP Studio "Add Migrations" UI
You can right-click the `.EntityFrameworkCore` package in the ABP Studio's *Solution Explorer* panel and select *EF Core CLI* -> *Add Migration* command as shown below:
![abp-studio-add-migration](abp-studio-add-migration.png)
You set a migration name on the opened dialog:
![abp-studio-add-migration-set-name](abp-studio-add-migration-set-name.png)
If you select the *Update Database* checkbox it will apply changes to the database after generating the migration code.
Lastly, select the main DbContext class for this migration:
![abp-studio-add-migration-select-dbcontext](abp-studio-add-migration-select-dbcontext.png)
This dialog is shown when your application has multiple `DbContext` classes. Once you click the *OK* button, a new migration class is added under the `Migrations` folder of the `.EntityFrameworkCore` project (you can see in your coding editor):
![added-product-entity-migration-main-context](added-product-entity-migration-main-context.png)
Since we selected the *Update Database* option, the database table is also created. The following screenshot shows the `AppProducts` table (`App` is the default prefix for your tables, but you can change or remove it) in Microsoft SQL Server Management Studio:
![product-database-table](product-database-table.png)
#### Using a Command-Line Terminal
If you prefer to use the command-line terminal (instead of ABP Studio UI), open a command-line terminal in the directory of the `.EntityFrameworkCore` project. As a shortcut, you can right-click the `.EntityFrameworkCore` project in ABP Studio, then select *Open with* -> *Terminal* command as shown in the following figure:
![abp-studio-open-with-terminal](abp-studio-open-with-terminal.png)
Then you can use the [EF Core command-line tool](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) to add a new database migration:
````bash
dotnet ef migrations add "Added_Product_Entity" --context MtDemoAppDbContext
````
It is important to set the `--context` parameter since we have two DbContext classes in the same project.
After adding the migration, you can update the database:
````bash
dotnet ef database update "Added_Product_Entity" --context MtDemoAppDbContext
````
> If you are using Visual Studio, you can also use the [Package Manager Console](https://learn.microsoft.com/en-us/ef/core/cli/powershell) inside your IDE to add migrations and update the database.
### Add a New Database Migration for the Tenant Database
We added a database migration for the main (shared) database. We also need to add a new database migration for tenants who have separate databases.
This time, no need to configure the DbContext since we did it in the base DbContext class, so it is valid for both of the DbContext classes. Just right-click the `.EntityFrameworkCore` package in the ABP Studio's *Solution Explorer* panel and select *EF Core CLI* -> *Add Migration* command as shown below:
![abp-studio-add-migration](abp-studio-add-migration.png)
You can set the same or a different migration name here:
![abp-studio-add-migration-set-name](abp-studio-add-migration-set-name.png)
The important part is to select the Tenant DbContext in the next dialog, because we want to change the tenant database this time:
![abp-studio-context-selection](abp-studio-context-selection.png)
After clicking the *OK* button, it will add a new database migration class, but this time to the `TenantMigrations` folder:
![added-product-entity-migration-tenant-context](added-product-entity-migration-tenant-context.png)
ABP Studio is smart enough to select the right folder name for the new migration by mapping with the DbContext name. However, you could manually type `TenantMigrations` in the *Output directory* textbox.
Since we selected the *Update Database* option, it also applied changes to the database. But, which database? Interestingly, it automatically creates a second database for tenants with the project name + `_Tenant` suffix:
![tenant-database](tenant-database.png)
> This new database is never used on runtime or production. It is only created to allow you to see the schema (tables and their fields) on development time to be sure that everything is as expected. As you see, some tables (like `Saas*` and `OpenIddict*`) are not available in that database, since they are used on the host side and only necessary to be in the main database.
>
> So, where is the real tenant database? If a tenant's database is dedicated (separate), it is created on runtime as I will explain in the *Managing Tenant Databases and Connection Strings* section later.
You can see that database's connection string in the `appsettings.development.json` file of the `.DbMigrator` project in the solution. If you want to understand how it works, you can check source code of the `DbContextFactory` classes in the `.EntityFrameworkCore` project:
![dbcontext-factories](dbcontext-factories.png)
These factory classes are used to create `DbContext` instances when you execute *Add Migration* and *Update Database* commands.
## Managing Tenant Databases and Connection Strings
Until now, we even didn't run the application. It is the time to do it.
### Running the Application with ABP Studio
You can run the `.Web` project in your IDE. But I prefer to use ABP Studio's *[Solution Runner](https://abp.io/docs/latest/studio/running-applications)* feature here. You can open the *Solution Runner* panel in *ABP Studio* and click the play icon near to the solution root (`MyDemoApp`):
![abp-studio-solution-runner](abp-studio-solution-runner.png)
Once the application runs (and you see the blue link icon near to it), right click and select the *Browse* command:
![abp-studio-browse](abp-studio-browse.png)
It will open the application's UI in the built-in browser of ABP Studio. You can Login the application (with `admin` as user name and `1q2w3E*` as the default password) and navigate to the *Saas* -> *Tenants* page.
### Creating a New Tenant with the Shared Database
The *Tenants* page of the [SaaS module](https://abp.io/modules/Volo.SaaS) is shown below:
![abp-saas-tenants-page](abp-saas-tenants-page.png)
As you see, there is no tenant at the beginning. I can click the *+ New tenant* button to create the first tenant:
![new-tenant-dialog-1](new-tenant-dialog-1.png)
On this screen, we can set the base tenant information. If you click the *Database connection strings* tab, you can see the following UI:
![new-tenant-dialog-conn-string-1](new-tenant-dialog-conn-string-1.png)
For this first tenant, I will keep it as default and use the shared (main) database for this tenant's data. After clicking the *Save* button, the tenant is created and an initial [data seed](https://abp.io/docs/latest/framework/infrastructure/data-seeding) operation is automatically performed for us. To see an example, you can open the database, show rows of the `AbpUsers` table:
![users-table-new-tenant](users-table-new-tenant.png)
As you see, a new `admin` user has been created with a `TenantId`. The first row is the `admin` user of the host side. So, ABP allows to define same user name in different tenants, because their data (users in this example) are completely isolated from each other.
### Sign in with the new Tenant
We created a new tenant. In this step, we will sign in with the new tenant's `admin` user to see the application UI by that new tenant. To do that, we should logout from the host admin user first. Click the user name (`admin`) on the top right area of the application and select the *Log out* command:
![user-logout](user-logout.png)
Click the *Login* button again, which redirects you to the *Login* page:
![user-login](user-login.png)
In this page, click the *switch* button near to the *TENANT* selection area and type `acme` as *Name*:
![switch-tenant-dialog](switch-tenant-dialog.png)
Once you click the *Save* button, you are now in the acme tenant's context. You can see it on the *TENANT* selection area:
![tenant-acme-name](tenant-acme-name.png)
> This kind of tenant switch feature is very useful in development to quickly change tenants to test your application. However, in production, you typically want to use subdomain/domain names or another mechanism to determine tenants automatically. When you configure domain based resolution, the tenant selection area is automatically disappears from the login page. You can check the [multi-tenant document](https://abp.io/docs/latest/framework/architecture/multi-tenancy) to learn how to configure it.
After switching to the `acme` tenant, we can use `admin` as user name and the password you set during the tenant creation (I had set it as `1q2w3E*`) to login to the application.
Here a screenshot from the *Roles* page after signing in as the `acme` tenant's `admin` user:
![acme-tenant-screen](acme-tenant-screen.png)
> Notice that each tenant has its own roles, users, permissions, and other data. If you change roles here, it doesn't affect other tenants or the host side.
>
> Also, you can see that there are less menu items compared to host side. For example, tenant management page is not available for tenants as you can expect.
### Switch Back to the Host Side
To switch back to the host side to add a new tenant, logout from the application, click the *Login* button again to open the login page and then again click the *switch* button to change the current tenant context:
![switch-host-side](switch-host-side.png)
In this dialog, clear the *Name* field and then *Save* the dialog to switch back to the host side. Then you can use the standard `admin` user name with `1q2w3E*` password to login to the application as the host administrator.
### Creating a New Tenant with a Separate Database
Finally, we came to the point that we will create a new tenant with a separate, dedicated database. Open the *Tenants* page of the SaaS module and click the *+ New tenant* button:
![new-tenant-dialog-2](new-tenant-dialog-2.png)
Just fill these information as you wish, then open the *Database connection strings* tab:
![new-tenant-dialog-conn-string-2](new-tenant-dialog-conn-string-2.png)
Uncheck the *Use the shared database* option and set a connection string to the *Default connection string* for this tenant. I used `Server=(LocalDb)\MSSQLLocalDB;Database=MtDemoApp_Volosoft;Trusted_Connection=True;TrustServerCertificate=true` as the connection string value. The database name is `MtDemoApp_Volosoft`. You can Test the connection string to be sure that it is a valid connection string.
Once you click the *Save* button, the new tenant is created, a new database is created on the fly, all the database migrations are applied and the initial data seed is performed. You can open the SQL Server Management Studio to see the new database:
![separate-database](separate-database.png)
You can check the tables (e.g. `AbpUsers`) to see that only this new tenant's data is stored in this database. To test the application, switch to the Volosoft tenant (as like explained in the *Sign in with the new Tenant* section before), create a new role or user and check the database.
## Migrating Existing Tenant Databases
In the previous section, we've seen that a tenant database is automatically created on runtime if you set a connection string for that tenant. Also, all the current migrations are automatically applied to the database, so it becomes up to date.
But what about existing tenant databases when a new migration is added to the application? Maybe you have a few tenants with their separate databases, or you may have thousands of tenants with separate databases. How will you apply database schema changes to all of these databases?
The startup template comes with a solution to this problem. There is a `.DbMigrator` console application in the solution that is responsible to apply schema (table and their fields) changes to all of the databases in the system (the main database and all the separate tenant databases). It also executes the data seeding if seed data is available. All you need to do is to execute this application on your production environment while deploying a new version of your application (of course, it is also very useful in the development environment). It checks and upgrades all the databases before the new version of your application is deployed.
Here is the console log screen when I run the `.DbMigrator` application on my development environment:
![dbmigrator-logs](dbmigrator-logs.png)
As you can see in the logs, it first migrates for the main (host) database, then migrates the tenant databases one by one. It doesn't make schema migration for the `acme` tenant since it has not a separate database, but uses the main database.
In brief, when you make changes on your entity classes;
1) Add a new migration for the main DbContext class as I explained in this article.
2) Add a new migration for the tenant DbContext class as I explained in this article.
3) Run the `.DbMigrator` application in your development environment to ensure all the databases are up to date.
4) When you deploy your application to production or test environments, remember to run the `.DbMigrator` application first, then update your application. Or better, setup a CI/CD pipeline that automates this process. You can run the `.DbMigrator` every time while deploying the application, regardless of whether there is a schema change or not.
> If you have too many tenants with separate database, then the migration process may take too much time. `.DbMigrator` provides the fundamental solution. But for more advanced scenarios or bigger systems, you can always develop your own solution. Just check the `.DbMigrator` application to understand how it was implemented. All the necessary code located in your solution, so you can easily understand and freely customize.
## Conclusion
In this article, I covered two important aspects of multi-tenant application development:
* How ABP startup templates provide a multi-tenant application setup, so some tenants may store their data in a single (main, shared) database while some others may have their own dedicated database.
* Demonstrate how it can manage database migration process on the fly for multiple databases.
I started by defining different database models for multi-tenant applications (Single database, separate databases, and hybrid), showed how to create an ABP application that supports hybrid model, explained the DbContext structure that is coming with the solution template, demonstrated how to define entities, create and apply database migrations in such an application.
I hope this article gives you a good understanding the problem and the solution provided by the ABP Framework. Please write your questions or comments under this article.
Enjoy coding! :)
## Further Reading
* [ABP Multi-Tenancy document](https://abp.io/docs/latest/framework/architecture/multi-tenancy)
* [Multi-Tenancy Architecture with .NET](https://abp.io/architecture/multi-tenancy)

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-saas-tenants-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration-select-dbcontext.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration-set-name.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-add-migration.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-browse.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-context-selection.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-open-with-terminal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/abp-studio-solution-runner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/acme-tenant-screen.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/added-product-entity-migration-main-context.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/added-product-entity-migration-tenant-context.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/builder-check-tenant-side.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/dbcontext-factories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/dbmigrator-logs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/hybrid-database-multi-tenancy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/multi-tenancy-dbcontext-structure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-conn-string-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/new-tenant-dialog-conn-string-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/product-database-table.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/separate-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/separate-tenant-database-multi-tenancy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save