Browse Source

Merge branch 'dev' into IAbpHostEnvironment

pull/14842/head
maliming 4 years ago
parent
commit
9c3db8d7d3
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 269
      docs/en/Application-Startup.md
  2. 2
      docs/en/Audit-Logging.md
  3. 1
      docs/en/CLI.md
  4. 13
      docs/en/Caching.md
  5. 2
      docs/en/Deployment/Clustered-Environment.md
  6. 6
      docs/en/Deployment/Configuring-Production.md
  7. 45
      docs/en/Deployment/Distributed-Microservice.md
  8. 6
      docs/en/Deployment/Index.md
  9. 22
      docs/en/Deployment/Optimizing-Production.md
  10. 8
      docs/en/Entities.md
  11. 131
      docs/en/Entity-Cache.md
  12. 2
      docs/en/Getting-Started-AspNetCore-Application.md
  13. 2
      docs/en/Getting-Started-Setup-Environment.md
  14. 2
      docs/en/Getting-Started.md
  15. 6
      docs/en/Migration-Guides/OpenIddict-Blazor.md
  16. 1
      docs/en/Module-Development-Basics.md
  17. 2
      docs/en/Tutorials/Todo/Index.md
  18. 2
      docs/en/Tutorials/Todo/Single-Layer/Index.md
  19. 80
      docs/en/docs-nav.json
  20. BIN
      docs/en/images/app-startup-console-initial.png
  21. 1
      docs/zh-Hans/CLI.md
  22. 2
      docs/zh-Hans/Getting-Started-Setup-Environment.md
  23. 8
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs
  24. 1
      framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs
  25. 1
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs
  26. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs
  27. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs
  28. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs
  29. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProxyCommandBase.cs
  30. 82
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs
  31. 49
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs
  32. 3
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs
  33. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs
  34. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  35. 8
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs
  36. 55
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs
  37. 4
      framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs
  38. 1
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs
  39. 5
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs
  40. 26
      modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs
  41. 6
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj
  42. 22
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs
  43. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj
  44. 2
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj
  45. 34
      modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs
  46. 6
      npm/ng-packs/.prettierignore
  47. 10
      npm/ng-packs/README.md
  48. 12
      npm/ng-packs/package.json
  49. 8
      npm/ng-packs/packages/components/tree/src/lib/components/tree.component.scss
  50. 13
      npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts
  51. 7
      npm/ng-packs/packages/core/src/lib/services/session-state.service.ts
  52. 29
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  53. 18
      npm/ng-packs/packages/schematics/src/commands/api/index.ts
  54. 34
      npm/ng-packs/packages/schematics/src/commands/api/schema.json
  55. 20
      npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts
  56. 31
      npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json
  57. 1
      npm/ng-packs/packages/schematics/src/enums/index.ts
  58. 7
      npm/ng-packs/packages/schematics/src/enums/service-types.ts
  59. 2
      npm/ng-packs/packages/schematics/src/models/api-definition.ts
  60. 3
      npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts
  61. 2
      npm/ng-packs/pre-commit
  62. 1
      npm/ng-packs/scripts/copy-packages-to-templates.ts
  63. 82
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Menus/MyProjectNameMenuContributor.cs
  64. 10
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Menus/MyProjectNameMenus.cs
  65. 47
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj
  66. 11
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBlazorAutoMapperProfile.cs
  67. 142
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBlazorModule.cs
  68. 10
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs
  69. 19
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBundleContributor.cs
  70. 12
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameComponentBase.cs
  71. 39
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/Index.razor
  72. 6
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/Index.razor.cs
  73. 1
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/index.razor.css
  74. 22
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Program.cs
  75. 30
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Properties/launchSettings.json
  76. 14
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/_Imports.razor
  77. 24
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/appsettings.json
  78. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/favicon.ico
  79. 27
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.css
  80. 37
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.js
  81. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/icon-192.png
  82. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/icon-512.png
  83. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-dark-thumbnail.png
  84. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-dark.png
  85. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-light-thumbnail.png
  86. BIN
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-light.png
  87. 35
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/index.html
  88. 57
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/main.css
  89. 21
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/manifest.json
  90. 22
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameDbContext.cs
  91. 88
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameDbMigrationService.cs
  92. 39
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameMongoDbSchemaMigrator.cs
  93. 308
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/OpenIddictDataSeedContributor.cs
  94. 0
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Entities/.gitkeep
  95. 99
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj
  96. 318
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyProjectNameServerModule.cs
  97. 11
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/ObjectMapping/MyProjectNameAutoMapperProfile.cs
  98. 81
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Program.cs
  99. 30
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Properties/launchSettings.json
  100. 0
      templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Services/.gitkeep

269
docs/en/Application-Startup.md

@ -0,0 +1,269 @@
## ABP Application Startup
You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application or how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you.
> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or if you need to modify how the ABP Framework starts.
While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NPM packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only need to install a single NuGet package and make a few small changes.
This document is for who wants to better understand how the ABP Framework is initialized and configured on startup.
## Installing to a Console Application
A .NET Console application is the minimalist .NET application. So, it is best to show the installing of the ABP Framework to a console application as a minimalist example.
If you [create a new console application with Visual Studio](https://learn.microsoft.com/en-us/dotnet/core/tutorials/with-visual-studio) (for .NET 7.0 or later), you will see the following solution structure (I named the solution as `MyConsoleDemo`):
![app-startup-console-initial](images/app-startup-console-initial.png)
This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code.
The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using the [Package Manager Console](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-powershell) in Visual Studio:
````bash
Install-Package Volo.Abp.Core
````
Alternatively, you can use a command-line terminal in the root folder of the project (the folder containing the `MyConsoleDemo.csproj` file, for this example):
````bash
dotnet add package Volo.Abp.Core
````
After adding the NuGet package, we should create a root [module class](Module-Development-Basics.md) for our application. We can create the following class in the project:
````csharp
using Volo.Abp.Modularity;
namespace MyConsoleDemo
{
public class MyConsoleDemoModule : AbpModule
{
}
}
````
This is an empty class deriving from the `AbpModule` class. It is the main class that you will control your application's dependencies with, and implement your configuration and startup/shutdown logic. For more information, please check the [Modularity](Module-Development-Basics.md) document.
As the second and the last step, change the `Program.cs` as shown in the following code block:
````csharp
using MyConsoleDemo;
using Volo.Abp;
// 1: Create the ABP application container
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
// 2: Initialize/start the ABP Framework (and all the modules)
await application.InitializeAsync();
Console.WriteLine("ABP Framework has been started...");
// 3: Stop the ABP Framework (and all the modules)
await application.ShutdownAsync();
````
That's all. Now, ABP Framework is installed, integrated, started and stopped in your application. From now, you can install [ABP packages](https://abp.io/packages) to your application whenever you need them.
## Installing a Framework Package
If you want to send emails from your .NET application, you can use .NET's standard [SmtpClient class](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient). ABP also provides an `IEmailSender` service that simplifies [sending emails](Emailing.md) and configuring the email settings in a central place. If you want to use it, you should install the [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) NuGet package to your project:
````bash
dotnet add package Volo.Abp.Emailing
````
Once you add a new ABP package/module, you also need to specify the module dependency from your module class. So, change the `MyConsoleDemoModule` class as shown below:
````csharp
using Volo.Abp.Emailing;
using Volo.Abp.Modularity;
namespace MyConsoleDemo
{
[DependsOn(typeof(AbpEmailingModule))] // Added the module dependency
public class MyConsoleDemoModule : AbpModule
{
}
}
````
I've just added a `[DependsOn]` attribute to declare that I want to use the ABP Emailing Module (`AbpEmailingModule`). Now, I can use the `IEmailSender` service in my `Program.cs`:
````csharp
using Microsoft.Extensions.DependencyInjection;
using MyConsoleDemo;
using Volo.Abp;
using Volo.Abp.Emailing;
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
await application.InitializeAsync();
// Sending emails using the IEmailSender service
var emailsender = application.ServiceProvider.GetRequiredService<IEmailSender>();
await emailsender.SendAsync(
to: "info@acme.com",
subject: "Hello World",
body: "My message body..."
);
await application.ShutdownAsync();
````
> If you run that application, you get a runtime error indicating that the email sending settings haven't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it.
That's all. Install an ABP NuGet package, add the module dependency (using the `[DependsOn]` attribute) and use any service inside the NuGet package.
The [ABP CLI](CLI.md) already has a special command to perform the addition of an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command:
````bash
abp add-package Volo.Abp.Emailing
````
We suggest you to use the `abp add-package` command instead of manually doing it.
## AbpApplicationFactory
`AbpApplicationFactory` is the main class that creates an ABP application container. It provides a single static `CreateAsync` (and `Create` if you can't use asynchronous programming) method with multiple overloads. Let's investigate these overloads to understand where you can use them.
The first overload gets a generic module class parameter as we've used before in this document:
````csharp
AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
````
The generic class parameter should be the root module class of your application. All the other modules are resolved as dependencies of that module.
The second overload gets the module class as a `Type` parameter, instead of the generic parameter. So, the previous code block could be re-written as shown below:
````csharp
AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule));
````
Both overloads work exactly the same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime.
If you create one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to setup the [dependency injection](Dependency-Injection.md) system internally. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system.
The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also sets up the dependency injection system internally.
We can change the `Program.cs` as shown below to externally manage the dependency injection setup:
````csharp
using Microsoft.Extensions.DependencyInjection;
using MyConsoleDemo;
using Volo.Abp;
// 1: Manually created the IServiceCollection
IServiceCollection services = new ServiceCollection();
// 2: Pass the IServiceCollection externally to the ABP Framework
using var application = await AbpApplicationFactory
.CreateAsync<MyConsoleDemoModule>(services);
// 3: Manually built the IServiceProvider object
IServiceProvider serviceProvider = services.BuildServiceProvider();
// 4: Pass the IServiceProvider externally to the ABP Framework
await application.InitializeAsync(serviceProvider);
Console.WriteLine("ABP Framework has been started...");
await application.ShutdownAsync();
````
In this example, we've used .NET's standard dependency injection container. The `services.BuildServiceProvider()` call creates the standard container. However, ABP provides an alternative extension method, `BuildServiceProviderFromFactory()`, that properly works even if you are using another dependency injection container:
````csharp
IServiceProvider serviceProvider = services.BuildServiceProviderFromFactory();
````
> You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework.
Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as in the following code block:
````csharp
using var application = await AbpApplicationFactory
.CreateAsync(typeof(MyConsoleDemoModule), services);
````
> All of the `CreateAsync` method overloads have `Create` counterparts. If your application type can not utilize asynchronous programming (that means you can't use the `await` keyword), then you can use the `Create` method instead of the `CreateAsync` method.
### AbpApplicationCreationOptions
All of the `CreateAsync` overloads can get an optional `Action<AbpApplicationCreationOptions>` parameter to configure the options that are used on the application creation. See the following example:
````csharp
using var application = await AbpApplicationFactory
.CreateAsync<MyConsoleDemoModule>(options =>
{
options.ApplicationName = "MyApp";
});
````
We've passed a lambda method to configure the `ApplicationName` option. Here's a list of all standard options:
* `ApplicationName`: A human-readable name for the application. It is a unique value for an application.
* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with an internal service provider, you can use this option to configure how the application configuration is built.
* `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins.
* `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class.
#### The ApplicationName option
As defined above, the `ApplicationName` option is a human-readable name for the application. It is a unique value for an application.
`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) that are saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`.
The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name.
There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file:
````json
{
"ApplicationName": "Services.Ordering"
}
````
Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below:
````csharp
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options =>
{
options.ApplicationName = "Services.Ordering";
});
````
#### IApplicationInfoAccessor
If you need to access the `ApplicationName` later in your solution, you can inject the `IApplicationInfoAccessor` service and get the value from its `ApplicationName` property.
`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other.
## IAbpApplication
`AbpApplicationFactory` returns an `IAbpApplication` object from its `CreateAsync` (or `Create`) method. `IAbpApplication` is the main container for an ABP application. It is also registered to the [dependency injection](Dependency-Injection.md) system, so you can inject `IAbpApplication` in your services to use its properties and methods.
Here's a list of `IAbpApplication` properties you may want to know:
* `StartupModuleType`: Gets the root module of the application that was used while creating the application container (on the `AbpApplicationFactory.CreateAsync` method).
* `Services`: A list of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialization (you can actually add, but it won't have any effect).
* `ServiceProvider`: A reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes.
* `Modules`: A read-only list of all the modules loaded into the current application. Alternatively, you can inject the `IModuleContainer` service if you need to access the module list in your application code.
The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface, so you can get the `ApplicationName` and `InstanceId` values from it. However, if you only need to access these properties, inject and use the `IApplicationInfoAccessor` service instead.
`IAbpApplication` is disposable. Always dispose of it before exiting your application.
## .NET Generic Host & ASP.NET Core Integrations
`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create an ABP application container that is well-integrated to these systems.
The [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document clearly explains how you can create an ABP application container in an ASP.NET Core application.
You can also [create a console application](Startup-Templates/Console) to see how it is integrated with .NET Generic Host.
> Most of the times, you will directly create ABP applications using the ABP CLI's `new` command. So, you don't need to care about these integration details.
## See Also
* [Dependency injection](Dependency-Injection.md)
* [Modularity](Module-Development-Basics.md)

2
docs/en/Audit-Logging.md

@ -42,7 +42,7 @@ Here, a list of the options you can configure:
* `AlwaysLogOnException` (default: `true`): If you set to true, it always saves the audit log on an exception/error case without checking other options (except `IsEnabled`, which completely disables the audit logging).
* `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests.
* `DisableLogActionInfo` (default: `false`):If you set to true, Will no longer log `AuditLogActionInfo`.
* `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications.
* `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. If you don't set, it is set from the `IApplicationInfoAccessor.ApplicationName` value, which is the entry assembly name by default.
* `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters.
* `EntityHistorySelectors`: A list of selectors those are used to determine if an entity type is selected for saving the entity change. See the section below for details.
* `Contributors`: A list of `AuditLogContributor` implementations. A contributor is a way of extending the audit log system. See the "Audit Log Contributors" section below.

1
docs/en/CLI.md

@ -353,6 +353,7 @@ abp generate-proxy -t csharp -url https://localhost:44302/
* `--type` or `-t`: The name of client type. Available clients:
* `csharp`: C#, work in the `*.HttpApi.Client` project directory. There are some additional options for this client:
* `--without-contracts`: Avoid generating the application service interface, class, enum and dto types.
* `--folder`: Folder name to place generated CSharp code in. Default value: `ClientProxies`.
* `ng`: Angular. There are some additional options for this client:
* `--api-name` or `-a`: The name of the API endpoint defined in the `/src/environments/environment.ts`. Default value: `default`.

13
docs/en/Caching.md

@ -10,7 +10,7 @@ ABP Framework extends the [ASP.NET Core distributed cache](https://docs.microsof
[Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) is the main package of the caching system. You can install it a project using the add-package command of the [ABP CLI](CLI.md):
```
```bash
abp add-package Volo.Abp.Caching
```
@ -252,6 +252,14 @@ ABP's distributed cache interfaces provide methods to perform batch methods thos
> These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` ... methods (called once for each item).
## Caching Entities
ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database.
It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted.
> See the [Entity Cache](Entity-Cache.md) documentation for more information.
## Advanced Topics
### Unit Of Work Level Cache
@ -272,4 +280,5 @@ You can [replace](Dependency-Injection.md) this service by your own implementati
## See Also
* [Redis Cache](Redis-Cache.md)
* [Entity Cache](Entity-Cache.md)
* [Redis Cache](Redis-Cache.md)

2
docs/en/Deployment/Clustered-Environment.md

@ -63,7 +63,7 @@ The [Database BLOB provider](../Blob-Storing-Database) is the easiest way since
> [ABP Commercial](https://commercial.abp.io/) startup solution templates come with the database BLOB provider as pre-installed, and stores BLOBs in the application's database.
Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOG storage providers.
Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOB storage providers.
## Configuring Background Jobs

6
docs/en/Deployment/Configuring-Production.md

@ -1,4 +1,4 @@
# Configuring for Production
# Configuring Your Application for Production Environments
ABP Framework has a lot of options to configure and fine-tune its features. They are all explained in their own documents. Default values for these options are pretty well for most of the deployment environments. However, you may need to care about some options based on how you've structured your deployment environment. In this document, we will highlight these kind of options. So, it is highly recommended to read this document in order to not have unexpected behaviors in your system in production.
@ -91,6 +91,10 @@ Configure<AbpStringEncryptionOptions>(options =>
Note that ABP CLI automatically sets the password to a random value on a new project creation. However, it is stored in the `appsettings.json` file and is generally added to your source control. It is suggested to use [User Secrets](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) or [Environment Variables](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration) to set that value.
## Logging
ABP uses .NET's standard [Logging services](../Logging.md). So, it is compatible with any logging provider that works with .NET. ABP's startup solution templates come with [Serilog](https://serilog.net/) pre-installed and configured for you. It writes logs to file system and console with the initial configuration. File system is useful for development environment, but it is suggested you to use a different provider for your production environment, like Elasticsearch, database or any other provider that can properly work.
## The Swagger UI
ABP's startup solution templates come with [Swagger UI](https://swagger.io/) pre-installed. Swagger is a pretty standard and useful tool to discover and test your HTTP APIs on a built-in UI that is embedded into your application or service. It is typically used in development environment, but you may want to enable it on staging or production environments too.

45
docs/en/Deployment/Distributed-Microservice.md

@ -0,0 +1,45 @@
# Deploying Distributed / Microservice Solutions
The ABP Framework is designed to consider distributed and microservice systems, where you have multiple applications and/or services communicating internally. All of its features are compatible with distributed scenarios. This document highlights some points you should care about when you deploy your distributed or microservice solution.
## Application Name & Instance Id
ABP provides the `IApplicationInfoAccessor` service that provides the following properties:
* `ApplicationName`: A human-readable name for an application. It is a unique value for an application.
* `InstanceId`: A random (GUID) value generated by the ABP Framework each time you start the application.
These values are used by the ABP Framework in several places to distinguish the application and the application instance (process) in the system. For example, the [audit logging](../Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`.
The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name.
There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](../Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file:
````json
{
"ApplicationName": "Services.Ordering"
}
````
Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below:
````csharp
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options =>
{
options.ApplicationName = "Services.Ordering";
});
````
## Using a Distributed Event Bus
ABP's [Distributed Event Bus](../Distributed-Event-Bus.md) system provides a standard interface to communicate with other applications and services. While the name is "distributed", the default implementation is in-process. That means, your applications / services can not communicate with each other unless you explicitly configure a distributed event bus provider.
If you are building a distributed system, then the applications should communicate through an external distributed messaging server. Please follow the [Distributed Event Bus](../Distributed-Event-Bus.md) document to learn how to install and configure your distributed event bus provider.
> **Warning**: Even if you don't use the distributed event bus directly in your application code, the ABP Framework and some of the modules you are using may use it. So, if you are building a distributed system, always configure a distributed event bus provider.
> **Info**: [Clustered deployment](Clustered-Environment.md) of a single application is not considered as a distributed system. So, if you only have a single application with multiple instances serving behind a load balancer, a real distributed messaging server may not be needed.
## See Also
* [Deploying to a Clustered Environment](Clustered-Environment.md)

6
docs/en/Deployment/Index.md

@ -6,5 +6,7 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Configuring for production](Configuring-Production.md): Notes for some important configurations for production environments.
* [Deploying to a clustered environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Configuring for Production](Configuring-Production.md): Notes for some essential configurations for production environments.
* [Optimization for Production](Optimizing-Production.md): Tips and suggestions for optimizing your application on production environments.
* [Deploying to a Clustered Environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
* [Deploying Distributed / Microservice Solutions](Distributed-Microservice.md): Deployment notes for solutions consisting of multiple applications and/or services.

22
docs/en/Deployment/Optimizing-Production.md

@ -0,0 +1,22 @@
# Optimizing Your Application for Production Environments
ABP Framework and the startup solution templates are configured well to get the maximum performance on production environments. However, there are still some points you need to pay attention to in order to optimize your system in production. In this document, we will mention some of these topics.
## Caching Static Contents
The following items are contents that can be cached in the client side (typically in the Browser) or in a CDN server:
* **Static images** can always be cached. Here, you should be careful that if you change an image, use a different file name, or use a versioning query-string parameter, so the browser (or CDN) understands it's been changed.
* **CSS and JavaScript files**. ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system always uses a query-string versioning parameter and a hash value in the files names of the CSS & JavaScript files for the [MVC (Razor Pages)](../UI/AspNetCore/Overall.md) UI. So, you can safely cache these files in the client side or in a CDN server.
* **Application bundle files** of an [Angular UI](../UI/Angular/Quick-Start.md) application.
* **[Application Localization Endpoint](../API/Application-Localization.md)** can be cached per culture (it already has a `cultureName` query string parameter) if you don't use dynamic localization on the server-side. ABP Commercial's [Language Management](https://commercial.abp.io/modules/Volo.LanguageManagement) module provides dynamic localization. If you're using it, you can't cache that endpoint forever. However, you can still cache it for a while. Applying dynamic localization text changes to the application can delay for a few minutes, even for a few hours in a real life scenario.
There may be more ways based on your solution structure and deployment environment, but these are the essential points you should consider to client-side cache in a production environment.
## Bundling & Minification for MVC (Razor Pages) UI
ABP's [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) system automatically bundles, minifies and versions your CSS and JavaScript files in production environment. Normally, you don't need to do anything, if you haven't disabled it yourself in your application code. It is important to follow the [bundling & minification](../UI/AspNetCore/Bundling-Minification.md) document and truly use the system to get the maximum optimization.
## Background Jobs
ABP's [Background Jobs](../Background-Jobs.md) system provides an abstraction with a basic implementation to enqueue jobs and execute them in a background thread. ABP's Default Background Job Manager may not be enough if you are adding too many jobs to the queue and want them to be executed in parallel by multiple servers with a high performance. If you need these, you should consider to configure a dedicated background job software, like [Hangfire](https://www.hangfire.io/). ABP has a pre-built [Hangfire integration](../Background-Jobs-Hangfire.md), so you can switch to Hangfire without changing your application code.

8
docs/en/Entities.md

@ -316,6 +316,14 @@ All these base classes also have non-generic versions to take `AuditedEntity` an
All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser<TUser>` and `FullAuditedAggregateRootWithUser<TKey, TUser>`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern). Also, if you add navigation properties to the AppUser class that comes with the startup template, consider to handle (ignore/map) it on the migration dbcontext (see [the EF Core migration document](Entity-Framework-Core-Migrations.md)).
## Caching Entities
ABP Framework provides a [Distributed Entity Cache System](Entity-Cache.md) for caching entities. It is useful if you want to use caching for quicker access to the entity rather than repeatedly querying it from the database.
It's designed as read-only and automatically invalidates a cached entity if the entity is updated or deleted.
> See the [Entity Cache](Entity-Cache.md) documentation for more information.
## Extra Properties
ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API.

131
docs/en/Entity-Cache.md

@ -0,0 +1,131 @@
# Entity Cache
ABP Framework provides an entity caching system that works on top of the [distributed caching](Caching.md) system. It does the following operations on behalf of you:
* Gets the entity from the database (by using the [repositories](Repositories.md)) in its first call and then gets it from the cache in subsequent calls.
* Automatically invalidates the cached entity if the entity is updated or deleted. Thus, it will be retrieved from the database in the next call and will be re-cached.
## Caching Entity Objects
`IEntityCache<TEntityCacheItem, TKey>` is a simple service provided by the ABP Framework for caching entities. Assume that you have a `Product` entity as shown below:
```csharp
public class Product : AggregateRoot<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public float Price { get; set; }
public int StockCount { get; set; }
}
```
If you want to cache this entity, you should first configure the [dependency injection](Dependency-Injection.md) system to register the `IEntityCache` service in the `ConfigureServices` method of your [module class](Module-Development-Basics.md):
```csharp
context.Services.AddEntityCache<Product, Guid>();
```
Now you can inject the `IEntityCache<Product, Guid>` service wherever you need:
```csharp
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IEntityCache<Product, Guid> _productCache;
public ProductAppService(IEntityCache<Product, Guid> productCache)
{
_productCache = productCache;
}
public async Task<ProductDto> GetAsync(Guid id)
{
var product = await _productCache.GetAsync(id);
return ObjectMapper.Map<Product, ProductDto>(product);
}
}
```
> Note that we've used the `ObjectMapper` service to map from `Product` to `ProductDto`. You should configure that [object mapping](Object-To-Object-Mapping.md) to make that example service properly works.
That's all. The cache name (in the distributed cache server) will be full name (with namespace) of the `Product` class. You can use the `[CacheName]` attribute to change it. Please refer to the [caching document](Caching.md) for details.
## Using a Cache Item Class
In the previous section, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable to JSON (and deserializable from JSON). Sometimes that might not be possible or you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity.
Assume that we've created a `ProductDto` class as shown below:
```csharp
public class ProductDto : EntityDto<Guid>
{
public string Name { get; set; }
public string Description { get; set; }
public float Price { get; set; }
public int StockCount { get; set; }
}
```
Now, we can register the entity cache services to [dependency injection](Dependency-Injection.md) in the `ConfigureServices` method of your [module class](Module-Development-Basics.md) with three generic parameters, as shown below:
```csharp
context.Services.AddEntityCache<Product, ProductDto, Guid>();
```
Since the entity cache system will perform the [object mapping](Object-To-Object-Mapping.md) (from `Product` to `ProductDto`), we should configure the object map. Here, an example configuration with [AutoMapper](https://automapper.org/):
```csharp
public class MyMapperProfile : Profile
{
public MyMapperProfile()
{
CreateMap<Product, ProductDto>();
}
}
```
Now, you can inject the `IEntityCache<ProductDto, Guid>` service wherever you want:
```csharp
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IEntityCache<ProductDto, Guid> _productCache;
public ProductAppService(IEntityCache<ProductDto, Guid> productCache)
{
_productCache = productCache;
}
public async Task<ProductDto> GetAsync(Guid id)
{
return await _productCache.GetAsync(id);
}
}
```
Notice that the `_productCache.GetAsync` method already returns a `ProductDto` object, so we could directly return it from out application service.
## Configuration
All of the `context.Services.AddEntityCache()` methods get an optional `DistributedCacheEntryOptions` parameter where you can easily configure the caching options:
```csharp
context.Services.AddEntityCache<Product, ProductDto, Guid>(
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30)
}
);
```
> The default cache duration is **2 minutes** with the `AbsoluteExpirationRelativeToNow` configuration.
## Additional Notes
* Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as explained before.
* Entity Caching System is designed as **read-only**. You should use the standard [repository](Repositories.md) methods to manipulate the entity if you need. If you need to manipulate (update) the entity, do not get it from the entity cache. Instead, read it from the repository, change it and update using the repository.
## See Also
* [Distributed caching](Caching.md)
* [Entities](Entities.md)
* [Repositories](Repositories.md)

2
docs/en/Getting-Started-AspNetCore-Application.md

@ -1,4 +1,4 @@
# Getting Started With an ABP and AspNet Core MVC Web Application
# Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application
This tutorial explains how to start ABP from scratch with minimal dependencies. You generally want to start with the **[startup template](Getting-Started-AspNetCore-MVC-Template.md)**.

2
docs/en/Getting-Started-Setup-Environment.md

@ -19,7 +19,7 @@ First things first! Let's setup your development environment before creating the
The following tools should be installed on your development machine:
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development.
{{ if UI != "Blazor" }}
* [Node v16 or v18](https://nodejs.org/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[1](#f-yarn)</sup> or npm v6+ (already installed with Node)

2
docs/en/Getting-Started.md

@ -11,8 +11,6 @@
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Contents
This tutorial explains how to **create and run** a new web application using the ABP Framework. Follow the steps below;
1. [Setup your development environment](Getting-Started-Setup-Environment.md)

6
docs/en/Migration-Guides/OpenIddict-Blazor.md

@ -62,7 +62,7 @@
typeof(AbpAccountWebOpenIddictModule),
```
- In the **MyApplicationBlazorModule.cs** add `PreConfigureServices` like below with your application name as the audience:
- In the **MyApplicationHostModule.cs** add `PreConfigureServices` like below with your application name as the audience:
```csharp
public override void PreConfigureServices(ServiceConfigurationContext context)
@ -79,7 +79,7 @@
}
```
- In the **MyApplicationBlazorModule.cs** `ConfigureServices` method, **replace the method call**:
- In the **MyApplicationHostModule.cs** `ConfigureServices` method, **replace the method call**:
From `ConfigureAuthentication(context, configuration);` to `ConfigureAuthentication(context);` and update the method as:
@ -90,7 +90,7 @@
}
```
- In the **MyApplicationBlazorModule.cs** `OnApplicationInitialization` method, **replace the midware**:
- In the **MyApplicationHostModule.cs** `OnApplicationInitialization` method, **replace the midware**:
```csharp
app.UseJwtTokenMiddleware();

1
docs/en/Module-Development-Basics.md

@ -20,7 +20,6 @@ public class BlogModule : AbpModule
{
}
````
### Configuring Dependency Injection & Other Modules

2
docs/en/Tutorials/Todo/Index.md

@ -16,7 +16,7 @@ You can find the source code of the completed application [here](https://github.
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v16.x](https://nodejs.org/)
{{if DB=="Mongo"}}

2
docs/en/Tutorials/Todo/Single-Layer/Index.md

@ -16,7 +16,7 @@ You can find the source code of the completed application [here](https://github.
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v16.x](https://nodejs.org/)
{{if DB=="Mongo"}}

80
docs/en/docs-nav.json

@ -154,48 +154,42 @@
"text": "Fundamentals",
"items": [
{
"text": "Dependency Injection",
"path": "Dependency-Injection.md",
"items": [
{
"text": "AutoFac Integration",
"path": "Autofac-Integration.md"
}
]
},
{
"text": "Configuration",
"path": "Configuration.md"
},
{
"text": "Options",
"path": "Options.md"
"text": "Application Startup",
"path": "Application-Startup.md"
},
{
"text": "Authorization",
"path": "Authorization.md"
},
{
"text": "Validation",
"path": "Validation.md",
"text": "Caching",
"path": "Caching.md",
"items": [
{
"text": "FluentValidation Integration",
"path": "FluentValidation.md"
"text": "Entity Cache",
"path": "Entity-Cache.md"
},
{
"text": "Redis Cache",
"path": "Redis-Cache.md"
}
]
},
{
"text": "Localization",
"path": "Localization.md"
"text": "Configuration",
"path": "Configuration.md"
},
{
"text": "Caching",
"path": "Caching.md",
"text": "Connection Strings",
"path": "Connection-Strings.md"
},
{
"text": "Dependency Injection",
"path": "Dependency-Injection.md",
"items": [
{
"text": "Redis Cache",
"path": "Redis-Cache.md"
"text": "AutoFac Integration",
"path": "Autofac-Integration.md"
}
]
},
@ -204,20 +198,34 @@
"path": "Exception-Handling.md"
},
{
"text": "Settings",
"path": "Settings.md"
"text": "Localization",
"path": "Localization.md"
},
{
"text": "Connection Strings",
"path": "Connection-Strings.md"
"text": "Logging",
"path": "Logging.md"
},
{
"text": "Object Extensions",
"path": "Object-Extensions.md"
},
{
"text": "Logging",
"path": "Logging.md"
"text": "Options",
"path": "Options.md"
},
{
"text": "Settings",
"path": "Settings.md"
},
{
"text": "Validation",
"path": "Validation.md",
"items": [
{
"text": "FluentValidation Integration",
"path": "FluentValidation.md"
}
]
}
]
},
@ -1310,6 +1318,14 @@
{
"text": "Deploying to a Clustered Environment",
"path": "Deployment/Clustered-Environment.md"
},
{
"text": "Distributed / Microservice Solutions",
"path": "Deployment/Distributed-Microservice.md"
},
{
"text": "Optimizing for Production",
"path": "Deployment/Optimizing-Production.md"
}
]
},

BIN
docs/en/images/app-startup-console-initial.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

1
docs/zh-Hans/CLI.md

@ -259,6 +259,7 @@ abp generate-proxy -t csharp -url https://localhost:44302/
* `--type` 或 `-t`: 客户端类型的名称. 可用的客户端有:
* `csharp`: C#, 工作在 `*.HttpApi.Client` 项目目录. 此客户端有一些可选选项:
* `--without-contracts`: 取消生成应用程序服务接口,类,枚举和DTO.
* `--folder`: 放置生成的 CSharp 代码的文件夹名称. 默认值: `ClientProxies`.
* `ng`: Angular. 此客户端有一些可选选项:
* `--api-name` 或 `-a`: 在 `/src/environments/environment.ts` 中定义的API端点名称。. 默认值: `default`.

2
docs/zh-Hans/Getting-Started-Setup-Environment.md

@ -19,7 +19,7 @@
开发计算机上应安装以下工具:
* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) 的开发.
* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 7.0+](https://dotnet.microsoft.com/download/dotnet) 的开发.
{{ if UI != "Blazor" }}
* [Node v16 或 v18](https://nodejs.org/)
* [Yarn v1.20+ (不是v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[1](#f-yarn)</sup> 或 npm v6+ (已跟随Node一起安装)

8
framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Authentication.OAuth;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Authentication.OAuth;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.RemoteServices;
@ -12,5 +13,8 @@ namespace Volo.Abp.AspNetCore.Authentication.OpenIdConnect;
)]
public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient();
}
}

1
framework/src/Volo.Abp.AspNetCore.Components.Server/Volo/Abp/AspNetCore/Components/Server/AbpAspNetCoreComponentsServerModule.cs

@ -26,6 +26,7 @@ public class AbpAspNetCoreComponentsServerModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient();
var serverSideBlazorBuilder = context.Services.AddServerSideBlazor(options =>
{
if (context.Services.GetHostingEnvironment().IsDevelopment())

1
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/AbpAspNetCoreComponentsWebAssemblyModule.cs

@ -41,6 +41,7 @@ public class AbpAspNetCoreComponentsWebAssemblyModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient();
context.Services
.GetHostBuilder().Logging
.AddProvider(new AbpExceptionHandlingLoggerProvider(context.Services));

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelperService.cs

@ -8,7 +8,7 @@ public class AbpCardTagHelperService : AbpTagHelperService<AbpCardTagHelper>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
output.Attributes.AddClass("card");
output.Attributes.AddClass("card mb-3");
SetBorder(context, output);
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Tab/AbpTabsTagHelperService.cs

@ -84,7 +84,7 @@ public class AbpTabsTagHelperService : AbpTagHelperService<AbpTabsTagHelper>
var id = TagHelper.Name + "Content";
var wrapper = new TagBuilder("div");
wrapper.AddCssClass("tab-content");
wrapper.AddCssClass("tab-content pt-3");
wrapper.Attributes.Add("id", id);
wrapper.InnerHtml.AppendHtml(contents);

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs

@ -29,6 +29,7 @@ public class GenerateProxyCommand : ProxyCommandBase<GenerateProxyCommand>
sb.AppendLine(" abp generate-proxy -t ng");
sb.AppendLine(" abp generate-proxy -t js -m identity -o Pages/Identity/client-proxies.js -url https://localhost:44302/");
sb.AppendLine(" abp generate-proxy -t csharp --folder MyProxies/InnerFolder -url https://localhost:44302/");
sb.AppendLine(" abp generate-proxy -t csharp -url https://localhost:44302/ --without-contracts");
return sb.ToString();
}

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProxyCommandBase.cs

@ -102,6 +102,7 @@ public abstract class ProxyCommandBase<T> : IConsoleCommand, ITransientDependenc
sb.AppendLine("-u|--url <url> API definition URL from.");
sb.AppendLine("-t|--type <generate-type> The name of generate type (csharp, js, ng).");
sb.AppendLine(" csharp");
sb.AppendLine(" --without-contracts Avoid generating the application service interface, class, enum and dto types.");
sb.AppendLine(" --folder <folder-name> (default: 'ClientProxies') Folder name to place generated CSharp code in.");
sb.AppendLine(" js");
sb.AppendLine(" -o|--output <output-name> JavaScript file path or folder to place generated code in.");

82
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeThemeStep.cs

@ -225,6 +225,12 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
ChangeThemeToLeptonForNoLayersBlazorServerProjects(context);
#endregion
#region MyCompanyName.MyProjectName.MauiBlazor
ChangeThemeToLeptonForMauiBlazorProjects(context);
#endregion
}
private void ConfigureLeptonManagementPackagesForNoLayersMvc(ProjectBuildContext context, string targetProjectPath, string[] projectNames)
@ -488,6 +494,33 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
file.SetLines(lines);
}
protected void ChangeKeyword(
ProjectBuildContext context,
string targetModuleFilePath,
string oldKeyword,
string newKeyword)
{
var file = context.Files.FirstOrDefault(x => x.Name.Contains(targetModuleFilePath));
if (file == null)
{
return;
}
file.NormalizeLineEndings();
var lines = file.GetLines();
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(oldKeyword))
{
lines[i] = lines[i].Replace(oldKeyword, newKeyword);
}
}
file.SetLines(lines);
}
protected void ReplaceImportPackage(
ProjectBuildContext context,
string filePath,
@ -973,4 +1006,53 @@ public class ChangeThemeStep : ProjectBuildPipelineStep
);
}
}
private void ChangeThemeToLeptonForMauiBlazorProjects(ProjectBuildContext context)
{
ReplacePackageReferenceWithProjectReference(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/MyCompanyName.MyProjectName.MauiBlazor.csproj",
"Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme",
@"..\..\..\..\..\lepton-theme\src\Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme\Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme.csproj"
);
ChangeNamespaceAndKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/MyProjectNameMauiBlazorModule.cs",
"Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme",
"Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme",
"AbpAspNetCoreComponentsMauiBlazorLeptonXThemeModule",
"AbpAspNetCoreComponentsMauiBlazorLeptonThemeModule"
);
ChangeKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/MainPage.xaml",
"clr-namespace:Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components;assembly=Volo.Abp.AspNetCore.Components.Web.LeptonXTheme",
"clr-namespace:Volo.Abp.AspNetCore.Components.Web.LeptonTheme.Components;assembly=Volo.Abp.AspNetCore.Components.Web.LeptonTheme");
ChangeKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/MainPage.xaml",
"leptonXTheme",
"leptonTheme");
ChangeKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/Login.razor",
"Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonXTheme.Components.AccountLayout",
"Volo.Abp.AspNetCore.Components.MauiBlazor.LeptonTheme.Components.AccountLayout");
ChangeKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/RedirectToLogout.razor",
"LeptonXResource",
"LeptonThemeManagementResource");
ChangeKeyword(
context,
"/MyCompanyName.MyProjectName.MauiBlazor/Pages/Account/RedirectToLogout.razor",
"Volo.Abp.LeptonX.Shared.Localization",
"Volo.Abp.LeptonTheme.Management.Localization");
}
}

49
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@ -66,6 +67,12 @@ public class AngularSourceCodeAdder : ITransientDependency
}
}
public async Task AddModuleConfigurationAsync(string angularPath, string moduleName)
{
await AddProjectToEnvironmentTsAsync(angularPath, moduleName);
await AddProjectToAppModuleTsAsync(angularPath, moduleName);
}
private async Task AddProjectsToAngularJsonAsync(string angularPath, List<string> projects)
{
var angularJsonFilePath = Path.Combine(angularPath, "angular.json");
@ -229,6 +236,46 @@ public class AngularSourceCodeAdder : ITransientDependency
File.WriteAllText(tsConfigPath, tsConfigAsJson.ToString(Formatting.Indented));
}
private async Task AddProjectToEnvironmentTsAsync(string angularPath, string moduleName)
{
var filePath = Path.Combine(angularPath, "src", "environments", "environment.ts");
if (!File.Exists(filePath))
{
return;
}
var fileContent = File.ReadAllText(filePath);
fileContent = Regex.Replace(fileContent, @"apis\s*:\s*{",
"apis: {"+ Environment.NewLine +
" " + moduleName.Split(".").Last() + ":"+ Environment.NewLine +
" rootNamespace: '" + moduleName + "',"+ Environment.NewLine +
" },");
File.WriteAllText(filePath, fileContent);
}
private async Task AddProjectToAppModuleTsAsync(string angularPath, string moduleName)
{
var filePath = Path.Combine(angularPath, "src", "app", "app.module.ts");
if (!File.Exists(filePath))
{
return;
}
var fileContent = File.ReadAllText(filePath);
fileContent = "import { "+moduleName.Split(".").Last()+"Module } from '@"+moduleName.Split(".").Last().ToKebabCase()+"/config';" + Environment.NewLine + fileContent;
fileContent = Regex.Replace(fileContent, "imports\\s*:\\s*\\[",
"imports: ["+ Environment.NewLine +
" " + moduleName.Split(".").Last() + "Module.forRoot(),");
File.WriteAllText(filePath, fileContent);
}
private async Task<List<string>> CopyAndGetNamesOfAngularProjectsAsync(string solutionFilePath,
string angularProjectsPath)
{
@ -305,7 +352,7 @@ public class AngularSourceCodeAdder : ITransientDependency
return projects;
}
private async Task<string> GetProjectPackageNameAsync(string angularProjectsPath, string project)
{
var packageJsonPath = Path.Combine(angularProjectsPath, project, "package.json");

3
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs

@ -16,5 +16,6 @@ public enum NuGetPackageTarget : byte
Blazor = 11,
IdentityServer = 12, //todo: Rename to AuthServer
BlazorServer = 13,
BlazorWebAssembly = 14
BlazorWebAssembly = 14,
MauiBlazor = 15
}

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs

@ -49,7 +49,8 @@ public static class ProjectFinder
FindProjectEndsWith(projectFiles, assemblyNames, ".Web") ??
FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Host");
case NuGetPackageTarget.Blazor:
return FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor");
return FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor")
?? FindProjectEndsWith(projectFiles, assemblyNames, ".MauiBlazor");;
case NuGetPackageTarget.BlazorWebAssembly:
var BlazorWebAssemblyTargetProject = FindProjectEndsWith(projectFiles, assemblyNames, ".Blazor");
return BlazorWebAssemblyTargetProject != null &&
@ -62,6 +63,8 @@ public static class ProjectFinder
BlazorProjectTypeChecker.IsBlazorServerProject(BlazorServerTargetProject)
? BlazorServerTargetProject
: null;
case NuGetPackageTarget.MauiBlazor:
return FindProjectEndsWith(projectFiles, assemblyNames, ".MauiBlazor");
default:
return null;
}

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -428,6 +428,11 @@ public class SolutionModuleAdder : ITransientDependency
await PublishEventAsync(9, $"Adding angular source code");
await AngularSourceCodeAdder.AddFromModuleAsync(solutionFilePath, angularPath);
if (newTemplate)
{
await AngularSourceCodeAdder.AddModuleConfigurationAsync(angularPath, moduleName);
}
}
private static void DeleteAngularDirectoriesInModulesFolder(string modulesFolderInSolution)

8
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs

@ -76,11 +76,9 @@ public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase<AngularSer
commandBuilder.Append($" --url {url}");
}
var serviceType = GetServiceType(args);
if (args.ServiceType != null)
{
commandBuilder.Append($" --service-type {serviceType.ToString().ToLower()}");
}
var serviceType = GetServiceType(args) ?? Volo.Abp.Cli.ServiceProxying.ServiceType.Application;
commandBuilder.Append($" --service-type {serviceType.ToString().ToLower()}");
_cmdhelper.RunCmd(commandBuilder.ToString());
}

55
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs

@ -19,7 +19,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
private const string ProxyDirectory = "ClientProxies";
private static readonly string[] ServicePostfixes = { "AppService", "ApplicationService", "IntService", "IntegrationService" , "Service"};
private readonly static string[] ServicePostfixes = { "AppService", "ApplicationService", "IntService", "IntegrationService" , "Service"};
private const string AppServicePrefix = "Volo.Abp.Application.Services";
private const string NamespacePlaceholder = "<namespace>";
@ -29,7 +29,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
private const string ClassNamePlaceholder = "<className>";
private const string ServiceInterfacePlaceholder = "<serviceInterface>";
private const string DtoClassNamePlaceholder = "<dtoName>";
private static readonly string ClassTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
private readonly static string ClassTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
@ -44,7 +44,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private static readonly string ClassTemplateEmptyPart = "// This file is part of <className>, you can customize it here" +
private readonly static string ClassTemplateEmptyPart = "// This file is part of <className>, you can customize it here" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>;" +
$"{Environment.NewLine}" +
@ -53,7 +53,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private static readonly string InterfaceTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
private readonly static string InterfaceTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
@ -65,24 +65,22 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private static readonly string DtoTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}using System;" +
$"{Environment.NewLine}using System.Collections.Generic;" +
$"{Environment.NewLine}using Volo.Abp.Application.Dtos;" +
$"{Environment.NewLine}using Volo.Abp.ObjectExtending;" +
$"{Environment.NewLine}<using>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>;" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}public <dtoName>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} <property>" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private static readonly List<string> ClassUsingNamespaceList = new()
private readonly static string DtoTemplate = "// This file is automatically generated by ABP framework to use MVC Controllers from CSharp" +
$"{Environment.NewLine}<using>" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}// ReSharper disable once CheckNamespace" +
$"{Environment.NewLine}namespace <namespace>;" +
$"{Environment.NewLine}" +
$"{Environment.NewLine}public <dtoName>" +
$"{Environment.NewLine}{{" +
$"{Environment.NewLine} <property>" +
$"{Environment.NewLine}}}" +
$"{Environment.NewLine}";
private readonly static List<string> ClassUsingNamespaceList = new()
{
"using System;",
"using System.Collections.Generic;",
"using System.Threading.Tasks;",
"using Volo.Abp.Application.Dtos;",
"using Volo.Abp.Http.Client;",
@ -91,14 +89,23 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
"using Volo.Abp.Http.Client.ClientProxying;"
};
private static readonly List<string> InterfaceUsingNamespaceList = new()
private readonly static List<string> InterfaceUsingNamespaceList = new()
{
"using System;",
"using System.Collections.Generic;",
"using System.Threading.Tasks;",
"using Volo.Abp.Application.Dtos;",
"using Volo.Abp.Application.Services;"
};
private readonly static List<string> DtoUsingNamespaceList = new()
{
"using System;",
"using System.Collections.Generic;",
"using Volo.Abp.Application.Dtos;",
"using Volo.Abp.ObjectExtending;",
};
public CSharpServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer) :
@ -106,7 +113,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
{
}
public override async Task GenerateProxyAsync(GenerateProxyArgs args)
public async override Task GenerateProxyAsync(GenerateProxyArgs args)
{
CheckWorkDirectory(args.WorkDirectory);
CheckFolder(args.Folder);
@ -418,7 +425,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
foreach (var type in applicationApiDescriptionModel.Types.Where(x => types.Contains(x.Key)))
{
var dto = new StringBuilder(DtoTemplate);
var dtoUsingNamespaceList = new List<string>()
var dtoUsingNamespaceList = new List<string>(DtoUsingNamespaceList)
{
$"using {GetTypeNamespace(type.Key)};"
};
@ -478,7 +485,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase<CSharpServi
}
}
dto.Replace($"{UsingPlaceholder}", string.Join(Environment.NewLine, dtoUsingNamespaceList.OrderBy(x => x).Select(x => x)));
dto.Replace($"{UsingPlaceholder}", string.Join(Environment.NewLine, dtoUsingNamespaceList.Distinct().OrderBy(x => x).Select(x => x)));
dto.Replace(PropertyPlaceholder, properties.ToString());
var folder = args.Folder.IsNullOrWhiteSpace()

4
framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs

@ -16,14 +16,14 @@ public interface IAbpApplication :
Type StartupModuleType { get; }
/// <summary>
/// List of services registered to this application.
/// List of all service registrations.
/// Can not add new services to this collection after application initialize.
/// </summary>
IServiceCollection Services { get; }
/// <summary>
/// Reference to the root service provider used by the application.
/// This can not be used before initialize the application.
/// This can not be used before initializing the application.
/// </summary>
IServiceProvider ServiceProvider { get; }

1
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/AbpHttpClientModule.cs

@ -23,6 +23,7 @@ public class AbpHttpClientModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClient();
context.Services.AddTransient(typeof(DynamicHttpProxyInterceptorClientProxy<>));
}
}

5
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs

@ -102,9 +102,6 @@ public class Product : FullAuditedAggregateRoot<Guid>
Price = price;
}
[JsonInclude]
public override Guid Id { get; protected set; }
public string Name { get; set; }
public decimal Price { get; set; }
@ -119,4 +116,4 @@ public class ProductCacheItem
public string Name { get; set; }
public decimal Price { get; set; }
}
}

26
modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs

@ -1,28 +1,24 @@
using System.Threading.Tasks;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.CmsKit.Pages;
using Volo.CmsKit.Public.Pages;
namespace Volo.CmsKit.Public.Web;
public class DefaultHomePageMiddleware
public class DefaultHomePageMiddleware : IMiddleware, ITransientDependency
{
private readonly RequestDelegate _next;
private readonly IPagePublicAppService _pagePublicAppService;
public DefaultHomePageMiddleware(RequestDelegate next, IPagePublicAppService pagePublicAppService)
{
_next = next;
_pagePublicAppService = pagePublicAppService;
}
public async Task InvokeAsync(HttpContext httpContext)
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var _pagePublicAppService = context.RequestServices.GetRequiredService<IPagePublicAppService>();
var page = await _pagePublicAppService.FindDefaultHomePageAsync();
if (page is not null && httpContext.Request.Path.Value == "/")
if (page is not null && context.Request.Path.Value == "/")
{
httpContext.Request.Path = $"{PageConsts.UrlPrefix}{page.Slug}";
context.Request.Path = $"{PageConsts.UrlPrefix}{page.Slug}";
}
await _next(httpContext);
await next(context);
}
}

6
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj

@ -20,8 +20,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="4.0.0-preview6.22566.67" />
<PackageReference Include="OpenIddict.Validation.AspNetCore" Version="4.0.0-preview6.22566.67" />
<PackageReference Include="OpenIddict.Validation.ServerIntegration" Version="4.0.0-preview6.22566.67" />
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="4.0.0-rc1.22607.41" />
<PackageReference Include="OpenIddict.Validation.AspNetCore" Version="4.0.0-rc1.22607.41" />
<PackageReference Include="OpenIddict.Validation.ServerIntegration" Version="4.0.0-rc1.22607.41" />
</ItemGroup>
</Project>

22
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs

@ -54,19 +54,19 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule
.AddServer(builder =>
{
builder
.SetAuthorizationEndpointUris("/connect/authorize", "/connect/authorize/callback")
// /.well-known/oauth-authorization-server
// /.well-known/openid-configuration
.SetAuthorizationEndpointUris("connect/authorize", "connect/authorize/callback")
// .well-known/oauth-authorization-server
// .well-known/openid-configuration
//.SetConfigurationEndpointUris()
// /.well-known/jwks
// .well-known/jwks
//.SetCryptographyEndpointUris()
.SetDeviceEndpointUris("/device")
.SetIntrospectionEndpointUris("/connect/introspect")
.SetLogoutEndpointUris("/connect/logout")
.SetRevocationEndpointUris("/connect/revocat")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify");
.SetDeviceEndpointUris("device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetRevocationEndpointUris("connect/revocat")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");
builder
.AllowAuthorizationCodeFlow()

2
modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj

@ -14,7 +14,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="OpenIddict.Abstractions" Version="4.0.0-preview6.22566.67" />
<PackageReference Include="OpenIddict.Abstractions" Version="4.0.0-rc1.22607.41" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
</ItemGroup>

2
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="OpenIddict.Core" Version="4.0.0-preview6.22566.67" />
<PackageReference Include="OpenIddict.Core" Version="4.0.0-rc1.22607.41" />
</ItemGroup>
</Project>

34
modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs

@ -75,31 +75,31 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
return (await Repository.FindByClientIdAsync(identifier, cancellationToken: cancellationToken)).ToModel();
}
public async IAsyncEnumerable<OpenIddictApplicationModel> FindByPostLogoutRedirectUriAsync(string address, [EnumeratorCancellation] CancellationToken cancellationToken)
public async IAsyncEnumerable<OpenIddictApplicationModel> FindByPostLogoutRedirectUriAsync(string uris, [EnumeratorCancellation] CancellationToken cancellationToken)
{
Check.NotNullOrEmpty(address, nameof(address));
Check.NotNullOrEmpty(uris, nameof(uris));
var applications = await Repository.FindByPostLogoutRedirectUriAsync(address, cancellationToken);
var applications = await Repository.FindByPostLogoutRedirectUriAsync(uris, cancellationToken);
foreach (var application in applications)
{
var addresses = await GetPostLogoutRedirectUrisAsync(application.ToModel(), cancellationToken);
if (addresses.Contains(address, StringComparer.Ordinal))
if (addresses.Contains(uris, StringComparer.Ordinal))
{
yield return application.ToModel();
}
}
}
public async IAsyncEnumerable<OpenIddictApplicationModel> FindByRedirectUriAsync(string address, [EnumeratorCancellation] CancellationToken cancellationToken)
public async IAsyncEnumerable<OpenIddictApplicationModel> FindByRedirectUriAsync(string uri, [EnumeratorCancellation] CancellationToken cancellationToken)
{
Check.NotNullOrEmpty(address, nameof(address));
Check.NotNullOrEmpty(uri, nameof(uri));
var applications = await Repository.FindByRedirectUriAsync(address, cancellationToken);
var applications = await Repository.FindByRedirectUriAsync(uri, cancellationToken);
foreach (var application in applications)
{
var addresses = await GetRedirectUrisAsync(application.ToModel(), cancellationToken);
if (addresses.Contains(address, StringComparer.Ordinal))
var uris = await GetRedirectUrisAsync(application.ToModel(), cancellationToken);
if (uris.Contains(uri, StringComparer.Ordinal))
{
yield return application.ToModel();
}
@ -423,12 +423,12 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
return default;
}
public virtual ValueTask SetPostLogoutRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray<string> addresses,
public virtual ValueTask SetPostLogoutRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray<string> uris,
CancellationToken cancellationToken)
{
Check.NotNull(application, nameof(application));
if (addresses.IsDefaultOrEmpty)
if (uris.IsDefaultOrEmpty)
{
application.PostLogoutRedirectUris = null;
return default;
@ -437,9 +437,9 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
application.PostLogoutRedirectUris = WriteStream(writer =>
{
writer.WriteStartArray();
foreach (var address in addresses)
foreach (var uri in uris)
{
writer.WriteStringValue(address);
writer.WriteStringValue(uri);
}
writer.WriteEndArray();
});
@ -472,12 +472,12 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
return default;
}
public virtual ValueTask SetRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray<string> addresses,
public virtual ValueTask SetRedirectUrisAsync(OpenIddictApplicationModel application, ImmutableArray<string> uris,
CancellationToken cancellationToken)
{
Check.NotNull(application, nameof(application));
if (addresses.IsDefaultOrEmpty)
if (uris.IsDefaultOrEmpty)
{
application.RedirectUris = null;
return default;
@ -486,9 +486,9 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase<IOpenIddictA
application.RedirectUris = WriteStream(writer =>
{
writer.WriteStartArray();
foreach (var address in addresses)
foreach (var uri in uris)
{
writer.WriteStringValue(address);
writer.WriteStringValue(uri);
}
writer.WriteEndArray();
});

6
npm/ng-packs/.prettierignore

@ -2,3 +2,9 @@
/dist
/coverage
/.angular
/.vscode
/node_modules
/scripts
/tools
/source-code-requirements

10
npm/ng-packs/README.md

@ -22,4 +22,12 @@ The `dev-app` project is the same as the Angular UI template project. `dev-app`
For more information, see the [docs.abp.io](https://docs.abp.io)
If would you like contribute, see the [contribution guideline](./CONTRIBUTING.md).
### Git Hooks
There is a pre-defined git hook for Angular project. This hook is `pre-commit`. The hook run prettier for staged files. If you want to use this hook. (This steps tested for linux and Mac os)
- Go to `npm/ng-packs`
- Run `chmod +x ./pre-commit` in terminal
- Run `git config core.hooksPath npm/ng-packs` in terminal
and now, when you create a git commit, git hook execute prettier for staged files.

12
npm/ng-packs/package.json

@ -10,6 +10,7 @@
"build:all": "nx run-many --target=build --all --exclude=dev-app,schematics --prod && yarn build:schematics",
"test": "ng test --detect-open-handles=true --run-in-band=true --watch-all=true",
"test:all": "nx run-many --target=test --all",
"lint-staged": "lint-staged",
"lint": "nx workspace-lint && ng lint",
"lint:all": "nx run-many --target=lint --all",
"e2e": "ng e2e",
@ -36,11 +37,10 @@
"lerna": "lerna",
"migrate-nx": "yarn nx migrate --run-migrations",
"mock:schematics": "cd scripts/mock-schematic && yarn && yarn start",
"debug:schematics": "./node_modules/.bin/ng g ./packages/schematics/src/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url http://localhost:4300"
"debug:schematics": "./node_modules/.bin/ng g ./packages/schematics/src/collection.json:proxy-add --module __default --apiName __default --source __default --target __default --url https://localhost:44305 --serviceType application"
},
"private": true,
"devDependencies": {
"@abp/utils": "~7.0.0-rc.2",
"@angular-devkit/build-angular": "14.2.1",
"@angular-devkit/build-ng-packagr": "^0.1002.0",
"@angular-devkit/schematics-cli": "~14.2.1",
@ -105,6 +105,7 @@
"just-clone": "^3.2.1",
"just-compare": "^1.4.0",
"lerna": "^4.0.0",
"lint-staged": "^13.0.3",
"ng-packagr": "14.2.1",
"ng-zorro-antd": "^14.0.0",
"nx": "14.7.5",
@ -125,5 +126,10 @@
"typescript": "4.7.4",
"zone.js": "0.11.4"
},
"dependencies": {}
"dependencies": {},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}":[
"npx prettier --write --config .prettierrc "
]
}
}

8
npm/ng-packs/packages/components/tree/src/lib/components/tree.component.scss

@ -1,4 +1,4 @@
.ant-tree {
abp-tree .ant-tree {
color: inherit;
.ant-tree-node-content-wrapper.ant-tree-node-selected {
@ -7,6 +7,9 @@
.ant-tree-switcher {
line-height: 17px;
align-items: center;
justify-content: center;
display: inline-flex;
}
.ant-tree-node-content-wrapper {
@ -19,13 +22,12 @@
position: relative;
display: inline-block;
margin: 0;
padding: 0 5px;
line-height: 30px;
text-decoration: none;
vertical-align: top;
border-radius: 2px;
cursor: pointer;
padding-left: 8px;
padding: 0 5px 0 8px;
border: 1px solid transparent;
}

13
npm/ng-packs/packages/components/tree/src/lib/components/tree.component.ts

@ -12,22 +12,27 @@ import { of } from 'rxjs';
import { TreeNodeTemplateDirective } from '../templates/tree-node-template.directive';
import { ExpandedIconTemplateDirective } from '../templates/expanded-icon-template.directive';
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
import { LazyLoadService, LOADING_STRATEGY, SubscriptionService } from '@abp/ng.core';
export type DropEvent = NzFormatEmitEvent & { pos: number };
@Component({
selector: 'abp-tree',
templateUrl: 'tree.component.html',
styleUrls: [
'../../../../../../node_modules/ng-zorro-antd/tree/style/index.min.css',
'tree.component.scss',
],
styleUrls: ['tree.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [SubscriptionService],
})
export class TreeComponent {
dropPosition: number;
dropdowns = {} as { [key: string]: NgbDropdown };
constructor(private lazyLoadService: LazyLoadService, subscriptionService: SubscriptionService) {
const loaded$ = this.lazyLoadService.load(
LOADING_STRATEGY.AppendAnonymousStyleToHead('ng-zorro-antd-tree.css'),
);
subscriptionService.addOne(loaded$);
}
@ContentChild('menu') menu: TemplateRef<any>;
@ContentChild(TreeNodeTemplateDirective) customNodeTemplate: TreeNodeTemplateDirective;

7
npm/ng-packs/packages/core/src/lib/services/session-state.service.ts

@ -31,7 +31,7 @@ export class SessionStateService {
}
private setInitialLanguage() {
if (this.getLanguage()) return;
const appLanguage = this.getLanguage();
this.configState
.getDeep$('localization.currentCulture.cultureName')
@ -43,8 +43,9 @@ export class SessionStateService {
if (lang.includes(';')) {
lang = lang.split(';')[0];
}
this.setLanguage(lang);
if (appLanguage !== lang) {
this.setLanguage(lang);
}
});
}

29
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts

@ -164,25 +164,16 @@ export class PermissionManagementComponent
return;
setTimeout(() => {
this.permissions = this.permissions
.map(per => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
return { ...per, isGranted: false };
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true };
}
return per;
})
.map((per, index, permissions) => {
const childrens = permissions.filter(p => p.parentName === per.name);
if (childrens.length > 0 && childrens.every(x => !x.isGranted)) {
return { ...per, isGranted: false };
}
return per;
});
this.permissions = this.permissions.map(per => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
return { ...per, isGranted: false };
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true };
}
return per;
});
this.setTabCheckboxState();
this.setGrantCheckboxState();
}, 0);

18
npm/ng-packs/packages/schematics/src/commands/api/index.ts

@ -8,8 +8,8 @@ import {
Tree,
url,
} from '@angular-devkit/schematics';
import { Exception } from '../../enums';
import { GenerateProxySchema, ServiceGeneratorParams } from '../../models';
import { defaultEServiceType, eServiceType, Exception } from '../../enums';
import { Controller, GenerateProxySchema, ServiceGeneratorParams } from '../../models';
import {
applyWithOverwrite,
buildTargetPath,
@ -47,6 +47,7 @@ export default function (schema: GenerateProxySchema) {
const data = readProxyConfig(tree);
const types = data.types;
const modules = data.modules;
const serviceType = schema.serviceType || defaultEServiceType;
if (!types || !modules) throw new SchematicsException(Exception.InvalidApiDefinition);
const definition = data.modules[moduleName];
@ -54,7 +55,7 @@ export default function (schema: GenerateProxySchema) {
throw new SchematicsException(interpolate(Exception.InvalidModule, moduleName));
const apiName = definition.remoteServiceName;
const controllers = Object.values(definition.controllers || {});
const controllers = filterControllersByServiceType(serviceType, definition.controllers);
const serviceImports: Record<string, string[]> = {};
const generateServices = createServiceGenerator({
targetPath,
@ -170,3 +171,14 @@ function createServiceGenerator(params: ServiceGeneratorParams) {
}),
);
}
function filterControllersByServiceType(
serviceType: eServiceType,
controllers: Record<string, Controller>,
): Controller[] {
const itShouldBeIntegratedService = serviceType === eServiceType.Integration;
const skipFilter = serviceType === eServiceType.All;
return Object.values(controllers || {}).filter(
x => x.isIntegrationService === itShouldBeIntegratedService || skipFilter,
);
}

34
npm/ng-packs/packages/schematics/src/commands/api/schema.json

@ -45,10 +45,42 @@
"type": "string",
"$default": {
"$source": "argv",
"index": 5
"index": 4
},
"x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")"
},
"serviceType": {
"description": "Service type to the generated code",
"type": "string",
"$default": {
"$source": "argv",
"index": 5
},
"enum": [
"application",
"integration",
"all"
],
"x-prompt": {
"message": "Specifies the service type to generate. `application`, `integration` and `all`, Default value: `application`",
"type": "list",
"items": [
{
"value": "all",
"label": "All"
},
{
"value": "application",
"label": "Application"
},
{
"value": "integration",
"label": "Integration"
}
]
}
}
},
"required": []
}

20
npm/ng-packs/packages/schematics/src/commands/change-theme/style-map.ts

@ -161,6 +161,16 @@ styleMap.set(ThemeOptionsEnum.LeptonX, [
inject: false,
bundleName: 'bootstrap-light.rtl',
},
{
input: 'node_modules/@volosoft/ngx-lepton-x/assets/css/font-bundle.css',
inject: false,
bundleName: 'font-bundle',
},
{
input: 'node_modules/@volosoft/ngx-lepton-x/assets/css/font-bundle.rtl.css',
inject: false,
bundleName: 'font-bundle.rtl',
},
{
input: 'node_modules/@volosoft/ngx-lepton-x/assets/css/ng-bundle.rtl.css',
inject: false,
@ -202,6 +212,16 @@ styleMap.set(ThemeOptionsEnum.LeptonXLite, [
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.rtl.css',
inject: false,
bundleName: 'bootstrap-dim.rtl',
},
{
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css',
inject: false,
bundleName: 'font-bundle.rtl',
},
{
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css',
inject: false,
bundleName: 'font-bundle',
},
{
input: 'node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.rtl.css',

31
npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json

@ -48,6 +48,37 @@
"index": 4
},
"x-prompt": "Please enter URL for API definition (default: API Name's url in environment file)"
},
"serviceType": {
"description": "Service type to the generated code",
"type": "string",
"$default": {
"$source": "argv",
"index": 5
},
"enum": [
"application",
"integration",
"all"
],
"x-prompt": {
"message": "Specifies the service type to generate. `application`, `integration` and `all`, Default value: `application`",
"type": "list",
"items": [
{
"value": "all",
"label": "All"
},
{
"value": "application",
"label": "Application"
},
{
"value": "integration",
"label": "Integration"
}
]
}
}
},
"entryPoint": {

1
npm/ng-packs/packages/schematics/src/enums/index.ts

@ -2,3 +2,4 @@ export * from './binding-source-id';
export * from './exception';
export * from './import-keyword';
export * from './method-modifier';
export * from './service-types';

7
npm/ng-packs/packages/schematics/src/enums/service-types.ts

@ -0,0 +1,7 @@
export enum eServiceType {
All = 'all',
Application = 'application',
Integration = 'integration',
}
export const defaultEServiceType = eServiceType.Application;

2
npm/ng-packs/packages/schematics/src/models/api-definition.ts

@ -31,6 +31,8 @@ export interface Module {
export interface Controller {
controllerName: string;
type: string;
isRemoteService: boolean;
isIntegrationService: boolean;
interfaces: InterfaceDef[];
actions: Record<string, Action>;
}

3
npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts

@ -1,3 +1,5 @@
import { eServiceType } from '../enums';
export interface GenerateProxySchema {
/**
* Backend module name
@ -28,4 +30,5 @@ export interface GenerateProxySchema {
* Secondary entrypoint for proxy generation
*/
entryPoint?: string;
serviceType?: eServiceType;
}

2
npm/ng-packs/pre-commit

@ -0,0 +1,2 @@
#!/usr/bin/env sh
cd npm/ng-packs && npm run lint-staged

1
npm/ng-packs/scripts/copy-packages-to-templates.ts

@ -17,6 +17,7 @@ const packageMap = {
'tenant-management': 'ng.tenant-management',
'theme-basic': 'ng.theme.basic',
'theme-shared': 'ng.theme.shared',
'schematics':'ng.schematics'
};
program.option('-t, --templates <templates>', 'template dirs', false);
program.option('-p, --template-path <templatePath>', 'root template path', false);

82
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Menus/MyProjectNameMenuContributor.cs

@ -0,0 +1,82 @@
using MyCompanyName.MyProjectName.Blazor.Menus;
using MyCompanyName.MyProjectName.Shared.Localization;
using MyCompanyName.MyProjectName.Shared.MultiTenancy;
using Volo.Abp.Account.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Identity.Blazor;
using Volo.Abp.SettingManagement.Blazor.Menus;
using Volo.Abp.TenantManagement.Blazor.Navigation;
using Volo.Abp.UI.Navigation;
namespace MyCompanyName.MyProjectName.Client.Menus;
public class MyProjectNameMenuContributor : IMenuContributor
{
private readonly IConfiguration _configuration;
public MyProjectNameMenuContributor(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name == StandardMenus.Main)
{
await ConfigureMainMenuAsync(context);
}
else if (context.Menu.Name == StandardMenus.User)
{
await ConfigureUserMenuAsync(context);
}
}
private Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{
var l = context.GetLocalizer<MyProjectNameResource>();
context.Menu.Items.Insert(
0,
new ApplicationMenuItem(
MyProjectNameMenus.Home,
l["Menu:Home"],
"/",
icon: "fas fa-home",
order: 0
)
);
var administration = context.Menu.GetAdministration();
if (MultiTenancyConsts.IsEnabled)
{
administration.SetSubItemOrder(TenantManagementMenuNames.GroupName, 1);
}
else
{
administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName);
}
administration.SetSubItemOrder(IdentityMenuNames.GroupName, 2);
administration.SetSubItemOrder(SettingManagementMenus.GroupName, 3);
return Task.CompletedTask;
}
private Task ConfigureUserMenuAsync(MenuConfigurationContext context)
{
var accountStringLocalizer = context.GetLocalizer<AccountResource>();
var authServerUrl = _configuration["AuthServer:Authority"] ?? "";
context.Menu.AddItem(new ApplicationMenuItem(
"Account.Manage",
accountStringLocalizer["MyAccount"],
$"{authServerUrl.EnsureEndsWith('/')}Account/Manage?returnUrl={_configuration["App:SelfUrl"]}",
icon: "fa fa-cog",
order: 1000,
null).RequireAuthenticated());
return Task.CompletedTask;
}
}

10
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Menus/MyProjectNameMenus.cs

@ -0,0 +1,10 @@
namespace MyCompanyName.MyProjectName.Blazor.Menus;
public class MyProjectNameMenus
{
private const string Prefix = "MyProjectName";
public const string Home = Prefix + ".Home";
//Add your menu items here...
}

47
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap5" Version="1.1.4.1" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.1.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<!-- <TEMPLATE-REMOVE> -->
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.WebAssembly.Theming\Volo.Abp.AspNetCore.Components.WebAssembly.Theming.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Http.Client.IdentityModel.WebAssembly\Volo.Abp.Http.Client.IdentityModel.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Components.Web.Theming\Volo.Abp.AspNetCore.Components.Web.Theming.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme" Version="2.0.0-*" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Autofac.WebAssembly\Volo.Abp.Autofac.WebAssembly.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.Blazor.WebAssembly\Volo.Abp.Identity.Blazor.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.Blazor.WebAssembly\Volo.Abp.TenantManagement.Blazor.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.Blazor.WebAssembly\Volo.Abp.SettingManagement.Blazor.WebAssembly.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\account\src\Volo.Abp.Account.HttpApi.Client\Volo.Abp.Account.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.HttpApi.Client\Volo.Abp.Identity.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.HttpApi.Client\Volo.Abp.PermissionManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.HttpApi.Client\Volo.Abp.TenantManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.HttpApi.Client\Volo.Abp.FeatureManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.HttpApi.Client\Volo.Abp.SettingManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\openiddict\src\Volo.Abp.OpenIddict.Domain.Shared\Volo.Abp.OpenIddict.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj" />
</ItemGroup>
</Project>

11
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBlazorAutoMapperProfile.cs

@ -0,0 +1,11 @@
using AutoMapper;
namespace MyCompanyName.MyProjectName.Client;
public class MyProjectNameBlazorAutoMapperProfile : Profile
{
public MyProjectNameBlazorAutoMapperProfile()
{
//Define your AutoMapper configuration here for the Blazor project.
}
}

142
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBlazorModule.cs

@ -0,0 +1,142 @@
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MyCompanyName.MyProjectName.Client.Menus;
using MyCompanyName.MyProjectName.Shared;
using OpenIddict.Abstractions;
using Volo.Abp.Account;
using Volo.Abp.AspNetCore.Components.Web.LeptonXLiteTheme.Themes.LeptonXLite;
using Volo.Abp.AspNetCore.Components.Web.Theming.Routing;
using Volo.Abp.AspNetCore.Components.WebAssembly.LeptonXLiteTheme;
using Volo.Abp.Autofac.WebAssembly;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Blazor.WebAssembly;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.SettingManagement.Blazor.WebAssembly;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.Blazor.WebAssembly;
using Volo.Abp.UI.Navigation;
namespace MyCompanyName.MyProjectName.Client;
[DependsOn(
typeof(MyProjectNameSharedModule),
// ABP Framework packages
typeof(AbpAutofacWebAssemblyModule),
typeof(AbpAspNetCoreComponentsWebAssemblyLeptonXLiteThemeModule),
// Account module packages
typeof(AbpAccountHttpApiClientModule),
// Identity module packages
typeof(AbpIdentityHttpApiClientModule),
typeof(AbpIdentityBlazorWebAssemblyModule),
typeof(AbpOpenIddictDomainSharedModule),
// Permission Management module packages
typeof(AbpPermissionManagementHttpApiClientModule),
// Tenant Management module packages
typeof(AbpTenantManagementHttpApiClientModule),
typeof(AbpTenantManagementBlazorWebAssemblyModule),
// Feature Management module packages
typeof(AbpFeatureManagementHttpApiClientModule),
// Setting Management module packages
typeof(AbpSettingManagementHttpApiClientModule),
typeof(AbpSettingManagementBlazorWebAssemblyModule)
)]
public class MyProjectNameBlazorModule : AbpModule
{
public const string RemoteServiceName = "Default";
public override void ConfigureServices(ServiceConfigurationContext context)
{
var environment = context.Services.GetSingletonInstance<IWebAssemblyHostEnvironment>();
var builder = context.Services.GetSingletonInstance<WebAssemblyHostBuilder>();
ConfigureAuthentication(builder);
ConfigureHttpClient(context, environment);
ConfigureBlazorise(context);
ConfigureRouter(context);
ConfigureUI(builder);
ConfigureMenu(context);
ConfigureAutoMapper(context);
ConfigureHttpClientProxies(context);
}
private void ConfigureRouter(ServiceConfigurationContext context)
{
Configure<AbpRouterOptions>(options =>
{
options.AppAssembly = typeof(MyProjectNameBlazorModule).Assembly;
});
}
private void ConfigureMenu(ServiceConfigurationContext context)
{
Configure<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new MyProjectNameMenuContributor(context.Services.GetConfiguration()));
});
}
private void ConfigureBlazorise(ServiceConfigurationContext context)
{
context.Services
.AddBootstrap5Providers()
.AddFontAwesomeIcons();
}
private void ConfigureHttpClientProxies(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(
typeof(MyProjectNameSharedModule).Assembly,
RemoteServiceName
);
}
private static void ConfigureAuthentication(WebAssemblyHostBuilder builder)
{
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("AuthServer", options.ProviderOptions);
options.UserOptions.NameClaim = OpenIddictConstants.Claims.Name;
options.UserOptions.RoleClaim = OpenIddictConstants.Claims.Role;
options.ProviderOptions.DefaultScopes.Add("MyProjectName");
options.ProviderOptions.DefaultScopes.Add("roles");
options.ProviderOptions.DefaultScopes.Add("email");
options.ProviderOptions.DefaultScopes.Add("phone");
});
}
private static void ConfigureUI(WebAssemblyHostBuilder builder)
{
builder.RootComponents.Add<App>("#ApplicationContainer");
}
private static void ConfigureHttpClient(ServiceConfigurationContext context, IWebAssemblyHostEnvironment environment)
{
context.Services.AddTransient(sp => new HttpClient
{
BaseAddress = new Uri(environment.BaseAddress)
});
}
private void ConfigureAutoMapper(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<MyProjectNameBlazorModule>();
});
}
}

10
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBrandingProvider.cs

@ -0,0 +1,10 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Ui.Branding;
namespace MyCompanyName.MyProjectName.Client;
[Dependency(ReplaceServices = true)]
public class MyProjectNameBrandingProvider : DefaultBrandingProvider
{
public override string AppName => "MyProjectName";
}

19
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameBundleContributor.cs

@ -0,0 +1,19 @@
using Volo.Abp.Bundling;
namespace MyCompanyName.MyProjectName.Client;
/* Add your global styles/scripts here.
* See https://docs.abp.io/en/abp/latest/UI/Blazor/Global-Scripts-Styles to learn how to use it
*/
public class MyProjectNameBundleContributor : IBundleContributor
{
public void AddScripts(BundleContext context)
{
}
public void AddStyles(BundleContext context)
{
context.Add("main.css", true);
}
}

12
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/MyProjectNameComponentBase.cs

@ -0,0 +1,12 @@
using MyCompanyName.MyProjectName.Shared.Localization;
using Volo.Abp.AspNetCore.Components;
namespace MyCompanyName.MyProjectName.Client;
public abstract class MyProjectNameComponentBase : AbpComponentBase
{
protected MyProjectNameComponentBase()
{
LocalizationResource = typeof(MyProjectNameResource);
}
}

39
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/Index.razor

@ -0,0 +1,39 @@
@page "/"
@inherits MyProjectNameComponentBase
<div class="container">
<div class="card">
<div class="card-header">
<h5 class="card-title">
@L["Welcome_Title"]
</h5>
</div>
<div class="card-body">
@L["Welcome_Text"]
</div>
</div>
<div class="card mt-5">
<div class="card-body">
<div class="row">
<div class="col-md-auto text-center">
<img src="https://abp.io/assets/png/mastering-abp-framework.webp" style="max-width: 400px;" class="w-100 mb-5 my-md-3">
</div>
<div class="col-md d-flex align-items-center">
<div class="pe-0 pe-md-4">
<small class="text-uppercase text-muted">THE OFFICIAL GUIDE</small>
<h2 class="mb-4">Mastering ABP Framework</h2>
<p class="mb-4">Written by the creator of the ABP Framework, this book will help you gain a complete understanding of the framework and modern web application development techniques.</p>
<div class="mb-4">
<a href="https://www.amazon.com/gp/product/B097Z2DM8Q/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0" class="btn btn-success mb-1">
Buy on Amazon US
</a>
<a href="https://www.packtpub.com/product/mastering-abp-framework/9781801079242" class="btn btn-primary mb-1">
Buy on PACKT
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

6
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/Index.razor.cs

@ -0,0 +1,6 @@
namespace MyCompanyName.MyProjectName.Client.Pages;
public partial class Index
{
}

1
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Pages/index.razor.css

@ -0,0 +1 @@
/* Write here your styles for the Index page */

22
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Program.cs

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
namespace MyCompanyName.MyProjectName.Client;
public class Program
{
public async static Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
var application = await builder.AddApplicationAsync<MyProjectNameBlazorModule>(options =>
{
options.UseAutofac();
});
var host = builder.Build();
await application.InitializeApplicationAsync(host.Services);
await host.RunAsync();
}
}

30
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/Properties/launchSettings.json

@ -0,0 +1,30 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44300",
"sslPort": 44300
}
},
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:44300",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

14
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/_Imports.razor

@ -0,0 +1,14 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using Volo.Abp.AspNetCore.Components.Web
@using MyCompanyName.MyProjectName.Client
@using Blazorise
@using Blazorise.DataGrid
@using Volo.Abp.BlazoriseUI
@using Volo.Abp.BlazoriseUI.Components

24
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/appsettings.json

@ -0,0 +1,24 @@
{
"App": {
"SelfUrl": "https://localhost:44300"
},
"AuthServer": {
"Authority": "https://localhost:44300",
"ClientId": "MyProjectName_Blazor",
"ResponseType": "code"
},
"RemoteServices": {
"Default": {
"BaseUrl": "https://localhost:44300"
}
},
"AbpCli": {
"Bundle": {
"Mode": "BundleAndMinify", /* Options: None, Bundle, BundleAndMinify */
"Name": "global",
"Parameters": {
}
}
}
}

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

27
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.css

File diff suppressed because one or more lines are too long

37
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/global.js

File diff suppressed because one or more lines are too long

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/icon-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/icon-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-dark-thumbnail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-dark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-light-thumbnail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/images/logo/leptonx/logo-light.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

35
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/index.html

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>MyCompanyName.MyProjectName.Blazor</title>
<base href="/" />
<!--ABP:Styles-->
<link href="global.css?_v=638058527301625844" rel="stylesheet"/>
<link href="main.css" rel="stylesheet"/>
<!--/ABP:Styles-->
<link href="MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.styles.css" rel="stylesheet"/>
<!-- <TEMPLATE-REMOVE IF-NOT='PWA'> -->
<link href="manifest.json" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
<!-- </TEMPLATE-REMOVE> -->
</head>
<body class="abp-application-layout bg-light">
<div id="ApplicationContainer">
<div class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
</div>
</div>
<!--ABP:Scripts-->
<script src="global.js?_v=638058527305084060"></script>
<!--/ABP:Scripts-->
</body>
</html>

57
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/main.css

@ -0,0 +1,57 @@
/* Global styles for the MyProjectName application */
/* <TEMPLATE-REMOVE IF-NOT='LEPTONX'> */
:root .lpx-brand-logo {
--lpx-logo: url('/images/logo/leptonx/logo-light.png');
--lpx-logo-icon: url('/images/logo/leptonx/logo-light-thumbnail.png');
}
/* </TEMPLATE-REMOVE> */
.spinner {
width: 40px;
height: 40px;
display: block;
position: fixed;
top: calc( 50% - ( 40px / 2) );
right: calc( 50% - ( 40px / 2) );
}
.double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #333;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes sk-bounce {
0%, 100% {
-webkit-transform: scale(0.0)
}
50% {
-webkit-transform: scale(1.0)
}
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
}
50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}

21
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Client/wwwroot/manifest.json

@ -0,0 +1,21 @@
{
"name": "MyProjectName",
"short_name": "MyCompanyName.MyProjectName",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#03173d",
"prefer_related_applications": false,
"icons": [
{
"src": "icon-512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "icon-192.png",
"type": "image/png",
"sizes": "192x192"
}
]
}

22
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameDbContext.cs

@ -0,0 +1,22 @@
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
namespace MyCompanyName.MyProjectName.Server.Data;
[ConnectionStringName("Default")]
public class MyProjectNameDbContext : AbpMongoDbContext
{
/* Add mongo collections here. Example:
* public IMongoCollection<Question> Questions => Collection<Question>();
*/
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
//builder.Entity<YourEntity>(b =>
//{
// //...
//});
}
}

88
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameDbMigrationService.cs

@ -0,0 +1,88 @@
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement;
namespace MyCompanyName.MyProjectName.Server.Data;
public class MyProjectNameDbMigrationService : ITransientDependency
{
public ILogger<MyProjectNameDbMigrationService> Logger { get; set; }
private readonly IDataSeeder _dataSeeder;
private readonly MyProjectNameMongoDbSchemaMigrator _dbSchemaMigrator;
private readonly ITenantRepository _tenantRepository;
private readonly ICurrentTenant _currentTenant;
public MyProjectNameDbMigrationService(
IDataSeeder dataSeeder,
MyProjectNameMongoDbSchemaMigrator dbSchemaMigrator,
ITenantRepository tenantRepository,
ICurrentTenant currentTenant)
{
_dataSeeder = dataSeeder;
_dbSchemaMigrator = dbSchemaMigrator;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
Logger = NullLogger<MyProjectNameDbMigrationService>.Instance;
}
public async Task MigrateAsync()
{
Logger.LogInformation("Started database migrations...");
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
Logger.LogInformation($"Successfully completed host database migrations.");
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
{
await MigrateDatabaseSchemaAsync(tenant);
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
}
}
await SeedDataAsync(tenant);
}
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
}
Logger.LogInformation("Successfully completed all database migrations.");
Logger.LogInformation("You can safely end this process...");
}
private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)
{
Logger.LogInformation($"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");
await _dbSchemaMigrator.MigrateAsync();
}
private async Task SeedDataAsync(Tenant? tenant = null)
{
Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");
await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id)
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue)
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue)
);
}
}

39
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/MyProjectNameMongoDbSchemaMigrator.cs

@ -0,0 +1,39 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MongoDB;
namespace MyCompanyName.MyProjectName.Server.Data;
public class MyProjectNameMongoDbSchemaMigrator : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public MyProjectNameMongoDbSchemaMigrator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
var dbContexts = _serviceProvider.GetServices<IAbpMongoDbContext>();
var connectionStringResolver = _serviceProvider.GetRequiredService<IConnectionStringResolver>();
foreach (var dbContext in dbContexts)
{
var connectionString =
await connectionStringResolver.ResolveAsync(
ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType()));
var mongoUrl = new MongoUrl(connectionString);
var databaseName = mongoUrl.DatabaseName;
var client = new MongoClient(mongoUrl);
if (databaseName.IsNullOrWhiteSpace())
{
databaseName = ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType());
}
(dbContext as AbpMongoDbContext)?.InitializeCollections(client.GetDatabase(databaseName));
}
}
}

308
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Data/OpenIddictDataSeedContributor.cs

@ -0,0 +1,308 @@
using JetBrains.Annotations;
using Microsoft.Extensions.Localization;
using OpenIddict.Abstractions;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.OpenIddict.Applications;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
namespace MyCompanyName.MyProjectName.Server.Data;
/* Creates initial data that is needed to property run the application
* and make client-to-server communication possible.
*/
public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IConfiguration _configuration;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IStringLocalizer<OpenIddictResponse> L;
public OpenIddictDataSeedContributor(
IConfiguration configuration,
IOpenIddictApplicationManager applicationManager,
IOpenIddictScopeManager scopeManager,
IPermissionDataSeeder permissionDataSeeder,
IStringLocalizer<OpenIddictResponse> l)
{
_configuration = configuration;
_applicationManager = applicationManager;
_scopeManager = scopeManager;
_permissionDataSeeder = permissionDataSeeder;
L = l;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
await CreateScopesAsync();
await CreateApplicationsAsync();
}
private async Task CreateScopesAsync()
{
if (await _scopeManager.FindByNameAsync("MyProjectName") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "MyProjectName",
DisplayName = "MyProjectName API",
Resources =
{
"MyProjectName"
}
});
}
}
private async Task CreateApplicationsAsync()
{
var commonScopes = new List<string>
{
OpenIddictConstants.Permissions.Scopes.Address,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles,
"MyProjectName"
};
var configurationSection = _configuration.GetSection("OpenIddict:Applications");
//Blazor Client
var blazorClientId = configurationSection["MyProjectName_Blazor:ClientId"];
if (!blazorClientId.IsNullOrWhiteSpace())
{
var blazorRootUrl = configurationSection["MyProjectName_Blazor:RootUrl"]?.TrimEnd('/');
await CreateApplicationAsync(
name: blazorClientId!,
type: OpenIddictConstants.ClientTypes.Public,
consentType: OpenIddictConstants.ConsentTypes.Implicit,
displayName: "Blazor Application",
secret: null,
grantTypes: new List<string>
{
OpenIddictConstants.GrantTypes.AuthorizationCode
},
scopes: commonScopes,
redirectUri: $"{blazorRootUrl}/authentication/login-callback",
clientUri: blazorRootUrl,
postLogoutRedirectUri: $"{blazorRootUrl}/authentication/logout-callback"
);
}
// Swagger Client
var swaggerClientId = configurationSection["MyProjectName_Swagger:ClientId"];
if (!swaggerClientId.IsNullOrWhiteSpace())
{
var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"]?.TrimEnd('/');
await CreateApplicationAsync(
name: swaggerClientId!,
type: OpenIddictConstants.ClientTypes.Public,
consentType: OpenIddictConstants.ConsentTypes.Implicit,
displayName: "Swagger Application",
secret: null,
grantTypes: new List<string>
{
OpenIddictConstants.GrantTypes.AuthorizationCode,
},
scopes: commonScopes,
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html"
);
}
}
private async Task CreateApplicationAsync(
[NotNull] string name,
[NotNull] string type,
[NotNull] string consentType,
string displayName,
string? secret,
List<string> grantTypes,
List<string> scopes,
string? clientUri = null,
string? redirectUri = null,
string? postLogoutRedirectUri = null,
List<string>? permissions = null)
{
if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]);
}
if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]);
}
if (!string.IsNullOrEmpty(name) && await _applicationManager.FindByClientIdAsync(name) != null)
{
return;
//throw new BusinessException(L["TheClientIdentifierIsAlreadyTakenByAnotherApplication"]);
}
var client = await _applicationManager.FindByClientIdAsync(name);
if (client == null)
{
var application = new AbpApplicationDescriptor
{
ClientId = name,
Type = type,
ClientSecret = secret,
ConsentType = consentType,
DisplayName = displayName,
ClientUri = clientUri
};
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));
Check.NotNullOrEmpty(scopes, nameof(scopes));
if (new [] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(grantTypes.Contains))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
}
}
if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace())
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout);
}
foreach (var grantType in grantTypes)
{
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
grantType == OpenIddictConstants.GrantTypes.ClientCredentials ||
grantType == OpenIddictConstants.GrantTypes.Password ||
grantType == OpenIddictConstants.GrantTypes.RefreshToken ||
grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
}
if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit);
}
if (grantType == OpenIddictConstants.GrantTypes.Password)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password);
}
if (grantType == OpenIddictConstants.GrantTypes.RefreshToken)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken);
}
if (grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token);
}
}
}
var buildInScopes = new []
{
OpenIddictConstants.Permissions.Scopes.Address,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles
};
foreach (var scope in scopes)
{
if (buildInScopes.Contains(scope))
{
application.Permissions.Add(scope);
}
else
{
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
}
}
if (redirectUri != null)
{
if (!redirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidRedirectUri", redirectUri]);
}
if (application.RedirectUris.All(x => x != uri))
{
application.RedirectUris.Add(uri);
}
}
}
if (postLogoutRedirectUri != null)
{
if (!postLogoutRedirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]);
}
if (application.PostLogoutRedirectUris.All(x => x != uri))
{
application.PostLogoutRedirectUris.Add(uri);
}
}
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(
ClientPermissionValueProvider.ProviderName,
name,
permissions,
null
);
}
await _applicationManager.CreateAsync(application);
}
}
}

0
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Entities/.gitkeep

99
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyCompanyName.MyProjectName.Blazor.WebAssembly.Server.Mongo.csproj

@ -0,0 +1,99 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.Swashbuckle\Volo.Abp.Swashbuckle.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Serilog\Volo.Abp.AspNetCore.Serilog.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\account\src\Volo.Abp.Account.Application\Volo.Abp.Account.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\account\src\Volo.Abp.Account.HttpApi\Volo.Abp.Account.HttpApi.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\account\src\Volo.Abp.Account.Web.OpenIddict\Volo.Abp.Account.Web.OpenIddict.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.HttpApi\Volo.Abp.Identity.HttpApi.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\identity\src\Volo.Abp.Identity.MongoDB\Volo.Abp.Identity.MongoDB.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\openiddict\src\Volo.Abp.OpenIddict.MongoDB\Volo.Abp.OpenIddict.MongoDB.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\openiddict\src\Volo.Abp.PermissionManagement.Domain.OpenIddict\Volo.Abp.PermissionManagement.Domain.OpenIddict.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application\Volo.Abp.PermissionManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.MongoDB\Volo.Abp.PermissionManagement.MongoDB.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.HttpApi\Volo.Abp.PermissionManagement.HttpApi.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.Application\Volo.Abp.TenantManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.MongoDB\Volo.Abp.TenantManagement.MongoDB.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\tenant-management\src\Volo.Abp.TenantManagement.HttpApi\Volo.Abp.TenantManagement.HttpApi.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.Application\Volo.Abp.FeatureManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.MongoDB\Volo.Abp.FeatureManagement.MongoDB.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\feature-management\src\Volo.Abp.FeatureManagement.HttpApi\Volo.Abp.FeatureManagement.HttpApi.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.Application\Volo.Abp.SettingManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.MongoDB\Volo.Abp.SettingManagement.MongoDB.csproj" />
<ProjectReference Include="..\..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.HttpApi\Volo.Abp.SettingManagement.HttpApi.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\modules\audit-logging\src\Volo.Abp.AuditLogging.MongoDB\Volo.Abp.AuditLogging.MongoDB.csproj" />
</ItemGroup>
<ItemGroup>
<!-- <TEMPLATE-REMOVE> -->
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<!-- </TEMPLATE-REMOVE> -->
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.0.0-*" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\MyCompanyName.MyProjectName.Blazor.WebAssembly.Client.csproj" />
<ProjectReference Include="..\Shared\MyCompanyName.MyProjectName.Blazor.WebAssembly.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="$(UserProfile)\.nuget\packages\*\*\contentFiles\any\*\*.abppkg*.json" />
</ItemGroup>
</Project>

318
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/MyProjectNameServerModule.cs

@ -0,0 +1,318 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models;
using MyCompanyName.MyProjectName.Server.Data;
using MyCompanyName.MyProjectName.Shared.Localization;
using MyCompanyName.MyProjectName.Shared;
using MyCompanyName.MyProjectName.Shared.MultiTenancy;
using OpenIddict.Validation.AspNetCore;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.AuditLogging.MongoDB;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.FeatureManagement.MongoDB;
using Volo.Abp.Identity;
using Volo.Abp.Identity.MongoDB;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict.MongoDB;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.MongoDB;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.OpenIddict;
using Volo.Abp.SettingManagement;
using Volo.Abp.SettingManagement.MongoDB;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.MongoDB;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.Uow;
using Volo.Abp.VirtualFileSystem;
namespace MyCompanyName.MyProjectName.Server;
[DependsOn(
typeof(MyProjectNameSharedModule),
// ABP Framework packages
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpAutofacModule),
typeof(AbpAutoMapperModule),
typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreSerilogModule),
// Account module packages
typeof(AbpAccountApplicationModule),
typeof(AbpAccountHttpApiModule),
typeof(AbpAccountWebOpenIddictModule),
// Identity module packages
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityHttpApiModule),
typeof(AbpIdentityMongoDbModule),
typeof(AbpOpenIddictMongoDbModule),
// Audit logging module packages
typeof(AbpAuditLoggingMongoDbModule),
// Permission Management module packages
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpPermissionManagementHttpApiModule),
typeof(AbpPermissionManagementMongoDbModule),
// Tenant Management module packages
typeof(AbpTenantManagementApplicationModule),
typeof(AbpTenantManagementHttpApiModule),
typeof(AbpTenantManagementMongoDbModule),
// Feature Management module packages
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpFeatureManagementMongoDbModule),
typeof(AbpFeatureManagementHttpApiModule),
// Setting Management module packages
typeof(AbpSettingManagementApplicationModule),
typeof(AbpSettingManagementMongoDbModule),
typeof(AbpSettingManagementHttpApiModule)
)]
public class MyProjectNameServerModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
{
options.AddAssemblyResource(
typeof(MyProjectNameResource)
);
});
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddValidation(options =>
{
options.AddAudiences("MyProjectName");
options.UseLocalServer();
options.UseAspNetCore();
});
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
if (hostingEnvironment.IsDevelopment())
{
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
}
ConfigureAuthentication(context);
ConfigureBundles();
ConfigureMultiTenancy();
ConfigureUrls(configuration);
ConfigureAutoMapper(context);
ConfigureSwagger(context.Services, configuration);
ConfigureAutoApiControllers();
ConfigureVirtualFiles(hostingEnvironment);
ConfigureCors(context, configuration);
ConfigureDataProtection(context);
ConfigureMongoDB(context);
}
private void ConfigureAuthentication(ServiceConfigurationContext context)
{
context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
}
private void ConfigureBundles()
{
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles.Configure(
LeptonXLiteThemeBundles.Styles.Global,
bundle => { bundle.AddFiles("/global-styles.css"); }
);
});
}
private void ConfigureMultiTenancy()
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
}
private void ConfigureUrls(IConfiguration configuration)
{
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());
});
}
private void ConfigureVirtualFiles(IWebHostEnvironment hostingEnvironment)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyProjectNameServerModule>();
if (hostingEnvironment.IsDevelopment())
{
/* Using physical files in development, so we don't need to recompile on changes */
options.FileSets.ReplaceEmbeddedByPhysical<MyProjectNameServerModule>(hostingEnvironment.ContentRootPath);
}
});
}
private void ConfigureAutoApiControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(MyProjectNameServerModule).Assembly);
});
}
private void ConfigureSwagger(IServiceCollection services, IConfiguration configuration)
{
services.AddAbpSwaggerGenWithOAuth(
configuration["AuthServer:Authority"],
new Dictionary<string, string>
{
{"MyProjectName", "MyProjectName API"}
},
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "MyProjectName API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
private void ConfigureAutoMapper(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<MyProjectNameServerModule>();
Configure<AbpAutoMapperOptions>(options =>
{
/* Uncomment `validate: true` if you want to enable the Configuration Validation feature.
* See AutoMapper's documentation to learn what it is:
* https://docs.automapper.org/en/stable/Configuration-validation.html
*/
options.AddMaps<MyProjectNameServerModule>(/* validate: true */);
});
}
private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]?
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray() ?? Array.Empty<string>()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
private void ConfigureDataProtection(ServiceConfigurationContext context)
{
context.Services.AddDataProtection().SetApplicationName("MyProjectName");
}
private void ConfigureMongoDB(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<MyProjectNameDbContext>(options =>
{
options.AddDefaultRepositories();
});
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAbpRequestLocalization();
if (!env.IsDevelopment())
{
app.UseErrorPage();
}
app.UseCorrelationId();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAbpOpenIddictValidation();
if (MultiTenancyConsts.IsEnabled)
{
app.UseMultiTenancy();
}
app.UseUnitOfWork();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyProjectName API");
var configuration = context.GetConfiguration();
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthScopes("MyProjectName");
});
app.UseAuditing();
app.UseAbpSerilogEnrichers();
app.UseConfiguredEndpoints();
if (app is WebApplication webApp)
{
webApp.MapFallbackToFile("index.html");
}
}
}

11
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/ObjectMapping/MyProjectNameAutoMapperProfile.cs

@ -0,0 +1,11 @@
using AutoMapper;
namespace MyCompanyName.MyProjectName.Server.ObjectMapping;
public class MyProjectNameAutoMapperProfile : Profile
{
public MyProjectNameAutoMapperProfile()
{
/* Create your AutoMapper object mappings here */
}
}

81
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Program.cs

@ -0,0 +1,81 @@
using MyCompanyName.MyProjectName.Server;
using MyCompanyName.MyProjectName.Server.Data;
using Serilog;
using Serilog.Events;
using Volo.Abp.Data;
public class Program
{
public async static Task<int> Main(string[] args)
{
//<TEMPLATE-REMOVE IF-NOT='dbms:PostgreSQL'>
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
//</TEMPLATE-REMOVE>
var loggerConfiguration = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console());
if (IsMigrateDatabase(args))
{
loggerConfiguration.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning);
loggerConfiguration.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
}
Log.Logger = loggerConfiguration.CreateLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson()
.UseAutofac()
.UseSerilog();
await builder.AddApplicationAsync<MyProjectNameServerModule>();
if (IsMigrateDatabase(args))
{
builder.Services.AddDataMigrationEnvironment();
}
var app = builder.Build();
await app.InitializeApplicationAsync();
if (IsMigrateDatabase(args))
{
await app.Services.GetRequiredService<MyProjectNameDbMigrationService>().MigrateAsync();
return 0;
}
Log.Information("Starting MyCompanyName.MyProjectName.");
await app.RunAsync();
return 0;
}
catch (Exception ex)
{
if (ex is HostAbortedException)
{
throw;
}
Log.Fatal(ex, "MyCompanyName.MyProjectName terminated unexpectedly!");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
private static bool IsMigrateDatabase(string[] args)
{
return args.Any(x => x.Contains("--migrate-database", StringComparison.OrdinalIgnoreCase));
}
}

30
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Properties/launchSettings.json

@ -0,0 +1,30 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:44300",
"sslPort": 44300
}
},
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:44300",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

0
templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/Services/.gitkeep

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

Loading…
Cancel
Save