@ -1,3 +1,107 @@ |
|||
# Tenant Management Module |
|||
|
|||
TODO |
|||
[Multi-Tenancy](../Multi-Tenancy.md) is one of the core features of ABP Framework. It provides the fundamental infrastructure to build your own SaaS (Software-as-a-Service) solution. ABP's multi-tenancy system abstracts where your tenants are stored, by providing the `ITenantStore` interface. All you need to do is to implement that interface. |
|||
|
|||
**The Tenant Management module is an implementation of the the `ITenantStore` interface. It stores tenants in a database. It also provides UI to manage your tenants and their [features](../Features.md).** |
|||
|
|||
> Please **refer to the [Multi-Tenancy](../Multi-Tenancy.md) documentation** to understand the multi-tenancy system of the ABP Framework. This document focuses on the Tenant Management module. |
|||
|
|||
### About the ABP Commercial SaaS Module |
|||
|
|||
The [SaaS Module](https://commercial.abp.io/modules/Volo.Saas) is an alternative implementation of this module with more features and possibilities. It is distributed as a part of the [ABP Commercial](https://commercial.abp.io/) subscription. |
|||
|
|||
## How to Install |
|||
|
|||
This module comes as pre-installed (as [NuGet/NPM packages](NuGet/NPM packages)) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module. |
|||
|
|||
### The Source Code |
|||
|
|||
The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/tenant-management). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it. |
|||
|
|||
## User Interface |
|||
|
|||
This module adds "*Administration -> Tenant Management -> Tenants*" menu item to the main menu of the application, which opens the page shown below: |
|||
|
|||
 |
|||
|
|||
In this page, you see the all the tenants. You can create a new tenant as shown below: |
|||
|
|||
 |
|||
|
|||
In this modal; |
|||
|
|||
* **Name**: The unique name of the tenant. If you use subdomains for your tenants (like https://some-tenant.your-domain.com), this will be the subdomain name. |
|||
* **Admin Email Address**: Email address of the admin user for this tenant. |
|||
* **Admin Password**: The password of the admin user for this tenant. |
|||
|
|||
When you click to *Actions* button near to a tenant, you will see the actions you can take: |
|||
|
|||
 |
|||
|
|||
### Managing the Tenant Features |
|||
|
|||
The Features action opens a modal to enable/disable/set [features](../Features.md) for the related tenant. Here, an example modal: |
|||
|
|||
 |
|||
|
|||
### Managing the Host Features |
|||
|
|||
*Manage Host features* button is used to set features for the host side, if you use the features of your application also in the host side. |
|||
|
|||
## Internals |
|||
|
|||
This section can be used as a reference if you want to [customize](../Customizing-Application-Modules-Guide.md) this module without changing [its source code](https://github.com/abpframework/abp/tree/dev/modules/tenant-management). |
|||
|
|||
### Domain Layer |
|||
|
|||
#### Aggregates |
|||
|
|||
* `Tenant` |
|||
|
|||
#### Repositories |
|||
|
|||
* `ITenantRepository` |
|||
|
|||
#### Domain Services |
|||
|
|||
* `TenantManager` |
|||
|
|||
### Application Layer |
|||
|
|||
#### Application Services |
|||
|
|||
* `TenantAppService` |
|||
|
|||
#### Permissions |
|||
|
|||
- `AbpTenantManagement.Tenants`: Tenant management. |
|||
- `AbpTenantManagement.Tenants.Create`: Creating a new tenant. |
|||
- `AbpTenantManagement.Tenants.Update`: Editing an existing tenant. |
|||
- `AbpTenantManagement.Tenants.Delete`: Deleting an existing tenant. |
|||
- `AbpTenantManagement.Tenants.ManageFeatures`: Manage features of the tenants. |
|||
|
|||
### Entity Framework Core Integration |
|||
|
|||
* `TenantManagementDbContext` (implements `ITenantManagementDbContext`) |
|||
|
|||
**Database Tables:** |
|||
|
|||
* `AbpTenants` |
|||
* `AbpTenantConnectionStrings` |
|||
|
|||
### MongoDB Integration |
|||
|
|||
* `TenantManagementMongoDbContext` (implements `ITenantManagementMongoDbContext`) |
|||
|
|||
**Database Collections:** |
|||
|
|||
* `AbpTenants` (also includes the connection string) |
|||
|
|||
## Notices |
|||
|
|||
ABP Framework allows to use *database per tenant* approach that allows a tenant can have a dedicated database. This module has the fundamental infrastructure to make that implementation possible (see its source code), however it doesn't implement the application layer and UI functionalities to provide it as an out of the box implementation. You can implement these features yourself, or consider to use the [ABP Commercial Saas Module](https://docs.abp.io/en/commercial/latest/modules/saas) that fully implements it and provides much more business features. |
|||
|
|||
## See Also |
|||
|
|||
* [Multi-Tenancy](../Multi-Tenancy.md) |
|||
* [ABP Commercial SaaS Module](https://docs.abp.io/en/commercial/latest/modules/saas) |
|||
@ -1,37 +0,0 @@ |
|||
# Custom Setting Page |
|||
|
|||
There are several settings tabs from different modules. You can add a custom setting page to your project. |
|||
|
|||
1. Create a component with the following command: |
|||
|
|||
```bash |
|||
yarn ng generate component my-settings |
|||
``` |
|||
|
|||
2. Open the `app.component.ts` and modify the file as shown below: |
|||
|
|||
```js |
|||
import { Component } from '@angular/core'; |
|||
import { SettingTabsService } from '@abp/ng.core'; // imported SettingTabsService |
|||
import { MySettingsComponent } from './my-settings/my-settings.component'; // imported MySettingsComponent |
|||
|
|||
@Component(/* component metadata */) |
|||
export class AppComponent { |
|||
constructor(private settingTabs: SettingTabsService) // injected MySettingsComponent |
|||
{ |
|||
// added below |
|||
settingTabs.add([ |
|||
{ |
|||
name: 'MySettings', |
|||
order: 1, |
|||
requiredPolicy: 'policy key here', |
|||
component: MySettingsComponent, |
|||
}, |
|||
]); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Navigate to `/setting-management` route to see the changes: |
|||
|
|||
 |
|||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 25 KiB |
@ -1,41 +0,0 @@ |
|||
# 自定义设置页面 |
|||
|
|||
不同的模块提供它们的设置选项卡. 你可以通过3个步骤在项目中自定义设置页面. |
|||
|
|||
1. 使用以下命令创建一个组件 |
|||
|
|||
```bash |
|||
yarn ng generate component my-settings |
|||
``` |
|||
|
|||
2. 打开 `app.component.ts` 做以下修改: |
|||
|
|||
```js |
|||
import { Component } from '@angular/core'; |
|||
import { SettingTabsService } from '@abp/ng.core'; // imported SettingTabsService |
|||
import { MySettingsComponent } from './my-settings/my-settings.component'; // imported MySettingsComponent |
|||
|
|||
@Component(/* component metadata */) |
|||
export class AppComponent { |
|||
constructor(private settingTabs: SettingTabsService) // injected MySettingsComponent |
|||
{ |
|||
// added below |
|||
settingTabs.add([ |
|||
{ |
|||
name: 'MySettings', |
|||
order: 1, |
|||
requiredPolicy: 'policy key here', |
|||
component: MySettingsComponent, |
|||
}, |
|||
]); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
导航到 `/setting-management` 路由你会看到以下变化: |
|||
|
|||
 |
|||
|
|||
## 下一步是什么? |
|||
|
|||
- [懒加载 Scripts 与 Styles](./Lazy-Load-Service.md) |
|||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 25 KiB |
@ -1,14 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,17 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<PackageId>Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme</PackageId> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Components.Web.BasicTheme\Volo.Abp.AspNetCore.Components.Web.BasicTheme.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.AspNetCore.Components.WebAssembly.Theming\Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Http.Client.IdentityModel.WebAssembly\Volo.Abp.Http.Client.IdentityModel.WebAssembly.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.LIbs; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class InstallLibsCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public ILogger<InstallLibsCommand> Logger { get; set; } |
|||
|
|||
protected IInstallLibsService InstallLibsService { get; } |
|||
|
|||
public InstallLibsCommand(IInstallLibsService installLibsService) |
|||
{ |
|||
InstallLibsService = installLibsService; |
|||
Logger = NullLogger<InstallLibsCommand>.Instance; |
|||
} |
|||
|
|||
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var workingDirectoryArg = commandLineArgs.Options.GetOrNull( |
|||
Options.WorkingDirectory.Short, |
|||
Options.WorkingDirectory.Long |
|||
); |
|||
|
|||
var workingDirectory = workingDirectoryArg ?? Directory.GetCurrentDirectory(); |
|||
|
|||
if (!Directory.Exists(workingDirectory)) |
|||
{ |
|||
throw new CliUsageException( |
|||
"Specified directory does not exist." + |
|||
Environment.NewLine + Environment.NewLine + |
|||
GetUsageInfo() |
|||
); |
|||
} |
|||
|
|||
await InstallLibsService.InstallLibsAsync(workingDirectory); |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp install-libs"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("-wd|--working-directory <directory-path> (default: empty)"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetShortDescription() |
|||
{ |
|||
return "Install NPM Packages for MVC / Razor Pages and Blazor Server UI types."; |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class WorkingDirectory |
|||
{ |
|||
public const string Short = "wd"; |
|||
public const string Long = "working-directory"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Cli.LIbs |
|||
{ |
|||
public interface IInstallLibsService |
|||
{ |
|||
Task InstallLibsAsync(string directory); |
|||
} |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.FileSystemGlobbing; |
|||
using Microsoft.Extensions.FileSystemGlobbing.Abstractions; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using NuGet.Versioning; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.LIbs |
|||
{ |
|||
public class InstallLibsService : IInstallLibsService, ITransientDependency |
|||
{ |
|||
public const string LibsDirectory = "./wwwroot/libs"; |
|||
|
|||
public ILogger<InstallLibsService> Logger { get; set; } |
|||
|
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public InstallLibsService(IJsonSerializer jsonSerializer) |
|||
{ |
|||
_jsonSerializer = jsonSerializer; |
|||
Logger = NullLogger<InstallLibsService>.Instance; |
|||
} |
|||
|
|||
public async Task InstallLibsAsync(string directory) |
|||
{ |
|||
var projectFiles = Directory.GetFiles(directory, "*.csproj"); |
|||
if (!projectFiles.Any()) |
|||
{ |
|||
Logger.LogError("No project file found in the directory."); |
|||
return; |
|||
} |
|||
|
|||
if (!await CanInstallLibs(directory)) |
|||
{ |
|||
Logger.LogWarning( |
|||
"abp install-libs command is available for MVC, Razor Page, and Blazor-Server UI types"); |
|||
return; |
|||
} |
|||
|
|||
if (!IsNpmInstalled()) |
|||
{ |
|||
Logger.LogWarning("NPM is not installed, visit https://nodejs.org/en/download/ and install NPM"); |
|||
return; |
|||
} |
|||
|
|||
if (IsYarnAvailable()) |
|||
{ |
|||
RunYarn(directory); |
|||
} |
|||
else |
|||
{ |
|||
RunNpmInstall(directory); |
|||
} |
|||
|
|||
await CleanAndCopyResources(directory); |
|||
} |
|||
|
|||
private async Task CleanAndCopyResources(string fileDirectory) |
|||
{ |
|||
var mappingFiles = Directory.GetFiles(fileDirectory, "abp.resourcemapping.js", SearchOption.AllDirectories); |
|||
var resourceMapping = new ResourceMapping |
|||
{ |
|||
Clean = new List<string> {LibsDirectory} |
|||
}; |
|||
|
|||
foreach (var mappingFile in mappingFiles) |
|||
{ |
|||
using (var reader = File.OpenText(mappingFile)) |
|||
{ |
|||
var mappingFileContent = await reader.ReadToEndAsync(); |
|||
|
|||
var mapping = _jsonSerializer.Deserialize<ResourceMapping>(mappingFileContent |
|||
.Replace("module.exports", string.Empty) |
|||
.Replace("=", string.Empty).Trim().TrimEnd(';')); |
|||
|
|||
mapping.ReplaceAliases(); |
|||
|
|||
mapping.Clean.ForEach(c => resourceMapping.Clean.AddIfNotContains(c)); |
|||
mapping.Aliases.ToList().ForEach(x => |
|||
{ |
|||
resourceMapping.Aliases.AddIfNotContains(new KeyValuePair<string, string>(x.Key, x.Value)); |
|||
}); |
|||
mapping.Mappings.ToList().ForEach(x => |
|||
{ |
|||
resourceMapping.Mappings.AddIfNotContains(new KeyValuePair<string, string>(x.Key, x.Value)); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
EnsureLibsFolderExists(fileDirectory, LibsDirectory); |
|||
|
|||
CleanDirsAndFiles(fileDirectory, resourceMapping); |
|||
CopyResourcesToLibs(fileDirectory, resourceMapping); |
|||
} |
|||
|
|||
private void EnsureLibsFolderExists(string fileDirectory, string libsDirectory) |
|||
{ |
|||
Directory.CreateDirectory(Path.Combine(fileDirectory, libsDirectory)); |
|||
} |
|||
|
|||
private void CopyResourcesToLibs(string fileDirectory, ResourceMapping resourceMapping) |
|||
{ |
|||
foreach (var mapping in resourceMapping.Mappings) |
|||
{ |
|||
var destPath = Path.Combine(fileDirectory, mapping.Value); |
|||
|
|||
var files = FindFiles(fileDirectory, mapping.Key); |
|||
|
|||
foreach (var file in files) |
|||
{ |
|||
var destFilePath = Path.Combine(destPath, Path.GetFileName(file)); |
|||
if (File.Exists(destFilePath)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
Directory.CreateDirectory(Path.GetFullPath(destPath)); |
|||
File.Copy(file, destFilePath); |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task<bool> CanInstallLibs(string fileDirectory) |
|||
{ |
|||
var projectFiles = Directory.GetFiles(fileDirectory, "*.csproj"); |
|||
|
|||
using (var reader = File.OpenText(projectFiles[0])) |
|||
{ |
|||
var projectFileContent = await reader.ReadToEndAsync(); |
|||
|
|||
return projectFileContent.Contains("Microsoft.NET.Sdk.Web"); |
|||
} |
|||
} |
|||
|
|||
private void CleanDirsAndFiles(string directory, ResourceMapping resourceMapping) |
|||
{ |
|||
var files = FindFiles(directory, resourceMapping.Clean.ToArray()); |
|||
|
|||
foreach (var file in files) |
|||
{ |
|||
if (File.Exists(file)) |
|||
{ |
|||
File.Delete(file); |
|||
} |
|||
} |
|||
|
|||
foreach (var directoryInfo in Directory.GetDirectories(Path.Combine(directory, resourceMapping.Clean.First()),"*", SearchOption.AllDirectories).Reverse()) |
|||
{ |
|||
if (!Directory.EnumerateFileSystemEntries(directoryInfo).Any()) |
|||
{ |
|||
Directory.Delete(directoryInfo); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private string[] FindFiles(string directory, params string[] patterns) |
|||
{ |
|||
var matcher = new Matcher(); |
|||
|
|||
foreach (var pattern in patterns) |
|||
{ |
|||
if (pattern.StartsWith("!")) |
|||
{ |
|||
matcher.AddExclude(NormalizeGlob(pattern).TrimStart('!')); |
|||
} |
|||
else |
|||
{ |
|||
matcher.AddInclude(NormalizeGlob(pattern)); |
|||
} |
|||
} |
|||
|
|||
var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(directory))); |
|||
|
|||
return result.Files.Select(x => Path.Combine(directory, x.Path)).ToArray(); |
|||
} |
|||
|
|||
private string NormalizeGlob(string pattern) |
|||
{ |
|||
pattern = pattern.Replace("//", "/"); |
|||
|
|||
if (!Path.HasExtension(pattern) && !pattern.EndsWith("*")) |
|||
{ |
|||
return pattern.EnsureEndsWith('/') + "**"; |
|||
} |
|||
|
|||
return pattern; |
|||
} |
|||
|
|||
private void RunNpmInstall(string directory) |
|||
{ |
|||
Logger.LogInformation($"Running npm install on {directory}"); |
|||
CmdHelper.RunCmd($"cd {directory} && npm install"); |
|||
} |
|||
|
|||
private void RunYarn(string directory) |
|||
{ |
|||
Logger.LogInformation($"Running Yarn on {directory}"); |
|||
CmdHelper.RunCmd($"cd {directory} && yarn"); |
|||
} |
|||
|
|||
private bool IsNpmInstalled() |
|||
{ |
|||
var output = CmdHelper.RunCmdAndGetOutput("npm -v").Trim(); |
|||
return SemanticVersion.TryParse(output, out _); |
|||
} |
|||
|
|||
private bool IsYarnAvailable() |
|||
{ |
|||
var output = CmdHelper.RunCmdAndGetOutput("npm list yarn -g").Trim(); |
|||
if (output.Contains("empty")) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!SemanticVersion.TryParse(output.Substring(output.IndexOf('@') + 1), out var version)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return version > SemanticVersion.Parse("1.20.0"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.Cli.LIbs |
|||
{ |
|||
public class ResourceMapping |
|||
{ |
|||
public Dictionary<string, string> Aliases { get; set; } |
|||
|
|||
public List<string> Clean { get; set; } |
|||
|
|||
public Dictionary<string, string> Mappings { get; set; } |
|||
|
|||
public ResourceMapping() |
|||
{ |
|||
Aliases = new Dictionary<string, string> |
|||
{ |
|||
{"@node_modules", "./node_modules"}, |
|||
{"@libs", "./wwwroot/libs"}, |
|||
}; |
|||
Clean = new List<string>(); |
|||
Mappings = new Dictionary<string, string>(); |
|||
} |
|||
|
|||
public void ReplaceAliases() |
|||
{ |
|||
for (var i = 0; i < Mappings.Count; i++) |
|||
{ |
|||
var mapping = Mappings.ElementAt(i); |
|||
Mappings.Remove(mapping.Key); |
|||
|
|||
var key = mapping.Key; |
|||
var value = mapping.Value; |
|||
|
|||
foreach (var alias in Aliases) |
|||
{ |
|||
key = key.Replace(alias.Key, alias.Value); |
|||
value = value.Replace(alias.Key, alias.Value); |
|||
} |
|||
|
|||
Mappings[key] = value; |
|||
} |
|||
|
|||
for (var i = 0; i < Clean.Count; i++) |
|||
{ |
|||
foreach (var alias in Aliases) |
|||
{ |
|||
Clean[i] = Clean[i].Replace(alias.Key, alias.Value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.ObjectExtending; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.Modeling |
|||
{ |
|||
public static class AbpModelBuilderObjectExtensions |
|||
{ |
|||
public static void TryConfigureObjectExtensions<TDbContext>(this ModelBuilder modelBuilder) |
|||
where TDbContext : DbContext |
|||
{ |
|||
ObjectExtensionManager.Instance.ConfigureEfCoreDbContext<TDbContext>(modelBuilder); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
|
|||
namespace Volo.Abp.ObjectExtending |
|||
{ |
|||
public class ObjectExtensionInfoEfCoreMappingOptions |
|||
{ |
|||
[NotNull] |
|||
public ObjectExtensionInfo ObjectExtension { get; } |
|||
|
|||
[CanBeNull] |
|||
public Action<EntityTypeBuilder> EntityTypeBuildAction { get; set; } |
|||
|
|||
[CanBeNull] |
|||
public Action<ModelBuilder> ModelBuildAction { get; set; } |
|||
|
|||
public ObjectExtensionInfoEfCoreMappingOptions( |
|||
[NotNull] ObjectExtensionInfo objectExtension, |
|||
[NotNull] Action<EntityTypeBuilder> entityTypeBuildAction) |
|||
{ |
|||
ObjectExtension = Check.NotNull(objectExtension, nameof(objectExtension)); |
|||
EntityTypeBuildAction = Check.NotNull(entityTypeBuildAction, nameof(entityTypeBuildAction)); |
|||
|
|||
EntityTypeBuildAction = entityTypeBuildAction; |
|||
} |
|||
|
|||
public ObjectExtensionInfoEfCoreMappingOptions( |
|||
[NotNull] ObjectExtensionInfo objectExtension, |
|||
[NotNull] Action<ModelBuilder> modelBuildAction) |
|||
{ |
|||
ObjectExtension = Check.NotNull(objectExtension, nameof(objectExtension)); |
|||
ModelBuildAction = Check.NotNull(modelBuildAction, nameof(modelBuildAction)); |
|||
|
|||
ModelBuildAction = modelBuildAction; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1}" |
|||
EndProject |
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0BC55E3B-4964-48E3-A390-2ADD37980149}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Components.Server.BasicTheme", "src\Volo.Abp.AspNetCore.Components.Server.BasicTheme\Volo.Abp.AspNetCore.Components.Server.BasicTheme.csproj", "{C8068E7F-4A04-4755-8976-C2A4C0ADC708}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Components.Web.BasicTheme", "src\Volo.Abp.AspNetCore.Components.Web.BasicTheme\Volo.Abp.AspNetCore.Components.Web.BasicTheme.csproj", "{655C0CF7-7BFA-45E4-A157-E868A97FB45B}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme", "src\Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme\Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.csproj", "{95954B0B-9FE0-4351-B1F2-53DDF03F0738}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", "src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj", "{21E20CC4-E82B-451B-BB73-141997C81C56}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj", "{7DFA95DB-F3A1-4883-AB03-9B02E540A134}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo", "test\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj", "{51B491ED-F959-4974-A876-528B5F16BC92}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj", "{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{C8068E7F-4A04-4755-8976-C2A4C0ADC708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{C8068E7F-4A04-4755-8976-C2A4C0ADC708}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{C8068E7F-4A04-4755-8976-C2A4C0ADC708}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{C8068E7F-4A04-4755-8976-C2A4C0ADC708}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{655C0CF7-7BFA-45E4-A157-E868A97FB45B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{655C0CF7-7BFA-45E4-A157-E868A97FB45B}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{655C0CF7-7BFA-45E4-A157-E868A97FB45B}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{655C0CF7-7BFA-45E4-A157-E868A97FB45B}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{95954B0B-9FE0-4351-B1F2-53DDF03F0738}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{95954B0B-9FE0-4351-B1F2-53DDF03F0738}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{95954B0B-9FE0-4351-B1F2-53DDF03F0738}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{95954B0B-9FE0-4351-B1F2-53DDF03F0738}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{21E20CC4-E82B-451B-BB73-141997C81C56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{21E20CC4-E82B-451B-BB73-141997C81C56}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{21E20CC4-E82B-451B-BB73-141997C81C56}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{21E20CC4-E82B-451B-BB73-141997C81C56}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{7DFA95DB-F3A1-4883-AB03-9B02E540A134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{7DFA95DB-F3A1-4883-AB03-9B02E540A134}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{7DFA95DB-F3A1-4883-AB03-9B02E540A134}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{7DFA95DB-F3A1-4883-AB03-9B02E540A134}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{51B491ED-F959-4974-A876-528B5F16BC92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{51B491ED-F959-4974-A876-528B5F16BC92}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{51B491ED-F959-4974-A876-528B5F16BC92}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{51B491ED-F959-4974-A876-528B5F16BC92}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(NestedProjects) = preSolution |
|||
{C8068E7F-4A04-4755-8976-C2A4C0ADC708} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} |
|||
{655C0CF7-7BFA-45E4-A157-E868A97FB45B} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} |
|||
{95954B0B-9FE0-4351-B1F2-53DDF03F0738} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} |
|||
{21E20CC4-E82B-451B-BB73-141997C81C56} = {ED6D078F-B0A2-48E8-A09D-3B7CDF6CE3D1} |
|||
{7DFA95DB-F3A1-4883-AB03-9B02E540A134} = {0BC55E3B-4964-48E3-A390-2ADD37980149} |
|||
{51B491ED-F959-4974-A876-528B5F16BC92} = {0BC55E3B-4964-48E3-A390-2ADD37980149} |
|||
{8C336CB8-F7A9-4203-AE55-D8F5FDB2A958} = {0BC55E3B-4964-48E3-A390-2ADD37980149} |
|||
EndGlobalSection |
|||
EndGlobal |
|||