diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index f38f7d92dc..8a0e47c529 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -210,7 +210,7 @@ "TrialPlan": "Do you have a trial plan?", "TrialPlanExplanation": "It has a 14 days trial period for the ABP Commercial team license. For more information visit here. Furthermore, for the Team licenses we provide a 30 days money-back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.", "DoYouAcceptBankWireTransfer": "Do you accept bank wire transfers?", - "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, send your receipt and requested license type to accounting@abp.io.
Our international bank account information:", + "DoYouAcceptBankWireTransferExplanation": "Yes, we accept bank wire transfers.
After sending the license fee via bank transfer, send your receipt and requested license type to accounting@volosoft.com.
Our international bank account information:", "HowToUpgrade": "How to upgrade existing applications when a new version is available?", "HowToUpgradeExplanation1": "When you create a new application using ABP Commercial, all the modules and theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.", "HowToUpgradeExplanation2": "In addition to the standard NuGet/NPM upgrades, ABP CLI provides an update command that automatically finds and upgrades all ABP related packages in your solution.", diff --git a/docs/en/API/Application-Configuration.md b/docs/en/API/Application-Configuration.md index 8441459e02..5f2f2a9c6d 100644 --- a/docs/en/API/Application-Configuration.md +++ b/docs/en/API/Application-Configuration.md @@ -2,13 +2,14 @@ ABP Framework provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, the list of some fundamental information at this endpoint: -* [Localization](../Localization.md) values, supported and the current language of the application. -* Available and granted [policies](../Authorization.md) (permissions) for the current user. +* Granted [policies](../Authorization.md) (permissions) for the current user. * [Setting](../Settings.md) values for the current user. * Info about the [current user](../CurrentUser.md) (like id and user name). * Info about the current [tenant](../Multi-Tenancy.md) (like id and name). * [Time zone](../Timing.md) information for the current user and the [clock](../Timing.md) type of the application. +> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint. + ## HTTP API If you navigate to the `/api/abp/application-configuration` URL of an ABP Framework based web application or HTTP Service, you can access the configuration as a JSON object. This endpoint is useful to create the client of your application. @@ -19,5 +20,5 @@ For ASP.NET Core MVC (Razor Pages) applications, the same configuration values a See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI. -Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint. +Other UI types provide services native to the related platform. For example, see the [Angular UI settings documentation](../UI/Angular/Settings.md) to learn how to use the setting values exposes by this endpoint. diff --git a/docs/en/API/Application-Localization.md b/docs/en/API/Application-Localization.md new file mode 100644 index 0000000000..adf26c8fa8 --- /dev/null +++ b/docs/en/API/Application-Localization.md @@ -0,0 +1,33 @@ +# Application Localization Endpoint + +ABP Framework provides a pre-built and standard endpoint that returns all the [localization](../Localization.md) resources and texts defined in the server. + +> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint. + +## HTTP API + +`/api/abp/application-localization` is the main URL of the HTTP API that returns the localization data as a JSON string. I accepts the following query string parameters: + +* `cultureName` (required): A culture code to get the localization data, like `en` or `en-US`. +* `onlyDynamics` (optional, default: `false`): Can be set to `true` to only get the dynamically defined localization resources and texts. If your client-side application shares the same localization resources with the server (like ABP's Blazor and MVC UIs), you can set `onlyDynamics` to `true`. + +**Example request:** + +```` +/api/abp/application-localization?cultureName=en +```` + +## Script + +For [ASP.NET Core MVC (Razor Pages)](../UI/AspNetCore/Overall.md) applications, the same localization data is also available on the JavaScript side. `/Abp/ApplicationLocalizationScript` is the URL of the script that is auto-generated based on the HTTP API above. + +**Example request:** + +```` +/Abp/ApplicationLocalizationScript?cultureName=en +```` + +See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI. + +Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint. + diff --git a/docs/en/Background-Workers-Hangfire.md b/docs/en/Background-Workers-Hangfire.md index b076cf0395..5a456b1092 100644 --- a/docs/en/Background-Workers-Hangfire.md +++ b/docs/en/Background-Workers-Hangfire.md @@ -53,7 +53,7 @@ public class MyLogWorker : HangfireBackgroundWorkerBase CronExpression = Cron.Daily(); } - public override Task DoWorkAsync() + public override Task DoWorkAsync(CancellationToken cancellationToken = default) { Logger.LogInformation("Executed MyLogWorker..!"); return Task.CompletedTask; @@ -68,14 +68,7 @@ public class MyLogWorker : HangfireBackgroundWorkerBase ### UnitOfWork -For use with `UnitOfWorkAttribute`, you need to define an interface for worker: - ```csharp -public interface IMyLogWorker : IHangfireBackgroundWorker -{ -} - -[ExposeServices(typeof(IMyLogWorker))] public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker { public MyLogWorker() @@ -84,11 +77,13 @@ public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker CronExpression = Cron.Daily(); } - [UnitOfWork] - public override Task DoWorkAsync() + public override Task DoWorkAsync(CancellationToken cancellationToken = default) { - Logger.LogInformation("Executed MyLogWorker..!"); - return Task.CompletedTask; + using (var uow = LazyServiceProvider.LazyGetRequiredService().Begin()) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } } } ``` @@ -105,9 +100,6 @@ public class MyModule : AbpModule ApplicationInitializationContext context) { await context.AddBackgroundWorkerAsync(); - - //If the interface is defined - //await context.AddBackgroundWorkerAsync(); } } ```` diff --git a/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png b/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png new file mode 100644 index 0000000000..c8a5516aa6 Binary files /dev/null and b/docs/en/Community-Articles/2022-11-25-JSON-columns/Database.png differ diff --git a/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md b/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md new file mode 100644 index 0000000000..4f69b4eb1f --- /dev/null +++ b/docs/en/Community-Articles/2022-11-25-JSON-columns/post.md @@ -0,0 +1,134 @@ +# JSON Columns in Entity Framework Core 7 + +In this article, we will see how to use the new **JSON Columns** features that came with EF Core 7 in an ABP based application (with examples). + +## JSON Columns + +Most relational databases support columns that contain JSON documents. The JSON in these columns can be drilled into with queries. This allows, for example, filtering and sorting by the elements of the documents, as well as projection of elements out of the documents into results. JSON columns allow relational databases to take on some of the characteristics of document databases, creating a useful hybrid between these two database management approaches. + +EF7 contains provider-agnostic support for JSON columns, with an implementation for SQL Server. This support allows the mapping of aggregates built from .NET types to JSON documents. Normal LINQ queries can be used on the aggregates, and these will be translated to the appropriate query constructs needed to drill into the JSON. EF7 also supports updating and saving changes to JSON documents. + +> You can find more information about JSON columns in EF Core's [documentation](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns). + +### Mapping JSON Columns + +In EF Core, aggregate types can be defined using `OwnsOne` and `OwnsMany` methods. `OwnsOne` can be used to map a single aggregate and the `OwnsMany` method can be used to map a collection of aggregates. + +With EF 7, we have a new extension method for mapping property to a JSON Column: `ToJson`. We can use this method to mark a property as a JSON Column. The property can be of any type that can be serialized to JSON. + +The following example shows how to map a JSON column to an aggregate type: + +```csharp +public class ContactDetails +{ + public Address Address { get; set; } + public string? Phone { get; set; } +} + +public class Address +{ + public Address(string street, string city, string postcode, string country) + { + Street = street; + City = city; + Postcode = postcode; + Country = country; + } + + public string Street { get; set; } + public string City { get; set; } + public string Postcode { get; set; } + public string Country { get; set; } +} + +public class Person : AggregateRoot +{ + public string Name { get; set; } = null!; + public ContactDetails ContactDetails { get; set; } = null!; +} +``` + +* Above, we have defined an aggregate type `ContactDetails` that contains an `Address` and a `Phone` number. The aggregate type is configured in `OnModelCreating` using `OwnsOne` and `ToJson` methods below. +* The `Address` property is mapped to a JSON column using `ToJson`, and the `Phone` property is mapped to a regular column. This requires just one call to **ToJson()** when configuring the aggregate type: + +```csharp + +public class MyDbContext : AbpDbContext +{ + public DbSet Persons { get; set; } + + public MyDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity(b => + { + b.ToTable(MyProjectConsts.DbTablePrefix + "Persons", MyProjectConsts.DbSchema); + b.ConfigureByConvention(); + b.OwnsOne(x=>x.ContactDetails, c => + { + c.ToJson(); //mark as JSON Column + c.OwnsOne(cd => cd.Address); + }); + }); + } +} +``` + +### Querying JSON Columns + +Queries into JSON columns work just the same as querying into any other aggregate type in EF Core. That's it, just use the LINQ! Here are some examples: + +```csharp +var persons = await (await GetDbSetAsync()).ToListAsync(); + +var contacts = await (await GetDbSetAsync()).Select(person => new +{ + person, + person.ContactDetails.Phone, //query over JSON column + Addresses = person.ContactDetails.Address //query over JSON column +}).ToListAsync(); + +var addresses = await (await GetDbSetAsync()).Select(person => new +{ + person, + Addresses = person.ContactDetails.Address //query over JSON column +}).ToListAsync(); +``` + +### Updating JSON Columns + +You can update JSON columns the same as updating any record by using the `UpdateAsync` method. The following example shows how to update a JSON column: + +```csharp +var person = await (await GetDbSetAsync()).FirstAsync(); + +person.ContactDetails.Phone = "123456789"; +person.ContactDetails.Address = new Address("Street", "City", "Postcode", "Country"); +await UpdateAsync(person, true); +``` + +### JSON Column in a Database + +After you've configured the database relations, created a new migration and applied it to database you will have a database table like below: + +![image](./Database.png) + +As you can see, thanks to JSON Columns feature the **ContactDetails** row has JSON content and we can use it in a query or update it from our application with the LINQ JSON query support that mentioned above. + +### Conclusion + +In this article, I've briefly introduced the JSON Columns feature that was shipped with EF Core 7. It's pretty straightforward to use JSON Columns in an ABP based application. You can see the examples above and give it a try! + +### The Source Code +* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreJSONColumnDemo). + +### References + +* [https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#json-columns) +* [https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities](https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities) \ No newline at end of file diff --git a/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md b/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md new file mode 100644 index 0000000000..70bcb15e38 --- /dev/null +++ b/docs/en/Community-Articles/2022-11-27-gRPC-Health-Checks/POST.md @@ -0,0 +1,73 @@ +# gRPC - Health Checks + +In this article we will show how to use gRPC health checks with the ABP Framework. + +## Health Checks + +ASP.NET Core 7 supports gRPC health checks. Health Checks allow us to determine the overall health and availability of our application infrastructure. They are exposed as HTTP endpoints and can be configured to provide information for various monitoring scenarios, such as the response time and memory usage of our application, or whether our application can communicate with our database provider. + +### gRPC Health Checks + +The [gRPC health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) is a standard for reporting the health of gRPC server apps. An app exposes health checks as a gRPC service. They are typically used with an external monitoring service to check the status of an app. + +### Grpc.AspNetCore.HealthChecks + +ASP.NET Core supports the gRPC health checking protocol with the [Grpc.AspNetCore.HealthChecks](https://www.nuget.org/packages/Grpc.AspNetCore.HealthChecks) package. Results from .NET health checks are reported to callers. + +## Using gRPC Health Checks with the ABP Framework + +In this article, I'm assuming you've used gRPC with ABP before. If you are still having problems with this, it may be good for you to review this article. +https://community.abp.io/posts/using-grpc-with-the-abp-framework-2dgaxzw3 + +### Set up gRPC Health Checks + +In this solution, `*.HttpApi.Host` is the project that configures and runs the server-side application. So, we will make changes in that project. + +* Add the `Grpc.AspNetCore.HealthChecks` package to your project. + +```bash +dotnet add package Grpc.AspNetCore.HealthChecks +``` + +* `AddGrpcHealthChecks` to register services that enable health checks. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + // Other configurations... + + context.Services.AddGrpcHealthChecks() + .AddCheck("SampleHealthCheck", () => HealthCheckResult.Healthy()); +} +``` +* `MapGrpcHealthChecksService` to add a health check service endpoint. + +```csharp +public override void OnApplicationInitialization(ApplicationInitializationContext context) +{ + // Other middlewares... + + app.UseConfiguredEndpoints(builder => + { + builder.MapGrpcHealthChecksService(); + }); +} +``` + +### Calling Health Checks From a Client + +Now that our server is configured for gRPC health checks, we can test it by creating a basic console client. + +```csharp +var channel = GrpcChannel.ForAddress("https://localhost:44357"); +var client = new Health.HealthClient(channel); + +var response = await client.CheckAsync(new HealthCheckRequest()); +var status = response.Status; + +Console.WriteLine($"Health Status: {status}"); +``` + +## References + +- https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#grpc-health-checks-in-aspnet-core diff --git a/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md b/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md new file mode 100644 index 0000000000..8ed92d43c2 --- /dev/null +++ b/docs/en/Community-Articles/2022-11-28-EF-Core-Entity-Dependency-Injection/POST.md @@ -0,0 +1,381 @@ +# Injecting Service Dependencies to Entities with Entity Framework Core 7.0 + +[Dependency injection](https://docs.abp.io/en/abp/latest/Dependency-Injection) is a widely-used pattern of obtaining references to other services from our classes. It is a built-in feature when you develop ASP.NET Core applications. In this article, I will explain why we may need to have references to other services in an entity class and how we can implement Entity Framework Core's new `IMaterializationInterceptor` interface to provide these services to the entities using the standard dependency injection system. + +> You can find the source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreEntityDependencyInjectionDemo). + +## The Problem + +While developing applications based on [Domain-Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) (DDD) patterns, we typically write our business code inside [application services](https://docs.abp.io/en/abp/latest/Application-Services), [domain services](https://docs.abp.io/en/abp/latest/Domain-Services) and [entities](https://docs.abp.io/en/abp/latest/Entities). Since the application and domain service instances are created by the dependency injection system, they can inject services into their constructors. + +Here, an example domain service that injects a repository into its constructor: + +````csharp +public class ProductManager : DomainService +{ + private readonly IRepository _productRepository; + + public ProductManager(IRepository productRepository) + { + _productRepository = productRepository; + } + + //... +} +```` + +`ProductManager` can then use the `_productRepository` object in its methods to perform its business logic. In the following example, `ChangeCodeAsync` method is used to change a product's code (the `ProductCode` property) by ensuring uniqueness of product codes in the system: + +````csharp +public class ProductManager : DomainService +{ + private readonly IRepository _productRepository; + + public ProductManager(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task ChangeCodeAsync(Product product, string newProductCode) + { + Check.NotNull(product, nameof(product)); + Check.NotNullOrWhiteSpace(newProductCode, nameof(newProductCode)); + + if (product.ProductCode == newProductCode) + { + return; + } + + if (await _productRepository.AnyAsync(x => x.ProductCode == newProductCode)) + { + throw new ApplicationException( + "Product code is already used: " + newProductCode); + } + + product.ProductCode = newProductCode; + } +} +```` + +Here, the `ProductManager` forces the rule "product code must be unique". Let's see the `Product` entity class too: + +````csharp +public class Product : AuditedAggregateRoot +{ + public string ProductCode { get; internal set; } + + public string Name { get; private set; } + + private Product() + { + /* This constructor is used by EF Core while + getting the Product from database */ + } + + /* Primary constructor that should be used in the application code */ + public Product(string productCode, string name) + { + ProductCode = Check.NotNullOrWhiteSpace(productCode, nameof(productCode)); + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } +} +```` + +You see that the `ProductCode` property's setter is `internal`, which makes possible to set it from the `ProductManager` class as shown before. + +This design has a problem: We had to make the `ProductCode` setter `internal`. Now, any developer may forget to use the `ProductManager.ChangeCodeAsync` method, and can directly set the `ProductCode` on the entity. So, we can't completely force the "product code must be unique" rule. + +It would be better to move the `ChangeCodeAsync` method into the `Product` class and make the `ProductCode` property's setter `private`: + +````csharp +public class Product : AuditedAggregateRoot +{ + public string ProductCode { get; private set; } + + public string Name { get; private set; } + + // ... + + public async Task ChangeCodeAsync(string newProductCode) + { + Check.NotNullOrWhiteSpace(newProductCode, nameof(newProductCode)); + + if (newProductCode == ProductCode) + { + return; + } + + /* ??? HOW TO INJECT THE PRODUCT REPOSITORY HERE ??? */ + if (await _productRepository.AnyAsync(x => x.ProductCode == newProductCode)) + { + throw new ApplicationException("Product code is already used: " + newProductCode); + } + + ProductCode = newProductCode; + } +} +```` + +With that design, there is no way to set the `ProductCode` without applying the rule "product code must be unique". Great! But we have a problem: An entity class can not inject dependencies into its constructor, because an entity is not created using the dependency injection system. There are two common points of creating an entity: + +* We can create an entity in our application code, using the standard `new` keyword, like `var product = new Product(...);`. +* Entity Framework (and any other ORM / database provider) creates entities after getting them from the database. They typically use the empty (default) constructor of the entity to create it, then sets the properties coming from the database query. + +So, how we can use the product repository in the `Product.ChangeCodeAsync` method? If we forget the dependency injection system, we would think to add the repository as a parameter to the `ChangeCodeAsync` method and delegate the responsibility of obtaining the service reference to the caller of that method: + +````csharp +public async Task ChangeCodeAsync( + IRepository productRepository, string newProductCode) +{ + Check.NotNull(productRepository, nameof(productRepository)); + Check.NotNullOrWhiteSpace(newProductCode, nameof(newProductCode)); + + if (newProductCode == ProductCode) + { + return; + } + + if (await productRepository.AnyAsync(x => x.ProductCode == newProductCode)) + { + throw new ApplicationException( + "Product code is already used: " + newProductCode); + } + + ProductCode = newProductCode; +} +```` + +However, that design would make hard to use the `ChangeCodeAsync` method, and also exposes its internal dependencies to outside. If we need another dependency in the `ChangeCodeAsync` method later, we should add another parameter, which will effect all the application code that uses the `ChangeCodeAsync` method. I think that's not reasonable. The next section offers a better and a more generic solution to the problem. + +## The Solution + +First of all, we can introduce an interface that should be implemented by the entity classes which needs to use services in their methods: + +````csharp +public interface IInjectServiceProvider +{ + ICachedServiceProvider ServiceProvider { get; set; } +} +```` + +`ICachedServiceProvider` is a service that is provided by the ABP Framework. It extends the standard `IServiceProvider`, but caches the resolved services. Basically, it internally resolves a service only a single time, even if you resolve the service from it multiple times. The `ICachedServiceProvider` service itself is a scoped service, means it is created only once in a scope. We can use it to optimize the service resolution, however, the standard `IServiceProvider` would work as expected. + +Next, we can implement the `IInjectServiceProvider` for our `Product` entity: + +````csharp +public class Product : AuditedAggregateRoot, IInjectServiceProvider +{ + public ICachedServiceProvider ServiceProvider { get; set; } + + //... +} +```` + +I will explain how to set the `ServiceProvider` property later, but first see how to use it in our `Product.ChangeCodeAsync` method. Here, the final `Product` class: + +````csharp +public class Product : AuditedAggregateRoot, IInjectServiceProvider +{ + public string ProductCode { get; internal set; } + + public string Name { get; private set; } + + public ICachedServiceProvider ServiceProvider { get; set; } + + private Product() + { + /* This constructor is used by EF Core while + getting the Product from database */ + } + + /* Primary constructor that should be used in the application code */ + public Product(string productCode, string name) + { + ProductCode = Check.NotNullOrWhiteSpace(productCode, nameof(productCode)); + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + } + + public async Task ChangeCodeAsync(string newProductCode) + { + Check.NotNullOrWhiteSpace(newProductCode, nameof(newProductCode)); + + if (newProductCode == ProductCode) + { + return; + } + + var productRepository = ServiceProvider + .GetRequiredService>(); + + if (await productRepository.AnyAsync(x => x.ProductCode == newProductCode)) + { + throw new ApplicationException("Product code is already used: " + newProductCode); + } + + ProductCode = newProductCode; + } +} +```` + +The `ChangeCodeAsync` method gets the product repository from the `ServiceProvider` and uses it to check if there is another product with the given `newProductCode` value. + +Now, let's explain how to set the `ServiceProvider` value... + +### Entity Framework Core Configuration + +Entity Framework 7.0 introduces the `IMaterializationInterceptor` interceptor that allows us to manipulate an entity object just after the entity object is created as a result of database query. + +We can write the following interceptor that sets the `ServiceProvider` property of an entity, if it implements the `IInjectServiceProvider` interface: + +````csharp +public class ServiceProviderInterceptor : IMaterializationInterceptor +{ + public object InitializedInstance( + MaterializationInterceptionData materializationData, + object instance) + { + if (instance is IInjectServiceProvider entity) + { + entity.ServiceProvider = materializationData + .Context + .GetService(); + } + + return instance; + } +} +```` + +> Lifetime of the resolved services are tied to the lifetime of the related `DbContext` instance. So, you don't need to care if the resolved dependencies are disposed. ABP's [unit of work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) system already disposes the `DbContext` instance when the unit of work is completed. + +Once we defined such an interceptor, we should configure our `DbContext` class to use it. You can do it by overriding the `OnConfiguring` method in your `DbContext` class: + +````csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +{ + base.OnConfiguring(optionsBuilder); + optionsBuilder.AddInterceptors(new ServiceProviderInterceptor()); +} +```` + +Finally, you should ignore the `ServiceProvider` property in your entity mapping configuration in your `DbContext` (because we don't want to map it to a database table field): + +````csharp +protected override void OnModelCreating(ModelBuilder builder) +{ + base.OnModelCreating(builder); + // ... + builder.Entity(b => + { + // ... + /* We should ignore the ServiceProvider on mapping! */ + b.Ignore(x => x.ServiceProvider); + }); +} +```` + +That's all. From now, EF Core will set the `ServiceProvider` property for you. + +### Manually Creating Entities + +While EF Core seamlessly set the `ServiceProvider` property while getting entities from database, you should still set it manually while creating new entities yourself. + +**Example: Set `ServiceProvider` property while creating a new Product entity:** + +````csharp +public async Task CreateAsync(CreateProductDto input) +{ + var product = new Product(input.ProductCode, input.Name) + { + ServiceProvider = _cachedServiceProvider + }; + + await _productRepository.InsertAsync(product); +} +```` + +Here, you may think that it is not necessary to set the `ServiceProvider`, because we haven't used the `ChangeCodeAsync` method. You are definitely right; It is not needed in this example, because it is clear to see the entity object is not used between the entity creation and saving it to the database. However, if you call a method of the entity, or pass it to another service before inserting into the database, you may not know if the `ServiceProvider` will be needed. So, you should carefully use it. + +Basically, I've introduced the problem and the solution. In the next section, I will explain some limitations of that design and some of my other thoughts. + +## Discussions + +In this section, I will first discuss a slightly different way of obtaining services. Then I will explain limitations and problems of injecting services into entities. + +### Why injected a service provider, but not the services? + +As an obvious question, you may ask why we've property-injected a service provider object, then resolved the services manually. Can't we directly property-inject our dependencies? + +**Example: Property-inject the `IRepository` service:** + +````csharp +public class Product : AuditedAggregateRoot +{ + // ... + + public IRepository ProductRepository { get; set; } + + public async Task ChangeCodeAsync(string newProductCode) + { + Check.NotNullOrWhiteSpace(newProductCode, nameof(newProductCode)); + + if (newProductCode == ProductCode) + { + return; + } + + if (await ProductRepository.AnyAsync(x => x.ProductCode == newProductCode)) + { + throw new ApplicationException("Product code is already used: " + newProductCode); + } + + ProductCode = newProductCode; + } +} +```` + +Now, we don't need to implement the `IInjectServiceProvider` interface and manually resolve the `IRepository` object from the `ServiceProvider`. You see that the `ChangeCodeAsync` method is much simpler now. + +So, how to set `ProductRepository`? For the EF Core interceptor part, you can somehow get all public properties of the entity via reflection. Then, for each property, check if such a service does exist, and set it from the dependency injection system if available. Surely, that will be less performant, but will work if you can truly implement. On the other hand, it would be extra hard to set all the dependencies of the entity while manually creating it using the `new` keyword. So, personally I wouldn't recommend that approach. + +### Limitations + +One important limitation is that you can not use the services inside your entity's constructor code. Ideally, the constructor of the `Product` class should check if the product code is already used before. See the following constructor: + +````csharp +public Product(string productCode, string name) +{ + ProductCode = Check.NotNullOrWhiteSpace(productCode, nameof(productCode)); + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + + /* Can not check if product code is already used by another product? */ +} +```` + +It is not possible to use the product repository here, because; + +1. The services are property-injected. That means they will be set after the object creation has completed. +2. Even if the service is available, it won't be truly possible to call async code in a constructor. You know constructors can not be async in C#, but the repository and other service methods are generally designed as async. + +So, if you want to force the "product code must be unique" rule, you should create an async domain service method (like `ProductManager.CreateAsync(...)`) and always use it to create products (you can make the `Product` class constructor `internal` to not allow to use it in the application layer). + +### Design Problems + +Beside the technical limitations, coupling your entities to external services is generally considered as a bad design. It makes your entities over-complicated, hard to test, and generally leads to take too much responsibility over the time. + +## Conclusion + +In this article, I tried to investigate all aspects of injecting services into entity classes. I explained how to use Entity Framework 7.0 `IMaterializationInterceptor` to implement property-injection pattern while getting entities from database. + +Injecting services into entities seems a certain way of forcing some business rules in your entities. However, because of the current technical limitations, design issues and usage difficulties, I don't suggest to depend on services in your entities. Instead, create domain services when you need to implement a business rule that depends on external services and entities. + +## The Source Code + +* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreEntityDependencyInjectionDemo). +* You can see [this pull request](https://github.com/abpframework/abp-samples/pull/207/files) for the changes I've done after creating the application. + +## See Also + +* [What's new in EF Core 7.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew) +* [ABP Framework: Dependency Injection](https://docs.abp.io/en/abp/latest/Dependency-Injection) +* [ABP Framework: Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) \ No newline at end of file diff --git a/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md b/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md new file mode 100644 index 0000000000..998b1e9735 --- /dev/null +++ b/docs/en/Community-Articles/2022-11-28-Model-building-conventions-ef7/POST.md @@ -0,0 +1,88 @@ +# Model building conventions in Entity Framework Core 7.0 + +In this article, I will show you one of the new features of EF Core 7 named "Model building conventions". + +Entity Framework Core uses a metadata model to describe how entity types are mapped to the database. Before EF Core 7.0, it was not possible to remove or replace existing conventions or add new conventions. With EF Core 7.0, this is now possible. To read more about it, you can visit its [documentation](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#model-building-conventions). + +EF Core uses many built-in conventions. You can see the full list of the conventions on `IConvetion` Interface API [documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.metadata.conventions.iconvention?view=efcore-7.0). + +If you want to add, remove or replace a convention, you need to override `ConfigureConventions` method of your DbContext as shown below; + +```csharp +protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) +{ + configurationBuilder.Conventions.Add(_ => new MyCustomConvention()); +} +``` + +## Allowed Operations + +### Removing an existing convention + +Existing conventions provided by EF Core are well thought and useful, but sometimes some of them might not be a good candidate for your application. In such cases, you can remove an existing convention as shown below; + +```csharp +protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) +{ + configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention)); +} +``` + +### Adding a new convention + +Just like removing a convention, we can add a completely new convention as well. You can define many different conventions here. For example, you can specify a standard precision for all decimal fields in your entities. + +```csharp +public class DecimalPrecisionConvention : IModelFinalizingConvention + { + public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext context) + { + foreach (var property in modelBuilder.Metadata.GetEntityTypes() + .SelectMany( + entityType => entityType.GetDeclaredProperties() + .Where( + property => property.ClrType == typeof(decimal)))) + { + property.Builder.HasPrecision(2); + } + } + } +``` + +Note that, conventions are executed in the order they are added. So you need to be careful in which order they are added. + +### Replacing an existing convention + +Sometimes, a default convention might work slightly different than what your app expects. In such cases, you can create your own implementation by inheriting from that convention and replace the default one. For example, you can create a convention as shown below; + +```csharp +public class MyCustomConvention : ADefaultEfCoreConvention +{ + public MyCustomConvention(ProviderConventionSetBuilderDependencies dependencies) + : base(dependencies) + { + } + // override the methods you want to change. +} +``` + +Then, you can replace the default one with your implementation as shown below; + +```csharp +protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) +{ + configurationBuilder.Conventions.Replace( + serviceProvider => new MyCustomConvention( + serviceProvider.GetRequiredService())); +} +``` + +As a final note, conventions never override configuration marked as **DataAnnotation** or **Explicit**. This means that, even if there is a convention, if the property has a `DataAnnotation` attribute or configuration in `OnModelCreating`, convetion will not be used. Here are the configuration types EF Core uses; + +* **Explicit:** The model element was explicitly configured in OnModelCreating +* **DataAnnotation:** The model element was configured using a mapping attribute (aka data annotation) on the CLR type +* **Convention:** The model element was configured by a model building convention + +## Using in ABP-based solution + +Since ABP uses EF Core, you can use this feature in ABP as well. \ No newline at end of file diff --git a/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md new file mode 100644 index 0000000000..ac517506ec --- /dev/null +++ b/docs/en/Community-Articles/2022-11-28-ef-bulk-operations/POST.md @@ -0,0 +1,71 @@ +# Bulk Operations with Entity Framework Core 7.0 +Entity Framework tracks all the entity changes and applies those changes to the database one by one when the `SaveChanges()` method is called. There was no way to execute bulk operations in Entity Framework Core without a dependency. + +As you know the [Entity Framework Extensions](https://entityframework-extensions.net/bulk-savechanges) library was doing it but it was not free. + +There was no other solution until now. [Bulk Operations](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) are now available in Entity Framework Core with .NET 7. + +With .NET 7, there are two new methods such as `ExecuteUpdate` and `ExecuteDelete` available to execute bulk operations. It's a similar usage with the Entity Framework Core Extensions library if you're familiar with it. + +You can visit the microsoft example [here](https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates) about how to use it. + + +It can be easily used with the DbContext. + +```csharp +await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync(); +``` + +## Using with ABP Framework +ABP Framework provides an abstraction over database operations and implements generic repository pattern. So, DbContext can't be accessed outside of [repositories](https://docs.abp.io/en/abp/latest/Repositories). + +You can use the `ExecuteUpdate` and `ExecuteDelete` methods inside a repository. + +```csharp +public class BookEntityFrameworkCoreRepository : EfCoreRepository, IBookRepository +{ + public BookEntityFrameworkCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task UpdateListingAsync() + { + var dbSet = await GetDbSetAsync(); + + await dbSet + .Where(x => x.IsListed && x.PublishedOn.Year <= 2022) + .ExecuteUpdateAsync(s => s.SetProperty(x => x.IsListed, x => false)); + } + + public async Task DeleteOldBooksAsync() + { + var dbSet = await GetDbSetAsync(); + + await dbSet + .Where(x => x.PublishedOn.Year <= 2000) + .ExecuteDeleteAsync(); + } +} +``` + +There is no need to take an action for bulk inserting. You can use the `InsertManyAsync` method of the repository instead of creating a new method for it if you don't have custom logic. It'll use a new bulk inserting feature automatically since it's available in EF Core 7.0. + +```csharp +public class MyDomainService : DomainService +{ + protected IRepository BookRepository { get; } + + public MyDomainService(IRepository bookRepository) + { + BookRepository = bookRepository; + } + + public async Task CreateBooksAsync(List books) + { + // It'll use bulk inserting automatically. + await BookRepository.InsertManyAsync(books); + } +} +``` + +> If you use `ExecuteDeleteAsync` or `ExecuteUpdateAsync`, then ABP's soft delete and auditing features can not work. Because these ABP features work with EF Core's change tracking system and these new methods doesn't work with the change tracking system. So, use them carefully. diff --git a/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md b/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md new file mode 100644 index 0000000000..53903e1ccc --- /dev/null +++ b/docs/en/Community-Articles/2022-11-29-EF-Core-Guarded-Types/POST.md @@ -0,0 +1,208 @@ +# Value generation for DDD guarded types with Entity Framework Core 7.0 + +In domain-driven design (DDD), *guarded keys* can improve the type safety of key properties. This is achieved by wrapping the key type in another type which is specific to the use of the key. In this article, I will explain the cases why you may need to use guarded types and discuss the advantages and limitations when implementing to an ABP application. + +> You can find the source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo). + +## The Problem + +While developing an applications, there are many cases where we manually assign foreign keys that can be in guid type or integer type, etc. This manual assignment mistakes can cause miss-match of unique identifiers, such as **assigning a product ID to a category**, that can be hard to detect in the future. + +Here is a very simplified sample of wrong assignment when trying to update a product category: + +````csharp +public class ProductAppService : MyProductStoreAppService, IProductAppService +{ + private readonly IRepository _productRepository; + + public ProductAppService(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task UpdateProductCategoryAsync(Guid productId, Guid categoryId) + { + var productToUpdate = await _productRepository.GetAsync(productId); + productToUpdate.CategoryId = productId; // Wrong assignment that causes error only at run-time + + await _productRepository.UpdateAsync(productToUpdate); + } +} +```` + +While the sample demonstrates a very simple mistake, it is easier to come across similar mistakes when the business logic gets more complex especially when you are using methods with **multiple foreign key arguments**. The next section offers using guarded types to prevent these kind of problems as a solution to the problem. + +## The Solution + +Strongly-typed IDs (*guarded keys*) is a DDD approach to address this problem. One of the main problems with .NET users was handling the persisting these objects. With EFCore7, key properties can be guarded with type safety seamlessly. + +To use guarded keys, update your aggregate root or entity unique identifier with a complex type to overcome *primitive obsession*: + +````csharp +public readonly struct CategoryId +{ + public CategoryId(Guid value) => Value = value; + public Guid Value { get; } +} + +public readonly struct ProductId +{ + public ProductId(Guid value) => Value = value; + public Guid Value { get; } +} +```` + +You can now use these keys for your aggregate roots or entities: + +```csharp +public class Product : AggregateRoot +{ + public ProductId Id { get; set; } + public string Name { get; set; } + public CategoryId CategoryId { get; set; } + + private Product() { } + + public Product(ProductId id, string name) : base(id) + { + Name = Check.NotNullOrEmpty(name, nameof(name)); + } +} + +public class Category : AggregateRoot +{ + public CategoryId Id { get; set; } + public string Name { get; set; } + public List Products { get; } = new(); + + private Category() { } + + public Category(CategoryId id, string name) : base(id) + { + Name = Check.NotNullOrEmpty(name, nameof(name)); + } +} +``` + +`ProductId` and `CategoryId` guarded key types shown in the sample use `Guid` key values, which means Guid values will be used in the mapped database tables. This is achieved by defining [value converters](https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions) for the types. + +Override the `ConfigureConventions` method of your DbContext to use the value converters: + +````csharp +protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) +{ + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); +} + +private class ProductIdConverter : ValueConverter +{ + public ProductIdConverter() + : base(v => v.Value, v => new(v)) + { + } +} + +private class CategoryIdConverter : ValueConverter +{ + public CategoryIdConverter() + : base(v => v.Value, v => new(v)) + { + } +} +```` + +> The code here uses `struct` types. This means they have appropriate value-type semantics for use as keys. If `class` types are used instead, then they need to either override equality semantics or also specify a [value comparer](https://learn.microsoft.com/en-us/ef/core/modeling/value-comparers). + +Now, you can use generic (or custom) repositories of ABP using the guarded type as the key for the repository: + +```csharp +public class ProductStoreDataSeedContributor : IDataSeedContributor, ITransientDependency +{ + private readonly IRepository _categoryRepository; + private readonly IRepository _productRepository; + + public ProductStoreDataSeedContributor( + IRepository categoryRepository, + IRepository productRepository + ) + { + _categoryRepository = categoryRepository; + _productRepository = productRepository; + } + + // ... +} +``` + +You can also use `integer` as guarded type for your key properties and use [Sequence-based key generation for SQL Server](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#sequence-based-key-generation-for-sql-server) for value generation. + +## Discussions + +In this section, I will discuss the use cases of guarded types and limitations when implementing to an ABP application. + +### Do I need to use guarded types? + +If you are already following the best practices of [Domain-Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design), you are aware that **updates** and **creations** of an aggregate are done **over** the aggregate root itself as a whole unit. And entity state changes of an aggregate root should be done using the [domain services](https://docs.abp.io/en/abp/latest/Domain-Services). Domain services should already validate the entity. + +**Example: Using domain service to update product:** + +````csharp +public class ProductManager : DomainService +{ + private readonly IRepository _productRepository; + + public ProductManager(IRepository productRepository) + { + _productRepository = productRepository; + } + + public Task AssignCategory(Product product, Category category) + { + // ... + + product.CategoryId = category.Id; + + //.. + } +} +```` + +In this sample, domain service validates that both **product** and the **category** entities, passed by the application layer, are valid objects since they are not key properties. However, manual assignment is already in place and more complex the domain logic, higher to miss out mistakes. At the end, it will depend on your tolerance level for developer errors comparing to the time you want to spend time on additional code base for guarded types. + +### Limitations + +One important limitation is automatic value generation when using `Guid` as guarded type for your key properties. The basic repository can not generate the unique identifier automatically by the time this article is written: + +```csharp +public readonly struct ProductId +{ + public ProductId(Guid value) => Value = value; + public Guid Value { get; } +} +``` + +you need to generate the unique identifier **manually**: + +````csharp +var newProduct = await _productRepository.InsertAsync( + new Product(new ProductId(_guidGenerator.Create()), "New product") +); +```` + +## Conclusion + +In this article, I tried to explain DDD guarded types for key properties and value generation for these properties using Entity Framework 7.0 and ABP. + +Using strongly-typed key properties reduce the chance of unnoticed errors. Admittedly it increases the code complexity by adding new types to your solution with extra coding, especially if you are using classes as keys. Guarded types provide improved safety for your code base at the expense of additional code complexity as in many DDD concepts and patterns. + +If you have a large team working on a large scale solution containing complex business logics where key assignments are abundant or if you are using methods with multiple foreign key arguments, I personally suggest using guarded types. + +## The Source Code + +* You can find the full source code of the example application [here](https://github.com/abpframework/abp-samples/tree/master/EfCoreGuardedTypeDemo). + +## See Also + +* [What's new in EF Core 7.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew) +* [ABP Framework: Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) \ No newline at end of file diff --git a/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md new file mode 100644 index 0000000000..d26f6c4aaa --- /dev/null +++ b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/Post.md @@ -0,0 +1,231 @@ +# Rate Limiting with ASP.NET Core 7.0 + +Rate limiting is a way of controlling the traffic that a web application or API receives. In other words, rate limiting helps you control the amount of traffic each user has access to at any given time. This is extremely useful when you want to manage the load on your server or services, avoid going over your monthly data transfer limit and allow the system to continue to function and meet service level agreements, even when an increase in demand places an extreme load on resources. + +In this article, we will look at what rate limiting is, why we need to use it, how the different rate limiting algorithms provided with .NET 7.0 work, and best practices for using rate limiting in your application. + +## What is rate limiting? + +Whether accidental or intentional, users may exhaust resources in a way that impacts others. When a number of requests are received on to resources for a long time, the server can run out of those resources. These resources can include memory, threads, connections, or anything else that is limited. To avoid this situation, set rate limiters. Rate limiters control the consumption of resources used by an instance of an application, a user, an individual tenant, or an entire service. + +## Why do you need to use rate limiting? + +A rate limiting system is crucial in any application where you have to control or throttle user requests or traffic. This is especially true in applications running on a cloud hosting platform because the user’s traffic can affect the whole server where the application is hosted. + +Why do you need to implement rate limiting? Here are a few reasons: + +- To ensure that a system continues to meet service level agreements (SLA). +- To prevent a single user, tenant, service, or so on from monopolizing the resources provided by an application. +- To help cost-optimize a system by limiting the maximum resource levels needed to keep it functioning. + +## Rate limiter algorithms + +The [`RateLimiterOptionsExtensions`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.ratelimiting.ratelimiteroptionsextensions) class provides the following extension methods for rate limiting: + +- **[Fixed window](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#fixed-window-limit)**: Fixed-window limits—such as 3,000 requests per hour or 10 requests per day—are easy to state, but they are subject to spikes at the edges of the window, as available quota resets. Consider, for example, a limit of 3,000 requests per hour, which still allows for a spike of all 3,000 requests to be made in the first minute of the hour, which might overwhelm the service. +- [**Sliding window**:](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#sliding-window-limit) Sliding windows have the benefits of a fixed window, but the rolling window of time smoothes out bursts. Systems such as Redis facilitate this technique with expiring keys. +- [**Token bucket**](https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet/#token-bucket-limit): A token bucket maintains a rolling and accumulating budget of usage as a balance of tokens. A token bucket adds tokens at some rate. When a service request is made, the service attempts to withdraw a token (decrementing the token count) to fulfill the request. If there are no tokens in the bucket, the service has reached its limit and responds with backpressure. +- [**Concurrency**](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#concurrency-limiter): A concurrency limiter is the simplest form of rate limiting. It doesn’t look at time, just at number of concurrent requests. + +In order to be a more realistic example, instead of making an example with each rate limiter algorithm, we will implement the following three algorithms in an **ABP-based** application. + +1. We will add a `SlidingWindowLimiter` with a partition for all anonymous users. +2. We will add a `TokenBucketRateLimiter` with a partition for each authenticated user. +3. We will add a `ConcurrencyLimiter` with a partition for each Tenant. + +**Note:** The following sample isn't meant for production code but is an example of how to use the limiters in ABP-based applications. + +### Limiter with `OnRejected`, `RetryAfter`, and `GlobalLimiter` + +#### Add rate limiter + +Let's create the following method in the `MyProjectNameWebModule.cs` class in the `MyProjectName.Web` project. + +**Note:** If the `**.Web` project is not in your application, you can do the same in the project where your application is hosted. + +```csharp +private void ConfigureRateLimiters(ServiceConfigurationContext context) +{ + context.Services.AddRateLimiter(limiterOptions => + { + limiterOptions.OnRejected = (context, cancellationToken) => + { + if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) + { + context.HttpContext.Response.Headers.RetryAfter = + ((int) retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo); + } + + context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; + context.HttpContext.RequestServices.GetService()? + .CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware") + .LogWarning("OnRejected: {RequestPath}", context.HttpContext.Request.Path); + + return new ValueTask(); + }; + + limiterOptions.AddPolicy("UserBasedRateLimiting", context => + { + var currentUser = context.RequestServices.GetService(); + + if (currentUser is not null && currentUser.IsAuthenticated) + { + return RateLimitPartition.GetTokenBucketLimiter(currentUser.UserName, _ => new TokenBucketRateLimiterOptions + { + TokenLimit = 10, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 3, + ReplenishmentPeriod = TimeSpan.FromMinutes(1), + TokensPerPeriod = 4, + AutoReplenishment = true + }); + } + + return RateLimitPartition.GetSlidingWindowLimiter("anonymous-user", + _ => new SlidingWindowRateLimiterOptions + { + PermitLimit = 2, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1, + Window = TimeSpan.FromMinutes(1), + SegmentsPerWindow = 2 + }); + }); + + limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create(context => + { + var currentTenant = context.RequestServices.GetService(); + + if (currentTenant is not null && currentTenant.IsAvailable) + { + return RateLimitPartition.GetConcurrencyLimiter(currentTenant!.Name, _ => new ConcurrencyLimiterOptions + { + PermitLimit = 5, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 1 + }); + } + + return RateLimitPartition.GetNoLimiter("host"); + }); + }); +} +``` + +In the above example, the `TokenBucketLimiter` is used for each authenticated user, while the `SlidingWindowLimiter` is used for all anonymous users. Additionally, as a global limiter, the `ConcurrencyLimiter` is used for each tenant, while rate limiting is disabled for the host(tenant is not available). Also, for requests that are rejected when the limit is reached, sets the response status code to [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) and the response mentions when to retry (if available from the rate-limiting metadata). + +Let's call the `ConfigureRateLimiters` method that we created in the `ConfigureServices` method. + +The final version of the `ConfigureServices` method: + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + ConfigureBundles(); + ConfigureUrls(configuration); + ConfigurePages(configuration); + ConfigureAuthentication(context); + ConfigureImpersonation(context, configuration); + ConfigureAutoMapper(); + ConfigureVirtualFileSystem(hostingEnvironment); + ConfigureNavigationServices(); + ConfigureAutoApiControllers(); + ConfigureSwaggerServices(context.Services); + ConfigureExternalProviders(context); + ConfigureHealthChecks(context); + ConfigureCookieConsent(context); + ConfigureTheme(); + + Configure(options => + { + options.IsDynamicPermissionStoreEnabled = true; + }); + + ConfigureRateLimiters(context); // added +} +``` + +#### Add RateLimiter middleware + +Add the following line just before the `app.UseConfiguredEndpoints(...)` line to add the `RateLimiter` middleware to your ASP.NET Core request pipeline: + +```csharp +app.UseRateLimiter(); +``` + +#### Use rate limiter for all controllers + +Let's edit the `ConfiguredEndpoints` middleware as follows: + +```csharp +app.UseConfiguredEndpoints(endpoints => +{ + endpoints.MapRazorPages() + .DisableRateLimiting(); + + endpoints.MapControllers() + .RequireRateLimiting("UserBasedRateLimiting"); +}); +``` + +- **DisableRateLimiting:** It is used to disable the `ConcurrencyLimiter` for razor pages, which we set globally when the tenant is available. +- **RequireRateLimiting:** We have enabled the rate limiter, which we define according to whether the user is authenticated or not, for all controllers. + +## `EnableRateLimiting` and `DisableRateLimiting` attributes + +It's kind of unrealistic to always use rate limiting for all controllers or pages. Sometimes, we may want to throttle a particular endpoint or page. In such cases, we can use the `EnableRateLimiting` and `DisableRateLimiting` attributes. The `EnableRateLimiting` and `DisableRateLimiting` attributes can be applied to a controller, action method, or razor rage. Check [here](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0#enableratelimiting-and-disableratelimiting-attributes) for more. + +## Rate limit an HTTP handler + +Rate limiting when sending an HTTP request can be a good practice, especially in service-to-service communication. Because, resources are consumed by apps that rely on them, and when an app makes too many requests for a single resource, it can lead to *resource contention*. Resource contention occurs when a resource is consumed by too many clients, and the resource is unable to serve all of the apps that are requesting it. This can result in a poor user experience, and in some cases, it can even lead to a denial of service (DoS) attack. Since there are similar codes, I will not mention an example in this article, but to avoid such situations, you can write your own HTTP handler as [here](https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter#implement-a-delegatinghandler-subclass). + +## How does it work? + +[System.Threading.RateLimiting](https://www.nuget.org/packages/System.Threading.RateLimiting) provides the primitives for writing rate limiters as well as providing a few commonly used algorithms built-in. The main type is the abstract base class [RateLimiter](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiter.cs). + +```csharp +public abstract class RateLimiter : IAsyncDisposable, IDisposable +{ + public abstract int GetAvailablePermits(); + public abstract TimeSpan? IdleDuration { get; } + + public RateLimitLease Acquire(int permitCount = 1); + public ValueTask WaitAsync(int permitCount = 1, CancellationToken cancellationToken = default); + + public void Dispose(); + public ValueTask DisposeAsync(); +} +``` + +`RateLimiter` contains `Acquire` and `WaitAsync` as the core methods for trying to gain permits for a resource that is being protected. Depending on the application, the protected resource may need to acquire more than 1 permits, so `Acquire` and `WaitAsync` both accept an optional `permitCount` parameter. `Acquire` is a synchronous method that will check if enough permits are available or not and return a `RateLimitLease` which contains information about whether you successfully acquired the permits or not. `WaitAsync` is similar to `Acquire` except that it can support queuing permit requests which can be de-queued at some point in the future when the permits become available, which is why it’s asynchronous and accepts an optional `CancellationToken` to allow canceling the queued request. + +`RateLimitLease` has an `IsAcquired` property which is used to see if the permits were acquired. Additionally, the `RateLimitLease` may contain metadata such as a suggested retry-after period if the lease failed. Finally, the `RateLimitLease` is disposable and should be disposed when the code is done using the protected resource. The disposal will let the `RateLimiter` know to update its limits based on how many permits were acquired. + +## Limitations + +In most cases, the rate-limiting middleware provided with ASP.NET 7.0 will meet your requirements. However, if you would want to return statistics about your limits (e.g. [the way GitHub does](https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-http-headers)), you’ll find out that the ASP.NET rate limiting middleware does not support this. You won’t have access to the “number of requests remaining” or other metadata. Not in `OnRejected`, and definitely not if you want to return this data as headers on every request. + +## Best practices for rate limiting + +In order to use rate limiting properly, you need to have a solid understanding of the types of limiting available, as well as the data rate and data volume of your service. You also need to have a clear idea of how many users you expect to use your service as well as how they will interact with it. The best practices for rate limiting are as follows: +- Find the right rate limiter algorithm for your endpoint. I mean, the cost of an endpoint should be considered when selecting a limiter. The cost of an endpoint includes the resources used, for example, time, data access, CPU, and I/O. +- Set realistic limits. Once you’ve figured out all the above, you need to set realistic limits for each service. Then, before deploying an app using rate limiting to production, stress test the app to validate the rate limiters and options used. For example, create a [JMeter script](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) with a tool like [BlazeMeter](https://guide.blazemeter.com/hc/articles/207421695-Writing-your-first-JMeter-script) or [Apache JMeter HTTP(S) Test Script Recorder](https://jmeter.apache.org/usermanual/jmeter_proxy_step_by_step.html) and load the script to [Azure Load Testing](https://learn.microsoft.com/en-us/azure/load-testing/overview-what-is-azure-load-testing). +- In response to rate-limiting, intermittent, or non-specific errors, a client should generally retry the request after a delay. It is a best practice for this delay to increase exponentially after each failed request, which is referred to as *exponential backoff*. When many clients might be making schedule-based requests (such as fetching results every hour), additional random time (*jitter*) should be applied to the request timing, the backoff period, or both of them to ensure that these multiple client instances don't become periodic [thundering herd](https://www.wikiwand.com/en/Thundering_herd_problem), and cause a form of DDoS themselves. + +## Conclusion + +In this article, we’ve covered what rate limiting is, why you need to use it and the best practices for doing so. We’ve also looked at how to use three rate-limiting algorithms that are provided with .NET 7.0 on ABP-based applications and how rate-limiting works. Now that you’re familiar with the concept of rate limiting, it’s time to start implementing rate limiting in your application. This will allow you to control the traffic and ensure that your application is running smoothly without any issues. + +## References + +- https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?preserve-view=true&view=aspnetcore-7.0 +- https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/ +- https://blog.maartenballiauw.be/post/2022/09/26/aspnet-core-rate-limiting-middleware.html +- https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter +- https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern +- https://learn.microsoft.com/en-us/azure/architecture/patterns/throttling +- https://devblogs.microsoft.com/dotnet/announcing-rate-limiting-for-dotnet +- https://cloud.google.com/architecture/rate-limiting-strategies-techniques diff --git a/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png new file mode 100644 index 0000000000..cedae39a3b Binary files /dev/null and b/docs/en/Community-Articles/2022-11-29-Rate-Limiting/cover-image.png differ diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index bfce8a71bf..47e60700bc 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -2,7 +2,7 @@ ABP's Dependency Injection system is developed based on Microsoft's [dependency injection extension](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) library (Microsoft.Extensions.DependencyInjection nuget package). So, it's documentation is valid in ABP too. -> While ABP has no core dependency to any 3rd-party DI provider, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with Autofac installed. See [Autofac integration](Autofac-Integration.md) document for more information. +> While ABP has no core dependency to any 3rd-party DI provider. However, it's required to use a provider that supports dynamic proxying and some other advanced features to make some ABP features properly work. Startup templates come with [Autofac](https://autofac.org/) installed. See [Autofac integration](Autofac-Integration.md) document for more information. ## Modularity @@ -20,24 +20,24 @@ public class BlogModule : AbpModule ## Conventional Registration -ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` by overriding the `PreConfigureServices` method. +ABP introduces conventional service registration. You need not do anything to register a service by convention. It's automatically done. If you want to disable it, you can set `SkipAutoServiceRegistration` to `true` in the constructor of your module class. Example: ````C# public class BlogModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) + public BlogModule() { SkipAutoServiceRegistration = true; } } ```` -Once you skip auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example: +Once you skip the auto registration, you should manually register your services. In that case, ``AddAssemblyOf`` extension method can help you to register all your services by convention. Example: ````c# public class BlogModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) + public BlogModule() { SkipAutoServiceRegistration = true; } @@ -105,9 +105,7 @@ Example: [Dependency(ServiceLifetime.Transient, ReplaceServices = true)] public class TaxCalculator { - } - ```` ``Dependency`` attribute has a higher priority than other dependency interfaces if it defines the ``Lifetime`` property. @@ -120,7 +118,6 @@ public class TaxCalculator [ExposeServices(typeof(ITaxCalculator))] public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency { - } ```` @@ -179,7 +176,11 @@ public class MyModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { //Replacing the IConnectionStringResolver service - context.Services.Replace(ServiceDescriptor.Transient()); + context.Services.Replace( + ServiceDescriptor.Transient< + IConnectionStringResolver, + MyConnectionStringResolver + >()); } } ```` @@ -202,7 +203,7 @@ public class TaxAppService : ApplicationService _taxCalculator = taxCalculator; } - public void DoSomething() + public async Task DoSomethingAsync() { //...use _taxCalculator... } @@ -227,7 +228,7 @@ public class MyService : ITransientDependency Logger = NullLogger.Instance; } - public void DoSomething() + public async Task DoSomethingAsync() { //...use Logger to write logs... } @@ -244,17 +245,21 @@ One restriction of property injection is that you cannot use the dependency in y Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class. -#### DisablePropertyInjectionAttribute +#### DisablePropertyInjection Attribute -You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties. +You can use `[DisablePropertyInjection]` attribute on classes or their properties to disable property injection for the whole class or some specific properties. ````C# +// Disabling for all properties of the MyService class [DisablePropertyInjection] public class MyService : ITransientDependency { + public ILogger Logger { get; set; } + public ITaxCalculator TaxCalculator { get; set; } } +// Disabling only for the TaxCalculator property public class MyService : ITransientDependency { public ILogger Logger { get; set; } @@ -262,17 +267,21 @@ public class MyService : ITransientDependency [DisablePropertyInjection] public ITaxCalculator TaxCalculator { get; set; } } - ```` ### Resolve Service from IServiceProvider -You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below: +You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject `IServiceProvider` into your class and use the ``GetService`` or the `GetRequiredService` method as shown below: ````C# public class MyService : ITransientDependency { - public ILogger Logger { get; set; } + private readonly ITaxCalculator _taxCalculator; + + public MyService(IServiceProvider serviceProvider) + { + _taxCalculator = serviceProvider.GetRequiredService(); + } } ```` @@ -374,15 +383,15 @@ IEnumerable services = _serviceProvider.GetServices(); + // TODO: Use the taxCalculator + } +} + +```` + +With such a usage, you don't need to deal with creating service scopes and disposing the resolved services (as explained in the *Releasing/Disposing Services* section above). Because all the services resolved from the `ICachedServiceProvider` will be released once the service scope of the `MyService` instance is disposed. Also, you don't need to care about memory leaks (because of creating too many `ITaxCalculator` instances if we call `DoSomethingAsync` too many times), because only one `ITaxCalculator` instance is created, and it is reused. + +Since `ICachedServiceProvider` and `ITransientCachedServiceProvider` extends the standard `IServiceProvider` interface, you can use all the extension method of the `IServiceProvider` interface on them. In addition, they provides some other methods to provide a default value or a factory method for the services that are not found (that means not registered to the dependency injection system). Notice that the default value (or the value returned from your factory method) is also cached and reused. + +Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unless you need to create the service cache per usage. `ITransientCachedServiceProvider` guarantees that the created service instances are not shared with any other service, even they are in the same service scope. The services resolved from `ICachedServiceProvider` are shared with other services in the same service scope (in the same HTTP Request, for example), so it can be thought as more optimized. + +> ABP Framework also provides the `IAbpLazyServiceProvider` service. It does exists for backward compatibility and works exactly same with the `ITransientCachedServiceProvider` service. So, use the `ITransientCachedServiceProvider` since the `IAbpLazyServiceProvider` might be removed in future ABP versions. ## Advanced Features diff --git a/docs/en/Deployment/Configuring-Production.md b/docs/en/Deployment/Configuring-Production.md new file mode 100644 index 0000000000..5efa560621 --- /dev/null +++ b/docs/en/Deployment/Configuring-Production.md @@ -0,0 +1,100 @@ +# Configuring for Production + +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. + +## Distributed Cache Prefix + +ABP's [distributed cache infrastructure](../Caching.md) provides an option to set a key prefix for all of your data that is saved into your distributed cache provider. The default value of this option is not set (it is `null`). If you are using a distributed cache server that is shared by different applications, then you can set a prefix value to isolate an application's cache data from others. + +````csharp +Configure(options => +{ + options.KeyPrefix = "MyCrmApp"; +}); +```` + +That's all. ABP, then will add this prefix to all of your cache keys in your application as along as you use ABP's `IDistributedCache` or `IDistributedCache` services. See the [Caching documentation](../Caching.md) if you are new to distributed caching. + +> **Warning**: If you use ASP.NET Core's standard `IDistributedCache` service, it's your responsibility to add the key prefix (you can get the value by injecting `IOptions`). ABP can not do it. + +> **Warning**: Even if you have never used distributed caching in your own codebase, ABP still uses it for some features. So, you should always configure this prefix if your caching server is shared among multiple systems. + +> **Warning**: If you are building a microservice system, then you will have multiple applications that share the same distributed cache server. In such systems, all applications (or services) should normally use the same cache prefix, because you want all the applications to use the same cache data to have consistency between them. + +> **Warning**: Some of ABP's startup templates are pre-configured to set a prefix value for the distributed cache. So, please check your application code if it is already configured. + +## Distributed Lock Prefix + +ABP's [distributed locking infrastructure](../Distributed-Locking.md) provides an option to set a prefix for all the keys you are using in the distributed lock server. The default value of this option is not set (it is `null`). If you are using a distributed lock server that is shared by different applications, then you can set a prefix value to isolate an application's lock from others. + +````csharp +Configure(options => +{ + options.KeyPrefix = "MyCrmApp"; +}); +```` + +That's all. ABP, then will add this prefix to all of your keys in your application. See the [Distributed Locking documentation](../Distributed-Locking.md) if you are new to distributed locking. + +> **Warning**: Even if you have never used distributed locking in your own codebase, ABP still uses it for some features. So, you should always configure this prefix if your distributed lock server is shared among multiple systems. + +> **Warning**: If you are building a microservice system, then you will have multiple applications that share the same distributed locking server. In such systems, all applications (or services) should normally use the same lock prefix, because you want to globally lock your resources in your system. + +> **Warning**: Some of ABP's startup templates are pre-configured to set a prefix value for distributed locking. So, please check your application code if it is already configured. + +## Email Sender + +ABP's [Email Sending](../Emailing.md) system abstracts sending emails from your application and module code and allows you to configure the email provider and settings in a single place. + +Email service is configured to write email contents to the standard [application log](../Logging.md) in development environment. You should configure the email settings to be able to send emails to users in your production environment. + +Please see the [Email Sending](../Emailing.md) document to learn how to configure its settings to really send emails. + +> **Warning**: If you don't configure the email settings, you will get errors while trying to send emails. For example, the [Account module](../Modules/Account.md)'s *Password Reset* feature sends email to the users to reset their passwords if they forget it. + +## SMS Sender + +ABP's [SMS Sending abstraction](https://docs.abp.io/en/abp/latest/SMS-Sending) provides a unified interface to send SMS to users. However, its implementation is left to you. Because, typically a paid SMS service is used to send SMS, and ABP doesn't depend on a specific SMS provider. + +So, if you are using the `ISmsSender` service, you must implement it yourself, as shown in the following code block: + +````csharp +public class MySmsSender : ISmsSender, ITransientDependency +{ + public async Task SendAsync(SmsMessage smsMessage) + { + // TODO: Send it using your provider... + } +} +```` + +> [ABP Commercial](https://commercial.abp.io/) provides a [Twilio SMS Module](https://docs.abp.io/en/commercial/latest/modules/twilio-sms) as a pre-built integration with the popular [Twilio](https://www.twilio.com/) platform. + +## BLOB Provider + +If you use ABP's [BLOB Storing](https://docs.abp.io/en/abp/latest/Blob-Storing) infrastructure, you should care about the BLOB provider in your production environment. For example, if you use the [File System](../Blob-Storing-File-System.md) provider and your application is running in a Docker container, you should configure a volume mapping for the BLOB storage path. Otherwise, your data will be lost when the container is restarted. Also, the File System is not a good provider for production if you have a [clustered deployment](Clustered-Environment.md) or a microservice system. + +Check the [BLOB Storing](../Blob-Storing.md) document to see all the available BLOB storage providers. + +> **Warning**: Even if you don't directly use the BLOB Storage system, a module you are depending on may use it. For example, ABP Commercial's [File Management](https://docs.abp.io/en/commercial/latest/modules/file-management) module stores file contents, and the [Account](https://docs.abp.io/en/commercial/latest/modules/account) module stores user profile pictures in the BLOB Storage system. So, be careful with the BLOB Storing configuration in production. Note that ABP Commercial uses the [Database Provider](../Blob-Storing-Database.md) as a pre-configured BLOB storage provider, which works in production without any problem, but you may still want to use another provider. + +## String Encryption + +ABP's [`IStringEncryptionService` Service](../String-Encryption.md) simply encrypts and decrypts given strings based on a password phrase. You should configure the `AbpStringEncryptionOptions` options for the production with a strong password and keep it as a secret. You can also configure the other properties of those options class. See the following example: + +````csharp +Configure(options => +{ + options.DefaultPassPhrase = "gs5nTT042HAL4it1"; +}); +```` + +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. + +## 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. + +While you will always secure your HTTP APIs with other techniques (like the [Authorization](../Authorization.md) system), allowing malicious software and people to easily discover your HTTP API endpoint details can be considered as a security problem for some systems. So, be careful while taking the decision of enabling or disabling Swagger for the production environment. + +> You may also want to see the [ABP Swagger integration](../API/Swagger-Integration.md) document. diff --git a/docs/en/Deployment/Index.md b/docs/en/Deployment/Index.md index 6503f8b664..852efa3d43 100644 --- a/docs/en/Deployment/Index.md +++ b/docs/en/Deployment/Index.md @@ -6,4 +6,5 @@ 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. diff --git a/docs/en/Distributed-Event-Bus.md b/docs/en/Distributed-Event-Bus.md index 21b1b07256..cc63e98fcd 100644 --- a/docs/en/Distributed-Event-Bus.md +++ b/docs/en/Distributed-Event-Bus.md @@ -434,6 +434,29 @@ Here, the following properties are available on the `config` object: * `HandlerSelector`: A predicate to filter the event handled types (classes implementing the `IDistributedEventHandler` interface) to be used for this configuration. This is especially useful if you want to ignore some event handler types from inbox processing, or want to define named inbox configurations and group event handlers within these configurations. See the *Named Configurations* section. * `ImplementationType`: Type of the class that implements the database operations for the inbox. This is normally set when you call `UseDbContext` as shown before. See *Implementing a Custom Outbox/Inbox Database Provider* section for advanced usages. +#### AbpEventBusBoxesOptions + +`AbpEventBusBoxesOptions` can be used to fine-tune how inbox and outbox systems work. For most of the systems, using the defaults would be more than enough, but you can configure it to optimize your system when it is needed. + +Just like all the [options classes](Options.md), `AbpEventBusBoxesOptions` can be configured in the `ConfigureServices` method of your [module class](Module-Development-Basics.md) as shown in the following code block: + +````csharp +Configure(options => +{ + // TODO: configure the options +}); +```` + +`AbpEventBusBoxesOptions` has the following properties to be configured: + +* `BatchPublishOutboxEvents`: Can be used to enable or disable batch publishing events to the message broker. Batch publishing works if it is supported by the distributed event bus provider. If not supported, events are sent one by one as the fallback logic. Keep it as enabled since it has a great performance gain wherever possible. Default value is `true` (enabled). +* `PeriodTimeSpan`: The period of the inbox and outbox message processors to check if there is a new event in the database. Default value is 2 seconds (`TimeSpan.FromSeconds(2)`). +* `CleanOldEventTimeIntervalSpan`: The event inbox system periodically checks and deletes the old processed events from the inbox in the database. You can set this value to determine the check period. Default value is 6 hours (`TimeSpan.FromHours(6)`). +* `WaitTimeToDeleteProcessedInboxEvents`: Inbox events are not deleted from the database for a while even if they are successfully processed. This is for a system to prevent multiple process of the same event (if the event broker sends it twice). This configuration value determines the time to keep the processed events. Default value is 2 hours (`TimeSpan.FromHours(2)`). +* `InboxWaitingEventMaxCount`: The maximum number of events to query at once from the inbox in the database. Default value is 1000. +* `OutboxWaitingEventMaxCount`: The maximum number of events to query at once from the outbox in the database. Default value is 1000. +* `DistributedLockWaitDuration`: ABP uses [distributed locking](Distributed-Locking.md) to prevent concurrent access to the inbox and outbox messages in the database, when running multiple instance of the same application. If an instance of the application can not obtain the lock, it tries after a duration. This is the configuration of that duration. Default value is 15 seconds (`TimeSpan.FromSeconds(15)`). + ### Skipping Outbox `IDistributedEventBus.PublishAsync` method provides an optional parameter, `useOutbox`, which is set to `true` by default. If you bypass outbox and immediately publish an event, you can set it to `false` for a specific event publishing operation. diff --git a/docs/en/Migration-Guides/Abp-7_0.md b/docs/en/Migration-Guides/Abp-7_0.md index 3b6b1c5e6a..7c3a0d90b5 100644 --- a/docs/en/Migration-Guides/Abp-7_0.md +++ b/docs/en/Migration-Guides/Abp-7_0.md @@ -101,3 +101,11 @@ See https://github.com/abpframework/abp/pull/13644 for more info. We've already done this for our themes. See https://github.com/abpframework/abp/pull/13845 for more info. + +## Replaced `BlogPostPublicDto` with `BlogPostCommonDto` + +- In the CMS Kit Module, `BlogPostPublicDto` has been moved to `Volo.CmsKit.Common.Application.Contracts` from `Volo.CmsKit.Public.Application.Contracts` and renamed to `BlogPostCommonDto`. + +- See the [PR#13499](https://github.com/abpframework/abp/pull/13499) for more information. + +> You can ignore this if you don't use CMS Kit Module. \ No newline at end of file diff --git a/docs/en/UI/Angular/Extensions-Overall.md b/docs/en/UI/Angular/Extensions-Overall.md index 8f7b697e59..2bb9862b1d 100644 --- a/docs/en/UI/Angular/Extensions-Overall.md +++ b/docs/en/UI/Angular/Extensions-Overall.md @@ -11,7 +11,7 @@ See the documents below for the details: ## Extensible Table Component -Using [ngx-datatable](https://github.com/swimlane/ngx-datatable) in extensinble table. +Using [ngx-datatable](https://github.com/swimlane/ngx-datatable) in extensible table. ````ts ().Begin()) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } } } ``` @@ -106,9 +101,6 @@ public class MyModule : AbpModule ApplicationInitializationContext context) { await context.AddBackgroundWorkerAsync(); - - //如果定义了接口 - //await context.AddBackgroundWorkerAsync(); } } ```` @@ -127,4 +119,4 @@ context.ServiceProvider 它解析给定的后台工作者并添加到 `IBackgroundWorkerManager`. -虽然我们通常在 `OnApplicationInitializationAsync` 中添加后台工作者, 但对此没有限制. 你可以在任何地方注入 `IBackgroundWorkerManager` 并在运行时添加后台工作者. \ No newline at end of file +虽然我们通常在 `OnApplicationInitializationAsync` 中添加后台工作者, 但对此没有限制. 你可以在任何地方注入 `IBackgroundWorkerManager` 并在运行时添加后台工作者. diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs new file mode 100644 index 0000000000..3cace3f113 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorRemoteTenantStore.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Pages.Abp.MultiTenancy.ClientProxies; +using Volo.Abp.AspNetCore.Mvc.MultiTenancy; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; +using DependencyAttribute = Volo.Abp.DependencyInjection.DependencyAttribute; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor; + +[Dependency(ReplaceServices = true)] +public class MauiBlazorRemoteTenantStore : ITenantStore, ITransientDependency +{ + protected AbpTenantClientProxy TenantAppService { get; } + protected IDistributedCache Cache { get; } + + public MauiBlazorRemoteTenantStore(AbpTenantClientProxy tenantAppService, IDistributedCache cache) + { + TenantAppService = tenantAppService; + Cache = cache; + } + + public async Task FindAsync(string name) + { + var cacheKey = CreateCacheKey(name); + + var tenantConfiguration = await Cache.GetOrAddAsync( + cacheKey, + async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name)), + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. + } + ); + + return tenantConfiguration; + } + + public async Task FindAsync(Guid id) + { + var cacheKey = CreateCacheKey(id); + + var tenantConfiguration = await Cache.GetOrAddAsync( + cacheKey, + async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id)), + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. + } + ); + + return tenantConfiguration; + } + + public TenantConfiguration Find(string name) + { + var cacheKey = CreateCacheKey(name); + + var tenantConfiguration = Cache.GetOrAdd( + cacheKey, + () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByNameAsync(name))), + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. + } + ); + + return tenantConfiguration; + } + + public TenantConfiguration Find(Guid id) + { + var cacheKey = CreateCacheKey(id); + + var tenantConfiguration = Cache.GetOrAdd( + cacheKey, + () => AsyncHelper.RunSync(async () => CreateTenantConfiguration(await TenantAppService.FindTenantByIdAsync(id))), + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = + TimeSpan.FromMinutes(5) //TODO: Should be configurable. + } + ); + + return tenantConfiguration; + } + + protected virtual TenantConfiguration CreateTenantConfiguration(FindTenantResultDto tenantResultDto) + { + if (!tenantResultDto.Success || tenantResultDto.TenantId == null) + { + return null; + } + + return new TenantConfiguration(tenantResultDto.TenantId.Value, tenantResultDto.Name); + } + + protected virtual string CreateCacheKey(string tenantName) + { + return $"RemoteTenantStore_Name_{tenantName}"; + } + + protected virtual string CreateCacheKey(Guid tenantId) + { + return $"RemoteTenantStore_Id_{tenantId:N}"; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj index 4a1a8789f9..d45892bccb 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj +++ b/framework/src/Volo.Abp.BlazoriseUI/Volo.Abp.BlazoriseUI.csproj @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 8421eb656b..ee0b6a90da 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -111,7 +111,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien var skipBundling = commandLineArgs.Options.ContainsKey(Options.SkipBundling.Long) || commandLineArgs.Options.ContainsKey(Options.SkipBundling.Short); if (!skipBundling) { - await RunBundleForBlazorWasmTemplateAsync(projectArgs); + await RunBundleForBlazorWasmOrMauiBlazorTemplateAsync(projectArgs); } await ConfigurePwaSupportForAngular(projectArgs); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs index 2d5c9856cf..e0cbfe7d07 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs @@ -118,7 +118,7 @@ public abstract class ProjectCreationCommandBase Logger.LogInformation("DBMS: " + databaseManagementSystem); } - var uiFramework = GetUiFramework(commandLineArgs); + var uiFramework = GetUiFramework(commandLineArgs, template); if (uiFramework != UiFramework.NotSpecified) { Logger.LogInformation("UI Framework: " + uiFramework); @@ -413,22 +413,24 @@ public abstract class ProjectCreationCommandBase } } - protected async Task RunBundleForBlazorWasmTemplateAsync(ProjectBuildArgs projectArgs) + protected async Task RunBundleForBlazorWasmOrMauiBlazorTemplateAsync(ProjectBuildArgs projectArgs) { - if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework == UiFramework.Blazor) + if (AppTemplateBase.IsAppTemplate(projectArgs.TemplateName) && projectArgs.UiFramework is UiFramework.Blazor or UiFramework.MauiBlazor) { - Logger.LogInformation("Generating bundles for Blazor Wasm..."); + var isWebassembly = projectArgs.UiFramework == UiFramework.Blazor; + var message = isWebassembly ? "Generating bundles for Blazor Wasm" : "Generating bundles for MAUI Blazor"; + Logger.LogInformation($"{message}..."); await EventBus.PublishAsync(new ProjectCreationProgressEvent { - Message = "Generating bundles for Blazor Wasm" + Message = message }, false); var directory = Path.GetDirectoryName( - Directory.GetFiles(projectArgs.OutputFolder, "*.Blazor.csproj", SearchOption.AllDirectories).First() + Directory.GetFiles(projectArgs.OutputFolder, isWebassembly? "*.Blazor.csproj" :"*.MauiBlazor.csproj", SearchOption.AllDirectories).First() ); - await _bundlingService.BundleAsync(directory, true); + await _bundlingService.BundleAsync(directory, true, projectType: isWebassembly ? BundlingConsts.WebAssembly : BundlingConsts.MauiBlazor); } } @@ -531,7 +533,7 @@ public abstract class ProjectCreationCommandBase } } - protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs) + protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs, string template = "app") { if (commandLineArgs.Options.ContainsKey("no-ui")) { @@ -554,6 +556,8 @@ public abstract class ProjectCreationCommandBase return UiFramework.Blazor; case "blazor-server": return UiFramework.BlazorServer; + case "maui-blazor" when template == AppProTemplate.TemplateName: + return UiFramework.MauiBlazor; default: throw new CliUsageException(ExceptionMessageHelper.GetInvalidOptionExceptionMessage("UI Framework")); } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index e0817d8a45..7dceb70337 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -261,6 +261,12 @@ public class AbpIoSourceCodeStore : ISourceCodeStore, ITransientDependency } catch (Exception ex) { + if(ex is UserFriendlyException) + { + Logger.LogWarning(ex.Message); + throw; + } + Console.WriteLine("Error occured while downloading source-code from {0} : {1}{2}{3}", url, responseMessage?.ToString(), Environment.NewLine, ex.Message); throw; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs index b1ffe8cb11..5b1ebb56f4 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/UiFramework.cs @@ -7,5 +7,6 @@ public enum UiFramework Mvc = 2, Angular = 3, Blazor = 4, - BlazorServer = 5 + BlazorServer = 5, + MauiBlazor = 6 } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index 2ffa12bca7..44b9c20423 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -132,7 +132,11 @@ public abstract class AppTemplateBase : TemplateInfo case UiFramework.BlazorServer: ConfigureWithBlazorServerUi(context, steps); break; - + + case UiFramework.MauiBlazor: + ConfigureWithMauiBlazorUi(context, steps); + break; + case UiFramework.Mvc: case UiFramework.NotSpecified: ConfigureWithMvcUi(context, steps); @@ -150,6 +154,11 @@ public abstract class AppTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Blazor.Server.Tiered")); } + if (context.BuildArgs.UiFramework != UiFramework.MauiBlazor) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MauiBlazor")); + } + if (context.BuildArgs.UiFramework != UiFramework.Angular) { steps.Add(new RemoveFolderStep("/angular")); @@ -510,6 +519,37 @@ public abstract class AppTemplateBase : TemplateInfo } } + protected void ConfigureWithMauiBlazorUi(ProjectBuildContext context, List steps) + { + context.Symbols.Add("ui:maui-blazor"); + + steps.Add(new MauiBlazorChangeApplicationIdGuidStep()); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests")); + + if (context.BuildArgs.ExtraProperties.ContainsKey("separate-identity-server") || + context.BuildArgs.ExtraProperties.ContainsKey("separate-auth-server")) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds")); + steps.Add(new MauiBlazorPortChangeForSeparatedAuthServersStep()); + steps.Add(new AppTemplateChangeDbMigratorPortSettingsStep("44300")); + + if (context.BuildArgs.MobileApp == MobileApp.ReactNative) + { + steps.Add(new ReactEnvironmentFilePortChangeForSeparatedAuthServersStep()); + } + } + else + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); + steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); + steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); + } + } + protected void RemoveUnnecessaryPorts(ProjectBuildContext context, List steps) { steps.Add(new RemoveUnnecessaryPortsStep()); @@ -599,7 +639,8 @@ public abstract class AppTemplateBase : TemplateInfo { if ((context.BuildArgs.UiFramework == UiFramework.Mvc || context.BuildArgs.UiFramework == UiFramework.Blazor - || context.BuildArgs.UiFramework == UiFramework.BlazorServer) && + || context.BuildArgs.UiFramework == UiFramework.BlazorServer + || context.BuildArgs.UiFramework == UiFramework.MauiBlazor) && context.BuildArgs.MobileApp == MobileApp.None) { steps.Add(new MoveFolderStep("/aspnet-core/", "/")); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs new file mode 100644 index 0000000000..953447cfa5 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorChangeApplicationIdGuidStep.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Xml; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.Utils; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.App; + +public class MauiBlazorChangeApplicationIdGuidStep: ProjectBuildPipelineStep +{ + public override void Execute(ProjectBuildContext context) + { + var projectFile = context.Files.FirstOrDefault(f => f.Name.EndsWith("MyCompanyName.MyProjectName.MauiBlazor.csproj")); + + if (projectFile == null) + { + return; + } + + using (var stream = StreamHelper.GenerateStreamFromString(projectFile.Content)) + { + var doc = new XmlDocument { PreserveWhitespace = true }; + doc.Load(stream); + + var node = doc.SelectSingleNode("/Project/PropertyGroup/ApplicationIdGuid"); + if (node != null) + { + node.InnerText = Guid.NewGuid().ToString(); + } + + projectFile.SetContent(doc.OuterXml); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs new file mode 100644 index 0000000000..92b59de7f3 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/MauiBlazorPortChangeForSeparatedAuthServersStep.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.App; + +public class MauiBlazorPortChangeForSeparatedAuthServersStep: ProjectBuildPipelineStep +{ + public override void Execute(ProjectBuildContext context) + { + + var appsettingsFile = context.Files.FirstOrDefault(x => + !x.IsDirectory && + x.Name.EndsWith("aspnet-core/src/MyCompanyName.MyProjectName.MauiBlazor/appsettings.json", + StringComparison.InvariantCultureIgnoreCase) + ); + + if(appsettingsFile == null) + { + return; + } + + appsettingsFile.NormalizeLineEndings(); + var lines = appsettingsFile.GetLines(); + + for (var i = 1; i < lines.Length; i++) + { + var line = lines[i]; + + var previousLine = lines[i-1]; + + if (line.Contains("Authority") && line.Contains("localhost")) + { + line = line.Replace("44305", "44301"); + } + else if (previousLine.Contains("AbpAccountPublic") && line.Contains("BaseUrl") && line.Contains("localhost")) + { + line = line.Replace("44305", "44301"); + } + else if (previousLine.Contains("Default") && line.Contains("BaseUrl") && line.Contains("localhost")) + { + line = line.Replace("44305", "44300"); + } + + lines[i] = line; + } + + appsettingsFile.SetLines(lines); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs index 2b45d64b19..41b9393573 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Maui/MauiChangePortStep.cs @@ -53,6 +53,7 @@ public class MauiChangePortStep : ProjectBuildPipelineStep { case UiFramework.Angular: case UiFramework.Blazor: + case UiFramework.MauiBlazor: authServerPort = "44305"; apiHostPort = "44305"; break; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs index 3aff8a78ad..a180592a01 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs @@ -1,15 +1,23 @@ using System; +using Microsoft.Extensions.DependencyInjection; namespace Volo.Abp.DependencyInjection; +/// +/// This class is equivalent of the . +/// Use instead of this class, for new projects. +/// [ExposeServices(typeof(IAbpLazyServiceProvider))] -public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyServiceProvider, ITransientDependency +public class AbpLazyServiceProvider : + CachedServiceProviderBase, + IAbpLazyServiceProvider, + ITransientDependency { public AbpLazyServiceProvider(IServiceProvider serviceProvider) : base(serviceProvider) { } - + public virtual T LazyGetRequiredService() { return (T)LazyGetRequiredService(typeof(T)); @@ -17,7 +25,7 @@ public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyService public virtual object LazyGetRequiredService(Type serviceType) { - return GetService(serviceType); + return this.GetRequiredService(serviceType); } public virtual T LazyGetService() @@ -32,24 +40,21 @@ public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyService public virtual T LazyGetService(T defaultValue) { - return (T)LazyGetService(typeof(T), defaultValue); + return GetService(defaultValue); } public virtual object LazyGetService(Type serviceType, object defaultValue) { - return LazyGetService(serviceType) ?? defaultValue; + return GetService(serviceType, defaultValue); } public virtual T LazyGetService(Func factory) { - return (T)LazyGetService(typeof(T), factory); + return GetService(factory); } public virtual object LazyGetService(Type serviceType, Func factory) { - return CachedServices.GetOrAdd( - serviceType, - _ => new Lazy(() => factory(ServiceProvider)) - ).Value; + return GetService(serviceType, factory); } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs index 870e1f8faf..ef0fed3541 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace Volo.Abp.DependencyInjection; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs index c261d3a3bb..21f2b1c516 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; namespace Volo.Abp.DependencyInjection; -public abstract class CachedServiceProviderBase +public abstract class CachedServiceProviderBase : ICachedServiceProviderBase { protected IServiceProvider ServiceProvider { get; } protected ConcurrentDictionary> CachedServices { get; } @@ -22,4 +22,27 @@ public abstract class CachedServiceProviderBase _ => new Lazy(() => ServiceProvider.GetService(serviceType)) ).Value; } + + public T GetService(T defaultValue) + { + return (T)GetService(typeof(T), defaultValue); + } + + public object GetService(Type serviceType, object defaultValue) + { + return GetService(serviceType) ?? defaultValue; + } + + public T GetService(Func factory) + { + return (T)GetService(typeof(T), factory); + } + + public object GetService(Type serviceType, Func factory) + { + return CachedServices.GetOrAdd( + serviceType, + _ => new Lazy(() => factory(ServiceProvider)) + ).Value; + } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs index 610bb40dfa..cd5f1da67f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs @@ -2,21 +2,57 @@ namespace Volo.Abp.DependencyInjection; -public interface IAbpLazyServiceProvider +/// +/// This service is equivalent of the . +/// Use instead of this interface, for new projects. +/// +public interface IAbpLazyServiceProvider : ICachedServiceProviderBase { + /// + /// This method is equivalent of the GetRequiredService method. + /// It does exists for backward compatibility. + /// T LazyGetRequiredService(); + /// + /// This method is equivalent of the GetRequiredService method. + /// It does exists for backward compatibility. + /// object LazyGetRequiredService(Type serviceType); + /// + /// This method is equivalent of the GetService method. + /// It does exists for backward compatibility. + /// T LazyGetService(); + /// + /// This method is equivalent of the GetService method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// T LazyGetService(T defaultValue); - + + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType, object defaultValue); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType, Func factory); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// T LazyGetService(Func factory); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs index 073daa135a..4a923901f9 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs @@ -1,6 +1,4 @@ -using System; - -namespace Volo.Abp.DependencyInjection; +namespace Volo.Abp.DependencyInjection; /// /// Provides services by caching the resolved services. @@ -8,7 +6,7 @@ namespace Volo.Abp.DependencyInjection; /// This service's lifetime is scoped and it should be used /// for a limited scope. /// -public interface ICachedServiceProvider : IServiceProvider +public interface ICachedServiceProvider : ICachedServiceProviderBase { } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs new file mode 100644 index 0000000000..2e15a45a46 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs @@ -0,0 +1,14 @@ +using System; + +namespace Volo.Abp.DependencyInjection; + +public interface ICachedServiceProviderBase : IServiceProvider +{ + T GetService(T defaultValue); + + object GetService(Type serviceType, object defaultValue); + + T GetService(Func factory); + + object GetService(Type serviceType, Func factory); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs index 55c43e7c94..7b57b54e07 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs @@ -1,5 +1,3 @@ -using System; - namespace Volo.Abp.DependencyInjection; /// @@ -8,7 +6,7 @@ namespace Volo.Abp.DependencyInjection; /// This service's lifetime is transient. /// for the one with scoped lifetime. /// -public interface ITransientCachedServiceProvider : IServiceProvider +public interface ITransientCachedServiceProvider : ICachedServiceProviderBase { } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs index 6d70b95512..93d740dc21 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using Volo.Abp.EventBus.Boxes; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs index 04cf28ea88..93d740dc21 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle/Volo/Abp/EntityFrameworkCore/DistributedEvents/OracleDbContextEventInbox.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using Volo.Abp.EventBus.Boxes; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs index c99192fc48..054ed78ee7 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/DistributedEvents/PostgreSqlDbContextEventInbox.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using Volo.Abp.EventBus.Boxes; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs index b076a17d36..1035716491 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/DbContextEventInbox.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using Volo.Abp.EventBus.Boxes; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs index 77c7177df6..0aa99e7df7 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DistributedEvents/SqlRawDbContextEventInbox.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using Volo.Abp.EventBus.Boxes; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs index 30e2a9e5b0..fe3d9f3032 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Volo.Abp.BackgroundWorkers; using Volo.Abp.DistributedLocking; using Volo.Abp.EventBus.Abstractions; -using Volo.Abp.EventBus.Boxes; using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusExtensions.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusExtensions.cs index d6439fb695..273f87ed16 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusExtensions.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpDistributedEventBusExtensions.cs @@ -1,6 +1,4 @@ -using Volo.Abp.EventBus.Distributed; - -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public static class AbpDistributedEventBusExtensions { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpEventBusBoxesOptions.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpEventBusBoxesOptions.cs index d92c48db3a..67facf5d42 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpEventBusBoxesOptions.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/AbpEventBusBoxesOptions.cs @@ -1,6 +1,6 @@ using System; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public class AbpEventBusBoxesOptions { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IInboxProcessor.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IInboxProcessor.cs index 82cef787f4..0591379a21 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IInboxProcessor.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IInboxProcessor.cs @@ -1,8 +1,7 @@ using System.Threading; using System.Threading.Tasks; -using Volo.Abp.EventBus.Distributed; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public interface IInboxProcessor { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IOutboxSender.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IOutboxSender.cs index 0a50c6b570..1fa2f51a3f 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IOutboxSender.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IOutboxSender.cs @@ -1,8 +1,7 @@ using System.Threading; using System.Threading.Tasks; -using Volo.Abp.EventBus.Distributed; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public interface IOutboxSender { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessManager.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessManager.cs index 074585e08d..597fca0340 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessManager.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessManager.cs @@ -5,9 +5,8 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.BackgroundWorkers; -using Volo.Abp.EventBus.Distributed; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public class InboxProcessManager : IBackgroundWorker { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessor.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessor.cs index 50c1a4e009..1b4f0e4deb 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessor.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/InboxProcessor.cs @@ -7,12 +7,11 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.DistributedLocking; -using Volo.Abp.EventBus.Distributed; using Volo.Abp.Threading; using Volo.Abp.Timing; using Volo.Abp.Uow; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public class InboxProcessor : IInboxProcessor, ITransientDependency { @@ -110,7 +109,7 @@ public class InboxProcessor : IInboxProcessor, ITransientDependency await Inbox.MarkAsProcessedAsync(waitingEvent.Id); - await uow.CompleteAsync(); + await uow.CompleteAsync(StoppingToken); } Logger.LogInformation($"Processed the incoming event with id = {waitingEvent.Id:N}"); diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSender.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSender.cs index 851ddf3da1..3f7e2dfd73 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSender.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSender.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -10,10 +9,9 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.DistributedLocking; -using Volo.Abp.EventBus.Distributed; using Volo.Abp.Threading; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public class OutboxSender : IOutboxSender, ITransientDependency { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSenderManager.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSenderManager.cs index 8877c283a7..f9977b86e5 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSenderManager.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/OutboxSenderManager.cs @@ -5,9 +5,8 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.BackgroundWorkers; -using Volo.Abp.EventBus.Distributed; -namespace Volo.Abp.EventBus.Boxes; +namespace Volo.Abp.EventBus.Distributed; public class OutboxSenderManager : IBackgroundWorker { diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs index 2a73bd4291..55b40fa29b 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DistributedEvents/MongoDbContextEventInbox.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Options; using MongoDB.Driver; using MongoDB.Driver.Linq; -using Volo.Abp.EventBus.Boxes; using Volo.Abp.EventBus.Distributed; using Volo.Abp.Timing; using Volo.Abp.Uow; diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs index 87882015fc..0e3969fccd 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs @@ -1,6 +1,9 @@ -using Volo.Abp.AspNetCore.Components.Web.Theming; +using Localization.Resources.AbpUi; +using Volo.Abp.AspNetCore.Components.Web.Theming; using Volo.Abp.FeatureManagement.Blazor.Settings; +using Volo.Abp.FeatureManagement.Localization; using Volo.Abp.Features; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.SettingManagement.Blazor; @@ -20,5 +23,12 @@ public class AbpFeatureManagementBlazorModule : AbpModule { options.Contributors.Add(new FeatureSettingManagementComponentContributor()); }); + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes(typeof(AbpUiResource)); + }); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs index 4b9dc5326f..3367b42a1b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs @@ -1,7 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +using Localization.Resources.AbpUi; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; using Volo.Abp.AutoMapper; using Volo.Abp.BlazoriseUI; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending.Modularity; @@ -39,6 +42,15 @@ public class AbpIdentityBlazorModule : AbpModule { options.AdditionalAssemblies.Add(typeof(AbpIdentityBlazorModule).Assembly); }); + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpUiResource) + ); + }); } public override void PostConfigureServices(ServiceConfigurationContext context) diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor index 657fe795ec..486da65537 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor @@ -14,22 +14,17 @@ @* ************************* PAGE HEADER ************************* *@ - + Toolbar="@Toolbar" /> - - - - - - - - - - - - + + + + @L["Search"] + + + + @* ************************* DATA GRID ************************* *@ string userName = null, string phoneNumber = null, string emailAddress = null, + string name = null, + string surname = null, bool? isLockedOut = null, bool? notActive = null, + bool? emailConfirmed = null, + bool? isExternal = null, + DateTime? maxCreationTime = null, + DateTime? minCreationTime = null, + DateTime? maxModifitionTime = null, + DateTime? minModifitionTime = null, CancellationToken cancellationToken = default ); @@ -98,8 +106,16 @@ public interface IIdentityUserRepository : IBasicRepository string userName = null, string phoneNumber = null, string emailAddress = null, + string name = null, + string surname = null, bool? isLockedOut = null, bool? notActive = null, + bool? emailConfirmed = null, + bool? isExternal = null, + DateTime? maxCreationTime = null, + DateTime? minCreationTime = null, + DateTime? maxModifitionTime = null, + DateTime? minModifitionTime = null, CancellationToken cancellationToken = default ); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index ab9cc892d6..124f631311 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -142,8 +142,16 @@ public class EfCoreIdentityUserRepository : EfCoreRepository x.UserName == userName) .WhereIf(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) .WhereIf(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) - .WhereIf(isLockedOut == true, x => x.LockoutEnabled && x.LockoutEnd.Value.CompareTo(DateTime.UtcNow) > 0) - .WhereIf(notActive == true, x => !x.IsActive) + .WhereIf(!string.IsNullOrWhiteSpace(name), x => x.Name == name) + .WhereIf(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) + .WhereIf(isLockedOut.HasValue, x => x.LockoutEnabled && x.LockoutEnd.Value.CompareTo(DateTime.UtcNow) > 0) + .WhereIf(notActive.HasValue, x => !x.IsActive) + .WhereIf(emailConfirmed.HasValue, x => x.EmailConfirmed) + .WhereIf(isExternal.HasValue, x => x.IsExternal) + .WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) + .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) + .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .OrderBy(sorting.IsNullOrWhiteSpace() ? nameof(IdentityUser.UserName) : sorting) .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); @@ -207,8 +223,16 @@ public class EfCoreIdentityUserRepository : EfCoreRepository x.UserName == userName) .WhereIf(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) .WhereIf(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) - .WhereIf(isLockedOut == true, x => x.LockoutEnabled && x.LockoutEnd.Value.CompareTo(DateTime.UtcNow) > 0) - .WhereIf(notActive == true, x => !x.IsActive) + .WhereIf(!string.IsNullOrWhiteSpace(name), x => x.Name == name) + .WhereIf(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) + .WhereIf(isLockedOut.HasValue, x => x.LockoutEnabled && x.LockoutEnd.Value.CompareTo(DateTime.UtcNow) > 0) + .WhereIf(notActive.HasValue, x => !x.IsActive) + .WhereIf(emailConfirmed.HasValue, x => x.EmailConfirmed) + .WhereIf(isExternal.HasValue, x => x.IsExternal) + .WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) + .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) + .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .LongCountAsync(GetCancellationToken(cancellationToken)); } @@ -305,11 +337,11 @@ public class EfCoreIdentityUserRepository : EfCoreRepository FindByTenantIdAndUserNameAsync( [NotNull] string userName, - Guid? tenantId, + Guid? tenantId, bool includeDetails = true, CancellationToken cancellationToken = default) { - return await(await GetDbSetAsync()) + return await (await GetDbSetAsync()) .IncludeDetails(includeDetails) .FirstOrDefaultAsync( u => u.TenantId == tenantId && u.UserName == userName, diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 71d0d2eb53..710363d4a6 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -143,8 +143,16 @@ public class MongoIdentityUserRepository : MongoDbRepository>(!string.IsNullOrWhiteSpace(userName), x => x.UserName == userName) .WhereIf>(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) .WhereIf>(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) - .WhereIf>(isLockedOut == true, x => x.LockoutEnabled && x.LockoutEnd > DateTimeOffset.UtcNow) - .WhereIf>(notActive == true, x => !x.IsActive) + .WhereIf>(!string.IsNullOrWhiteSpace(name), x => x.Name == name) + .WhereIf>(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) + .WhereIf>(isLockedOut.HasValue, x => x.LockoutEnabled && x.LockoutEnd > DateTimeOffset.UtcNow) + .WhereIf>(notActive.HasValue, x => !x.IsActive) + .WhereIf>(emailConfirmed.HasValue, x => x.EmailConfirmed) + .WhereIf>(isExternal.HasValue, x => x.IsExternal) + .WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) + .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) + .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .OrderBy(sorting.IsNullOrWhiteSpace() ? nameof(IdentityUser.UserName) : sorting) .As>() .PageBy>(skipCount, maxResultCount) @@ -211,8 +227,16 @@ public class MongoIdentityUserRepository : MongoDbRepository>(!string.IsNullOrWhiteSpace(userName), x => x.UserName == userName) .WhereIf>(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) .WhereIf>(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) - .WhereIf>(isLockedOut == true, x => x.LockoutEnabled && x.LockoutEnd > DateTimeOffset.UtcNow) - .WhereIf>(notActive == true, x => !x.IsActive) + .WhereIf>(!string.IsNullOrWhiteSpace(name), x => x.Name == name) + .WhereIf>(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) + .WhereIf>(isLockedOut.HasValue, x => x.LockoutEnabled && x.LockoutEnd > DateTimeOffset.UtcNow) + .WhereIf>(notActive.HasValue, x => !x.IsActive) + .WhereIf>(emailConfirmed.HasValue, x => x.EmailConfirmed) + .WhereIf>(isExternal.HasValue, x => x.IsExternal) + .WhereIf>(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) + .WhereIf>(minCreationTime != null, p => p.CreationTime >= minCreationTime) + .WhereIf>(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) + .WhereIf>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .LongCountAsync(GetCancellationToken(cancellationToken)); } @@ -272,9 +304,9 @@ public class MongoIdentityUserRepository : MongoDbRepository FindByTenantIdAndUserNameAsync( - [NotNull] string userName, - Guid? tenantId, - bool includeDetails = true, + [NotNull] string userName, + Guid? tenantId, + bool includeDetails = true, CancellationToken cancellationToken = default) { return await (await GetMongoQueryableAsync(cancellationToken)) diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/UserInfoController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/UserInfoController.cs index 74fc01c89f..418b361bda 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/UserInfoController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/UserInfoController.cs @@ -44,6 +44,7 @@ public class UserInfoController : AbpOpenIdDictControllerBase claims[AbpClaimTypes.TenantId] = user.TenantId; claims[OpenIddictConstants.Claims.PreferredUsername] = user.UserName; claims[OpenIddictConstants.Claims.FamilyName] = user.Surname; + claims[OpenIddictConstants.Claims.GivenName] = user.Name; } if (User.HasScope(OpenIddictConstants.Scopes.Email)) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/AbpPermissionManagementBlazorModule.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/AbpPermissionManagementBlazorModule.cs index fbcb7f26a1..1e7e69d4c3 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/AbpPermissionManagementBlazorModule.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/AbpPermissionManagementBlazorModule.cs @@ -1,6 +1,9 @@ -using Volo.Abp.AspNetCore.Components.Web.Theming; +using Localization.Resources.AbpUi; +using Volo.Abp.AspNetCore.Components.Web.Theming; using Volo.Abp.AutoMapper; +using Volo.Abp.Localization; using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.Localization; namespace Volo.Abp.PermissionManagement.Blazor; @@ -11,5 +14,15 @@ namespace Volo.Abp.PermissionManagement.Blazor; )] public class AbpPermissionManagementBlazorModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpUiResource) + ); + }); + } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/AbpSettingManagementBlazorModule.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/AbpSettingManagementBlazorModule.cs index 8888df3596..533e272198 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/AbpSettingManagementBlazorModule.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Blazor/AbpSettingManagementBlazorModule.cs @@ -1,10 +1,13 @@ -using Microsoft.Extensions.DependencyInjection; +using Localization.Resources.AbpUi; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Web.Theming; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; using Volo.Abp.AutoMapper; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.SettingManagement.Blazor.Menus; using Volo.Abp.SettingManagement.Blazor.Settings; +using Volo.Abp.SettingManagement.Localization; using Volo.Abp.UI.Navigation; namespace Volo.Abp.SettingManagement.Blazor; @@ -39,5 +42,14 @@ public class AbpSettingManagementBlazorModule : AbpModule { options.Contributors.Add(new EmailingPageContributor()); }); + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpUiResource) + ); + }); } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/AbpTenantManagementBlazorModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/AbpTenantManagementBlazorModule.cs index 0296e377bb..0433f2e33b 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/AbpTenantManagementBlazorModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Blazor/AbpTenantManagementBlazorModule.cs @@ -1,11 +1,15 @@ -using Microsoft.Extensions.DependencyInjection; +using Localization.Resources.AbpUi; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement.Blazor; +using Volo.Abp.FeatureManagement.Localization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending.Modularity; using Volo.Abp.TenantManagement.Blazor.Navigation; +using Volo.Abp.TenantManagement.Localization; using Volo.Abp.Threading; using Volo.Abp.UI.Navigation; @@ -38,6 +42,15 @@ public class AbpTenantManagementBlazorModule : AbpModule { options.AdditionalAssemblies.Add(typeof(AbpTenantManagementBlazorModule).Assembly); }); + + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpFeatureManagementResource), + typeof(AbpUiResource)); + }); } public override void PostConfigureServices(ServiceConfigurationContext context) diff --git a/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts b/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts index 54b1770ac5..32e5b1e7ab 100644 --- a/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/personal-settings/personal-settings.component.ts @@ -36,7 +36,6 @@ export class PersonalSettingsComponent form: UntypedFormGroup; inProgress: boolean; - private profile: ProfileDto; constructor( private fb: UntypedFormBuilder, @@ -79,24 +78,12 @@ export class PersonalSettingsComponent }); } - isDataSame(oldValue, newValue) { - return Object.entries(oldValue).some(([key, value]) => { - if (key in newValue) { - return value !== newValue[key]; - } - return false; - }); - } - logoutConfirmation = () => { this.authService.logout().subscribe(); }; private isLogoutConfirmMessageActive() { - if (!this.isPersonalSettingsChangedConfirmationActive) { - return false; - } - return this.isDataSame(this.profile, this.form.value); + return this.isPersonalSettingsChangedConfirmationActive; } private showLogoutConfirmMessage() { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss index 64bf8a6068..192af4afdb 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.scss @@ -47,6 +47,9 @@ $toastClass: abp-toast; } .#{$toastClass}-content { position: relative; + display: flex; + align-self: center; + word-break: break-word; .#{$toastClass}-close-button { position: absolute; top: 0; diff --git a/npm/packs/core/src/abp.js b/npm/packs/core/src/abp.js index 5a2747a3c7..7a82f58e98 100644 --- a/npm/packs/core/src/abp.js +++ b/npm/packs/core/src/abp.js @@ -135,14 +135,20 @@ var abp = abp || {}; if (sourceName === '_') { //A convention to suppress the localization return key; } + + if (sourceName) { + return abp.localization.internal.localize.apply(this, arguments).value; + } - sourceName = sourceName || abp.localization.defaultResourceName; - if (!sourceName) { + if (!abp.localization.defaultResourceName) { abp.log.warn('Localization source name is not specified and the defaultResourceName was not defined!'); return key; } - return abp.localization.internal.localize.apply(this, arguments).value; + var copiedArguments = Array.prototype.slice.call(arguments, 0); + copiedArguments.splice(1, 1, abp.localization.defaultResourceName); + + return abp.localization.internal.localize.apply(this, copiedArguments).value; }; abp.localization.isLocalized = function (key, sourceName) { diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Data/MyProjectNameDbMigrationService.cs index 1d598a1ce9..54a179ea61 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/Data/MyProjectNameDbMigrationService.cs @@ -70,13 +70,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj index 22d4f51fbf..7f4f101afa 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyCompanyName.MyProjectName.Blazor.Server.Mongo.csproj @@ -2,13 +2,14 @@ net7.0 + enable enable true - - + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs index f234a8b833..e088b4a84f 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/MyProjectNameModule.cs @@ -164,7 +164,7 @@ public class MyProjectNameModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); }); } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbMigrationService.cs index eeffd14d64..352c55ef26 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/Data/MyProjectNameDbMigrationService.cs @@ -79,13 +79,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); @@ -184,15 +184,15 @@ public class MyProjectNameDbMigrationService : ITransientDependency return Path.Combine(slnDirectoryPath, "MyCompanyName.MyProjectName.Blazor.Server"); } - private string GetSolutionDirectoryPath() + private string? GetSolutionDirectoryPath() { var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); - while (Directory.GetParent(currentDirectory.FullName) != null) + while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null) { currentDirectory = Directory.GetParent(currentDirectory.FullName); - if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) + if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) { return currentDirectory.FullName; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index c840b24cbb..6161a2f87d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -2,13 +2,14 @@ net7.0 + enable enable true - - + + diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs index 6defe2cc2c..b0953ae2eb 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameModule.cs @@ -166,7 +166,7 @@ public class MyProjectNameModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); }); } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/MyProjectNameDbMigrationService.cs index 1d598a1ce9..54a179ea61 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/MyProjectNameDbMigrationService.cs @@ -70,13 +70,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/OpenIddictDataSeedContributor.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/OpenIddictDataSeedContributor.cs index 429a83d562..8807969649 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/OpenIddictDataSeedContributor.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/Data/OpenIddictDataSeedContributor.cs @@ -78,7 +78,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep { var webClientRootUrl = configurationSection["MyProjectName_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: consoleAndAngularClientId, + name: consoleAndAngularClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Console Test / Angular Application", @@ -100,10 +100,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var swaggerClientId = configurationSection["MyProjectName_Swagger:ClientId"]; if (!swaggerClientId.IsNullOrWhiteSpace()) { - var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"].TrimEnd('/'); + var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: swaggerClientId, + name: swaggerClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Swagger Application", @@ -123,12 +123,12 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep [NotNull] string type, [NotNull] string consentType, string displayName, - string secret, + string? secret, List grantTypes, List scopes, - string redirectUri = null, - string postLogoutRedirectUri = null, - List permissions = null) + string? redirectUri = null, + string? postLogoutRedirectUri = null, + List? permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj index 60712105a2..62eda99090 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyCompanyName.MyProjectName.Host.Mongo.csproj @@ -2,6 +2,7 @@ net7.0 + enable enable true diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs index 45e5006b8a..aa072a238e 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/MyProjectNameModule.cs @@ -171,7 +171,7 @@ public class MyProjectNameModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; @@ -275,10 +275,10 @@ public class MyProjectNameModule : AbpModule { builder .WithOrigins( - configuration["App:CorsOrigins"] + configuration["App:CorsOrigins"]? .Split(",", StringSplitOptions.RemoveEmptyEntries) .Select(o => o.RemovePostFix("/")) - .ToArray() + .ToArray() ?? Array.Empty() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbMigrationService.cs index b0b929dcc9..83d4f3dcbb 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/MyProjectNameDbMigrationService.cs @@ -79,13 +79,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); @@ -184,15 +184,15 @@ public class MyProjectNameDbMigrationService : ITransientDependency return Path.Combine(slnDirectoryPath, "MyCompanyName.MyProjectName.Host"); } - private string GetSolutionDirectoryPath() + private string? GetSolutionDirectoryPath() { var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); - while (Directory.GetParent(currentDirectory.FullName) != null) + while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null) { currentDirectory = Directory.GetParent(currentDirectory.FullName); - if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) + if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) { return currentDirectory.FullName; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/OpenIddictDataSeedContributor.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/OpenIddictDataSeedContributor.cs index 429a83d562..8807969649 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/OpenIddictDataSeedContributor.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/Data/OpenIddictDataSeedContributor.cs @@ -78,7 +78,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep { var webClientRootUrl = configurationSection["MyProjectName_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: consoleAndAngularClientId, + name: consoleAndAngularClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Console Test / Angular Application", @@ -100,10 +100,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var swaggerClientId = configurationSection["MyProjectName_Swagger:ClientId"]; if (!swaggerClientId.IsNullOrWhiteSpace()) { - var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"].TrimEnd('/'); + var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: swaggerClientId, + name: swaggerClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Swagger Application", @@ -123,12 +123,12 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep [NotNull] string type, [NotNull] string consentType, string displayName, - string secret, + string? secret, List grantTypes, List scopes, - string redirectUri = null, - string postLogoutRedirectUri = null, - List permissions = null) + string? redirectUri = null, + string? postLogoutRedirectUri = null, + List? permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj index afb804eae6..68cdc93feb 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyCompanyName.MyProjectName.Host.csproj @@ -2,6 +2,7 @@ net7.0 + enable enable true diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs index 16ef3cf47b..bbc6b36262 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/MyProjectNameModule.cs @@ -174,7 +174,7 @@ public class MyProjectNameModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; @@ -278,10 +278,10 @@ public class MyProjectNameModule : AbpModule { builder .WithOrigins( - configuration["App:CorsOrigins"] + configuration["App:CorsOrigins"]? .Split(",", StringSplitOptions.RemoveEmptyEntries) .Select(o => o.RemovePostFix("/")) - .ToArray() + .ToArray() ?? Array.Empty() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Data/MyProjectNameDbMigrationService.cs index 1d598a1ce9..54a179ea61 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/Data/MyProjectNameDbMigrationService.cs @@ -70,13 +70,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj index 5f99ac74ae..a203e9e1cf 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/MyCompanyName.MyProjectName.Mvc.Mongo.csproj @@ -2,6 +2,7 @@ net7.0 + enable enable true diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbMigrationService.cs b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbMigrationService.cs index 14db7d1e8d..d51d346284 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/Data/MyProjectNameDbMigrationService.cs @@ -79,13 +79,13 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + 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) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); @@ -184,15 +184,15 @@ public class MyProjectNameDbMigrationService : ITransientDependency return Path.Combine(slnDirectoryPath, "MyCompanyName.MyProjectName.Mvc"); } - private string GetSolutionDirectoryPath() + private string? GetSolutionDirectoryPath() { var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); - while (Directory.GetParent(currentDirectory.FullName) != null) + while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null) { currentDirectory = Directory.GetParent(currentDirectory.FullName); - if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) + if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) { return currentDirectory.FullName; } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj index c848ad3cf6..21cdc1b0db 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/MyCompanyName.MyProjectName.Mvc.csproj @@ -2,6 +2,7 @@ net7.0 + enable enable true diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj index 1e50cf8678..6029c23a7f 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj index 8037a452b4..a9c83b797b 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj index 21fb37e73e..70a55520a0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs index fb963a81c7..c8f1c89424 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs @@ -114,7 +114,7 @@ public class MyProjectNameAuthServerModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; @@ -150,10 +150,10 @@ public class MyProjectNameAuthServerModule : AbpModule { builder .WithOrigins( - configuration["App:CorsOrigins"] + configuration["App:CorsOrigins"]? .Split(",", StringSplitOptions.RemoveEmptyEntries) .Select(o => o.RemovePostFix("/")) - .ToArray() + .ToArray() ?? Array.Empty() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml index 48b2d8de02..e45ec1b8f1 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml @@ -19,10 +19,8 @@
- -
\ No newline at end of file +
diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs index b700bdd9bb..d734388689 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/Pages/Index.cshtml.cs @@ -9,11 +9,11 @@ namespace MyCompanyName.MyProjectName.Pages; public class IndexModel : AbpPageModel { - public List Applications { get; protected set; } + public List? Applications { get; protected set; } - public IReadOnlyList Languages { get; protected set; } + public IReadOnlyList? Languages { get; protected set; } - public string CurrentLanguage { get; protected set; } + public string? CurrentLanguage { get; protected set; } protected IOpenIddictApplicationRepository OpenIdApplicationRepository { get; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj index 0573ce737f..b67ef63875 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/MyCompanyName.MyProjectName.Blazor.Server.Tiered.csproj @@ -4,6 +4,7 @@ net7.0 + enable true true true @@ -13,8 +14,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index e1c5a68a56..01766b2110 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -4,6 +4,7 @@ net7.0 + enable true true true @@ -13,8 +14,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs index b4f2e723e3..758b60f74e 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyProjectNameBlazorModule.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using Blazorise.Bootstrap5; using Blazorise.Icons.FontAwesome; @@ -111,7 +112,7 @@ public class MyProjectNameBlazorModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); }); } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj index 923d27c148..481062bd15 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj @@ -4,6 +4,7 @@ net7.0 + enable true service-worker-assets.js @@ -11,8 +12,8 @@ - - + + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/index.html b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/index.html index 5a6bb01acf..86839294ad 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/index.html +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/wwwroot/index.html @@ -8,7 +8,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj index af519e222a..3c581e3947 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj @@ -5,6 +5,7 @@ Exe net7.0 + enable diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj index cb7b4cd5bf..d90c6e0a7c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName true diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs index dd0e61135d..16ef00af77 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/Data/MyProjectNameDbMigrationService.cs @@ -87,7 +87,7 @@ public class MyProjectNameDbMigrationService : ITransientDependency Logger.LogInformation("You can safely end this process..."); } - private async Task MigrateDatabaseSchemaAsync(Tenant tenant = null) + private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null) { Logger.LogInformation( $"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database..."); @@ -98,7 +98,7 @@ public class MyProjectNameDbMigrationService : ITransientDependency } } - private async Task SeedDataAsync(Tenant tenant = null) + private async Task SeedDataAsync(Tenant? tenant = null) { Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); @@ -152,8 +152,7 @@ public class MyProjectNameDbMigrationService : ITransientDependency private bool MigrationsFolderExists() { var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath(); - - return Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations")); + return dbMigrationsProjectFolder != null && Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations")); } private void AddInitialMigration() @@ -188,7 +187,7 @@ public class MyProjectNameDbMigrationService : ITransientDependency } } - private string GetEntityFrameworkCoreProjectFolderPath() + private string? GetEntityFrameworkCoreProjectFolderPath() { var slnDirectoryPath = GetSolutionDirectoryPath(); @@ -203,15 +202,15 @@ public class MyProjectNameDbMigrationService : ITransientDependency .FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore")); } - private string GetSolutionDirectoryPath() + private string? GetSolutionDirectoryPath() { var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()); - while (Directory.GetParent(currentDirectory.FullName) != null) + while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null) { currentDirectory = Directory.GetParent(currentDirectory.FullName); - if (Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) + if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null) { return currentDirectory.FullName; } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 16f823b3ba..61f9ea3e8d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs index 804e15d0f2..7a8b8f7bf0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/OpenIddict/OpenIddictDataSeedContributor.cs @@ -87,7 +87,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep /* MyProjectName_Web client is only needed if you created a tiered * solution. Otherwise, you can delete this client. */ await CreateApplicationAsync( - name: webClientId, + name: webClientId!, type: OpenIddictConstants.ClientTypes.Confidential, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Web Application", @@ -110,7 +110,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep { var consoleAndAngularClientRootUrl = configurationSection["MyProjectName_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: consoleAndAngularClientId, + name: consoleAndAngularClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Console Test / Angular Application", @@ -133,10 +133,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var blazorClientId = configurationSection["MyProjectName_Blazor:ClientId"]; if (!blazorClientId.IsNullOrWhiteSpace()) { - var blazorRootUrl = configurationSection["MyProjectName_Blazor:RootUrl"].TrimEnd('/'); + var blazorRootUrl = configurationSection["MyProjectName_Blazor:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: blazorClientId, + name: blazorClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Blazor Application", @@ -159,7 +159,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var blazorServerTieredRootUrl = configurationSection["MyProjectName_BlazorServerTiered:RootUrl"].EnsureEndsWith('/'); await CreateApplicationAsync( - name: blazorServerTieredClientId, + name: blazorServerTieredClientId!, type: OpenIddictConstants.ClientTypes.Confidential, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Blazor Server Application", @@ -180,10 +180,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var swaggerClientId = configurationSection["MyProjectName_Swagger:ClientId"]; if (!swaggerClientId.IsNullOrWhiteSpace()) { - var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"].TrimEnd('/'); + var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: swaggerClientId, + name: swaggerClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Swagger Application", @@ -204,13 +204,13 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep [NotNull] string type, [NotNull] string consentType, string displayName, - string secret, + string? secret, List grantTypes, List scopes, - string clientUri = null, - string redirectUri = null, - string postLogoutRedirectUri = null, - List permissions = null) + string? clientUri = null, + string? redirectUri = null, + string? postLogoutRedirectUri = null, + List? permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index e8d5b7f7f3..36e3318b68 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj index b2bd230422..702510363b 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 540657184c..d2e831a9e0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-4681b4fd-151f-4221-84a4-929d86723e4c diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index 0c8ef2c82c..c848b1f774 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -155,12 +155,10 @@ public class MyProjectNameHttpApiHostModule : AbpModule options.AddDefaultPolicy(builder => { builder - .WithOrigins( - configuration["App:CorsOrigins"] - .Split(",", StringSplitOptions.RemoveEmptyEntries) - .Select(o => o.RemovePostFix("/")) - .ToArray() - ) + .WithOrigins(configuration["App:CorsOrigins"]? + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .Select(o => o.RemovePostFix("/")) + .ToArray() ?? Array.Empty()) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyHeader() diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj index 6e72b6879f..abc297b855 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyCompanyName.MyProjectName.HttpApi.HostWithIds.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-4681b4fd-151f-4221-84a4-929d86723e4c diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs index a88511db4e..dd79fb773c 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs @@ -95,7 +95,7 @@ public class MyProjectNameHttpApiHostModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); options.Applications["Angular"].RootUrl = configuration["App:ClientUrl"]; options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password"; @@ -155,14 +155,12 @@ public class MyProjectNameHttpApiHostModule : AbpModule context.Services.AddCors(options => { options.AddDefaultPolicy(builder => - { + { builder - .WithOrigins( - configuration["App:CorsOrigins"] - .Split(",", StringSplitOptions.RemoveEmptyEntries) - .Select(o => o.RemovePostFix("/")) - .ToArray() - ) + .WithOrigins(configuration["App:CorsOrigins"]? + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .Select(o => o.RemovePostFix("/")) + .ToArray() ?? Array.Empty()) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyHeader() diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj index 2a585f45a4..08243ccd22 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj index e9f07986e2..e39e4d8082 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index fe7c773451..4840959fcd 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName.Web $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs index 7a85f3ab7e..84f6b391b7 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Menus/MyProjectNameMenuContributor.cs @@ -18,7 +18,7 @@ public class MyProjectNameMenuContributor : IMenuContributor } } - private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + private Task ConfigureMainMenuAsync(MenuConfigurationContext context) { var administration = context.Menu.GetAdministration(); var l = context.GetLocalizer(); @@ -45,5 +45,7 @@ public class MyProjectNameMenuContributor : IMenuContributor administration.SetSubItemOrder(IdentityMenuNames.GroupName, 2); administration.SetSubItemOrder(SettingManagementMenuNames.GroupName, 3); + + return Task.CompletedTask; } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index 560ba570dd..90f7f56e0a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName.Web $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj index ee00e7f04c..12f36a7520 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj index defd452da5..27d5002e1b 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs index d68373b4fb..0a76f9928e 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs @@ -17,7 +17,7 @@ namespace MyCompanyName.MyProjectName.EntityFrameworkCore; )] public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule { - private SqliteConnection _sqliteConnection; + private SqliteConnection? _sqliteConnection; public override void ConfigureServices(ServiceConfigurationContext context) { @@ -39,7 +39,7 @@ public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule public override void OnApplicationShutdown(ApplicationShutdownContext context) { - _sqliteConnection.Dispose(); + _sqliteConnection?.Dispose(); } private static SqliteConnection CreateDatabaseAndGetConnection() diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj index 6806868b1c..7b13ac6079 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj index 14dd55a22e..d8eff36d42 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj @@ -3,6 +3,7 @@ Exe net7.0 + enable diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj index b9844d6d0f..642c72b84f 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj index 05c7539b52..2a8705b312 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index 717ba2c324..512430a84a 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -13,30 +13,13 @@ public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor return GetPrincipal(); } - private ClaimsPrincipal _principal; - private ClaimsPrincipal GetPrincipal() { - if (_principal == null) + return new ClaimsPrincipal(new ClaimsIdentity(new List { - lock (this) - { - if (_principal == null) - { - _principal = new ClaimsPrincipal( - new ClaimsIdentity( - new List - { - new Claim(AbpClaimTypes.UserId,"2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), - new Claim(AbpClaimTypes.UserName,"admin"), - new Claim(AbpClaimTypes.Email,"admin@abp.io") - } - ) - ); - } - } - } - - return _principal; + new Claim(AbpClaimTypes.UserId, "2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), + new Claim(AbpClaimTypes.UserName, "admin"), + new Claim(AbpClaimTypes.Email, "admin@abp.io") + })); } } diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj index 05193d0987..9bf9d3e1d7 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyCompanyName.MyProjectName.Web.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable Exe $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; MyCompanyName.MyProjectName diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs index ae94b2fe6d..9736848b6b 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/MyProjectNameWebTestBase.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; @@ -14,10 +15,10 @@ public abstract class MyProjectNameWebTestBase : AbpAspNetCoreIntegratedTestBase { return base .CreateHostBuilder() - .UseContentRoot(WebContentDirectoryFinder.CalculateContentRootFolder()); + .UseContentRoot(WebContentDirectoryFinder.CalculateContentRootFolder() ?? throw new InvalidOperationException()); } - protected virtual async Task GetResponseAsObjectAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) + protected virtual async Task GetResponseAsObjectAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) { var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode); return JsonSerializer.Deserialize(strResponse, new JsonSerializerOptions(JsonSerializerDefaults.Web)); diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/WebContentDirectoryFinder.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/WebContentDirectoryFinder.cs index 4c8c4bfd0c..f0a3149272 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/WebContentDirectoryFinder.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.Web.Tests/WebContentDirectoryFinder.cs @@ -11,7 +11,7 @@ namespace MyCompanyName.MyProjectName; /// public static class WebContentDirectoryFinder { - public static string CalculateContentRootFolder() + public static string? CalculateContentRootFolder() { var domainAssemblyDirectoryPath = Path.GetDirectoryName(typeof(MyProjectNameDomainModule).Assembly.Location); diff --git a/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index c25eb1af14..616d70bc3e 100644 --- a/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/console/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -5,6 +5,7 @@ Exe net7.0 + enable diff --git a/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index 205a22c2c9..b36be3895a 100644 --- a/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/maui/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -1,12 +1,13 @@  - + net7.0-android;net7.0-ios;net7.0-maccatalyst $(TargetFrameworks);net7.0-windows10.0.19041.0 + enable Exe MyCompanyName.MyProjectName true @@ -54,7 +55,7 @@ - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj index 13d7c366b4..022889cd58 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyCompanyName.MyProjectName.AuthServer.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-c2d31439-b723-48e2-b061-5ebd7aeb6010 diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs index 8d5967e361..5fef3d79a8 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/MyProjectNameAuthServerModule.cs @@ -175,10 +175,10 @@ public class MyProjectNameAuthServerModule : AbpModule { builder .WithOrigins( - configuration["App:CorsOrigins"] + configuration["App:CorsOrigins"]? .Split(",", StringSplitOptions.RemoveEmptyEntries) .Select(o => o.RemovePostFix("/")) - .ToArray() + .ToArray() ?? Array.Empty() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/OpenIddict/OpenIddictDataSeedContributor.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/OpenIddict/OpenIddictDataSeedContributor.cs index 7ecafc5f6a..dea53ea554 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/OpenIddict/OpenIddictDataSeedContributor.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/OpenIddict/OpenIddictDataSeedContributor.cs @@ -86,7 +86,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep /* MyProjectName_Web client is only needed if you created a tiered * solution. Otherwise, you can delete this client. */ await CreateApplicationAsync( - name: webClientId, + name: webClientId!, type: OpenIddictConstants.ClientTypes.Confidential, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Web Application", @@ -108,7 +108,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep { var consoleAndAngularClientRootUrl = configurationSection["MyProjectName_App:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: consoleAndAngularClientId, + name: consoleAndAngularClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Console Test / Angular Application", @@ -130,10 +130,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var blazorClientId = configurationSection["MyProjectName_Blazor:ClientId"]; if (!blazorClientId.IsNullOrWhiteSpace()) { - var blazorRootUrl = configurationSection["MyProjectName_Blazor:RootUrl"].TrimEnd('/'); + var blazorRootUrl = configurationSection["MyProjectName_Blazor:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: blazorClientId, + name: blazorClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Blazor Application", @@ -152,10 +152,10 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep var swaggerClientId = configurationSection["MyProjectName_Swagger:ClientId"]; if (!swaggerClientId.IsNullOrWhiteSpace()) { - var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"].TrimEnd('/'); + var swaggerRootUrl = configurationSection["MyProjectName_Swagger:RootUrl"]?.TrimEnd('/'); await CreateApplicationAsync( - name: swaggerClientId, + name: swaggerClientId!, type: OpenIddictConstants.ClientTypes.Public, consentType: OpenIddictConstants.ConsentTypes.Implicit, displayName: "Swagger Application", @@ -175,12 +175,12 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep [NotNull] string type, [NotNull] string consentType, string displayName, - string secret, + string? secret, List grantTypes, List scopes, - string redirectUri = null, - string postLogoutRedirectUri = null, - List permissions = null) + string? redirectUri = null, + string? postLogoutRedirectUri = null, + List? permissions = null) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj index 04772fc8e8..89d0af9e3c 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/MyCompanyName.MyProjectName.Blazor.Host.csproj @@ -4,12 +4,13 @@ net7.0 + enable true - - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/Pages/Index.razor b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/Pages/Index.razor index 1140cddeff..cddd608f45 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/Pages/Index.razor +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/Pages/Index.razor @@ -47,12 +47,12 @@ @code { - private IEnumerable _claims; + private IEnumerable? _claims; - protected async override Task OnInitializedAsync() + protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - if (authState.User.Identity.IsAuthenticated) + if (authState.User.Identity != null && authState.User.Identity.IsAuthenticated) { _claims = authState.User.Claims; } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/wwwroot/index.html b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/wwwroot/index.html index 1c7a8ec1ae..0ba799d6a9 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/wwwroot/index.html +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Host/wwwroot/index.html @@ -8,7 +8,7 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj index 4cb1ef18df..ce942ef18c 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyCompanyName.MyProjectName.Blazor.Server.Host.csproj @@ -4,6 +4,7 @@ net7.0 + enable true true true @@ -12,8 +13,8 @@ - - + + diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs index 46bb89e1b8..1dc8d7d14d 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/MyProjectNameBlazorHostModule.cs @@ -209,7 +209,7 @@ public class MyProjectNameBlazorHostModule : AbpModule Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty()); }); #if DEBUG @@ -217,7 +217,7 @@ public class MyProjectNameBlazorHostModule : AbpModule #endif } - public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) + public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) { var env = context.GetEnvironment(); var app = context.GetApplicationBuilder(); diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Pages/Index.razor b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Pages/Index.razor index 0c9af64456..a7aebf6ded 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Pages/Index.razor +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/Pages/Index.razor @@ -1,8 +1,5 @@ @page "/" -@using Volo.Abp.MultiTenancy @inherits MyProjectNameComponentBase -@inject ICurrentTenant CurrentTenant -@inject AuthenticationStateProvider AuthenticationStateProvider
diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj index 8cb063a10b..0cd160826b 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj index 34f688c58c..ebe9f5c565 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-c2d31439-b723-48e2-b061-5ebd7aeb6010 diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs index a736a9ca0f..bc1ec583b6 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -143,10 +143,10 @@ public class MyProjectNameHttpApiHostModule : AbpModule { builder .WithOrigins( - configuration["App:CorsOrigins"] + configuration["App:CorsOrigins"]? .Split(",", StringSplitOptions.RemoveEmptyEntries) .Select(o => o.RemovePostFix("/")) - .ToArray() + .ToArray() ?? Array.Empty() ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj index f0eb77f53f..efb9ebd119 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/MyCompanyName.MyProjectName.Web.Host.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-c2d31439-b723-48e2-b061-5ebd7aeb6010 diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj index 1a37ef59ea..2d0959a426 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/MyCompanyName.MyProjectName.Web.Unified.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName true MyCompanyName.MyProjectName-c2d31439-b723-48e2-b061-5ebd7aeb6010 diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj index c572d2bee1..341d5640d8 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj index 82e5ec1a94..ddf2e84283 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj index fb7721dabc..828dbeda41 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/MyCompanyName.MyProjectName.Blazor.Server.csproj @@ -4,6 +4,7 @@ net7.0 + enable diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebAssembly/MyCompanyName.MyProjectName.Blazor.WebAssembly.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebAssembly/MyCompanyName.MyProjectName.Blazor.WebAssembly.csproj index 7a47626fc9..e89a3d035c 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebAssembly/MyCompanyName.MyProjectName.Blazor.WebAssembly.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebAssembly/MyCompanyName.MyProjectName.Blazor.WebAssembly.csproj @@ -4,8 +4,9 @@ net7.0 + enable - + diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj index c5bf783de3..7ac06b0881 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/MyCompanyName.MyProjectName.Blazor.csproj @@ -4,6 +4,7 @@ net7.0 + enable diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj index 70f7079e3f..619813ab46 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName true diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 2902edbe3d..aa33eecfda 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDbProperties.cs b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDbProperties.cs index 2ac3e3f9e3..1fa7ecaf35 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDbProperties.cs +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyProjectNameDbProperties.cs @@ -4,7 +4,7 @@ public static class MyProjectNameDbProperties { public static string DbTablePrefix { get; set; } = "MyProjectName"; - public static string DbSchema { get; set; } = null; + public static string? DbSchema { get; set; } = null; public const string ConnectionStringName = "MyProjectName"; } diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj index 5c50aed0f2..a1511f1a51 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj index 8486d3f247..a05ea0e71a 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj index 411049eb72..9edd4d08cc 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Installer/MyCompanyName.MyProjectName.Installer.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Installer/MyCompanyName.MyProjectName.Installer.csproj index 5dcb0e44df..64f9efa66d 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Installer/MyCompanyName.MyProjectName.Installer.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Installer/MyCompanyName.MyProjectName.Installer.csproj @@ -4,6 +4,7 @@ net7.0 + enable true MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj index 654c7a14f3..95f1137ebb 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj @@ -4,6 +4,7 @@ netstandard2.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj index ea75e4e731..8985ad3713 100644 --- a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj @@ -4,6 +4,7 @@ net7.0 + enable $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true Library diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj index ae01d6eff1..d472d6a29f 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj index 5edd4ddc4f..c13f03f701 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/Samples/SampleManager_Tests.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/Samples/SampleManager_Tests.cs index 24254b2628..d9c35ff3f9 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/Samples/SampleManager_Tests.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.Domain.Tests/Samples/SampleManager_Tests.cs @@ -13,8 +13,8 @@ public class SampleManager_Tests : MyProjectNameDomainTestBase } [Fact] - public async Task Method1Async() + public Task Method1Async() { - + return Task.CompletedTask; } } diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj index 69baaac85e..e4f7896f97 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj index 3c75dcf3c9..450625e048 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj @@ -3,6 +3,7 @@ Exe net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj index 1dffc4d80f..ef1b43437b 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj index 0ec8bf147b..9cef5098e7 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj @@ -4,6 +4,7 @@ net7.0 + enable MyCompanyName.MyProjectName diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Samples/SampleRepository_Tests.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Samples/SampleRepository_Tests.cs index 738c202b90..acfc4f7139 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Samples/SampleRepository_Tests.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Samples/SampleRepository_Tests.cs @@ -19,8 +19,8 @@ public abstract class SampleRepository_Tests : MyProjectNameTest } [Fact] - public async Task Method1Async() + public Task Method1Async() { - + return Task.CompletedTask; } } diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs index 717ba2c324..45db4c6afe 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -13,30 +13,15 @@ public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor return GetPrincipal(); } - private ClaimsPrincipal _principal; - private ClaimsPrincipal GetPrincipal() { - if (_principal == null) - { - lock (this) - { - if (_principal == null) + return new ClaimsPrincipal(new ClaimsIdentity(new List { - _principal = new ClaimsPrincipal( - new ClaimsIdentity( - new List - { - new Claim(AbpClaimTypes.UserId,"2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), - new Claim(AbpClaimTypes.UserName,"admin"), - new Claim(AbpClaimTypes.Email,"admin@abp.io") - } - ) - ); + new Claim(AbpClaimTypes.UserId, "2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), + new Claim(AbpClaimTypes.UserName, "admin"), + new Claim(AbpClaimTypes.Email, "admin@abp.io") } - } - } - - return _principal; + ) + ); } } diff --git a/templates/wpf/src/MyCompanyName.MyProjectName/App.xaml.cs b/templates/wpf/src/MyCompanyName.MyProjectName/App.xaml.cs index 21124d6400..c089f1f00a 100644 --- a/templates/wpf/src/MyCompanyName.MyProjectName/App.xaml.cs +++ b/templates/wpf/src/MyCompanyName.MyProjectName/App.xaml.cs @@ -12,9 +12,9 @@ namespace MyCompanyName.MyProjectName; /// public partial class App : Application { - private IAbpApplicationWithInternalServiceProvider _abpApplication; + private IAbpApplicationWithInternalServiceProvider? _abpApplication; - protected async override void OnStartup(StartupEventArgs e) + protected override async void OnStartup(StartupEventArgs e) { Log.Logger = new LoggerConfiguration() #if DEBUG @@ -48,9 +48,12 @@ public partial class App : Application } } - protected async override void OnExit(ExitEventArgs e) + protected override async void OnExit(ExitEventArgs e) { - await _abpApplication.ShutdownAsync(); + if (_abpApplication != null) + { + await _abpApplication.ShutdownAsync(); + } Log.CloseAndFlush(); } } diff --git a/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj b/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj index 0f3b9ee973..4329eec058 100644 --- a/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj +++ b/templates/wpf/src/MyCompanyName.MyProjectName/MyCompanyName.MyProjectName.csproj @@ -5,6 +5,7 @@ WinExe net7.0-windows + enable true