diff --git a/Directory.Packages.props b/Directory.Packages.props
index 91574ec319..67c100c20b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -138,8 +138,8 @@
-
-
+
+
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
index 69796af696..faf7c0b36d 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json
@@ -223,6 +223,12 @@
"AbpStudio": "ABP Studio",
"ExtraMonths": "{0}EXTRA MONTHS",
"RSS": "RSS",
- "RSSFeed": "RSS Feed"
+ "RSSFeed": "RSS Feed",
+ "Articles": "Articles",
+ "Organizations": "Organizations",
+ "ManageAccount": "Manage Account",
+ "CommunityProfile": "Community Profile",
+ "BlogProfile": "Blog Profile",
+ "Tickets": "Tickets"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
index b14d1ffa70..92d7450319 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json
@@ -601,7 +601,7 @@
"PaymentSucceed_ViewOrganization": "Click here to view organization",
"Purchase_TotalAnnualPrice": "TOTAL (annual fee)",
"Purchase_TrainingPrice": "Training Price",
- "Purchase_OnboardingTraining": "ABP Onboarding & Web Application Development Live Training",
+ "Purchase_OnboardingTraining": "Onboarding & Web Application Development Live Training",
"TotalDeveloperPrice": "Total Developer Price",
"Purchase_PricePerDeveloper": "{0} {1} per developer",
"Purchase_IncludedDeveloperInfo": "{0} {1} included.",
@@ -978,10 +978,10 @@
"Purchase_DevelopersAlreadyIncluded": "{0} developers already included",
"1Year": "1 year",
"{0}Years": "{0} years",
- "1YearLicense": "1 year license",
- "{0}YearsLicense": "{0} years license",
- "1AdditionalDeveloper": "1 additional developer",
- "{0}AdditionalDevelopers": "{0} additional developers",
+ "1YearLicense": "1 Year License",
+ "{0}YearsLicense": "{0} Years License",
+ "1AdditionalDeveloper": "1 Additional Developer",
+ "{0}AdditionalDevelopers": "{0} Additional Developers",
"Discount": "Discount ({0}%)",
"Summary": "Summary",
"TrainingPack": "Training pack",
@@ -1081,6 +1081,10 @@
"CampaignInfo": "Buy a new license or renew your existing license and get an additional 2 months at no additional cost! This offer is valid for all license plans. Ensure you take advantage of this limited-time promotion to expand your access to premium features and upgrades.",
"HurryUpLastDay": "Hurry Up! Last Day: {0}",
"CreatingCRUDPagesWithABPSuite": "Creating CRUD pages with ABP Suite",
- "Testimonials": "Testimonials"
+ "Testimonials": "Testimonials",
+ "MultipleYearDiscount": "Multiple Year Discount",
+ "CampaignDiscountText": "Black Friday Discount",
+ "CampaignDiscountName": "Black Friday",
+ "CampaignName:BlackFriday": "Black Friday"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
index 3c8c8ac397..3889b5ec77 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
@@ -248,6 +248,10 @@
"ShareOnTwitter": "Share on Twitter",
"ShareOnLinkedIn": "Share on LinkedIn",
"MoreFrom{0}": "More from {0}",
- "SeeAllFrom{0}": "See all from {0}"
+ "SeeAllFrom{0}": "See all from {0}",
+ "MostWatched": "Most Watched",
+ "Articles({0})": "Articles ({0})",
+ "Videos({0})": "Videos ({0})",
+ "LatestArticles": "Latest Articles"
}
}
diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
index 0fff7ed719..7b0394bac5 100644
--- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
+++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
@@ -433,6 +433,10 @@
"PackageDetailPage_SeeDocumentation": "See the documentation to learn how to use this package in your applications.",
"PackageDetailPage_InstallingUsingPMC": "3: Installing with the Package Manager Console",
"PackageDetailPage_InstallingUsingPMCDescription1": "Open the Package Manager Console in Visual Studio (Tools -> Nuget Package Manager -> Package Manager Console) and execute the following command",
- "UIOptions": "UI Options"
+ "UIOptions": "UI Options",
+ "Testimonials": "Testimonials",
+ "CoolestCompaniesUseABPFramework": "Coolest Companies Use ABP Framework",
+ "Index_Page_Testimonial_1": "ABP Framework is not just a tool but a catalyst that has accelerated my growth as a developer. It has made it possible for me to build new features faster than ever before, reminiscent of the experiences of other users. The unified coding pattern has streamlined my projects, giving me more time to focus on creating rather than troubleshooting.\nI would say the ABP Framework has been the cornerstone of my early professional journey. It has facilitated my transition from an aspiring developer to a confident professional ready to make a mark in the software world. I am looking forward to the exciting projects that await me, knowing that ABP will be there to guide me. It is more than just a product; it's a partner in success.",
+ "Index_Page_Testimonial_2": "ABP Framework is not only a framework, it is also a guidance for project development/management, because it provides DDD, GenericRepository, DI, Microservice, Modularity trainings. Even if you are not going to use framework itself, you can develop yourself with docs.abp.io which is well and professionally prepared. (OpenIddict, Redis, Quartz etc.)\nBecause many thing pre-built, it shortens project development time significantly. (Such as login page, exception handling, data filtering-seeding, audit logging, localization, auto api controller etc.)\nAs an example from our app, i have used Local Event Bus for stock control. So, I am able to manage order movements by writing stock handler.\nIt is wonderful not to lose time for CreationTime, CreatorId. They are filled automatically."
}
}
diff --git a/docs/en/Community-Articles/2023-04-15-Converting-Create-Edit-Modal-To-Page/POST.md b/docs/en/Community-Articles/2023-04-15-Converting-Create-Edit-Modal-To-Page/POST.md
index a8f1aeadfa..73d2e6bad5 100644
--- a/docs/en/Community-Articles/2023-04-15-Converting-Create-Edit-Modal-To-Page/POST.md
+++ b/docs/en/Community-Articles/2023-04-15-Converting-Create-Edit-Modal-To-Page/POST.md
@@ -315,7 +315,7 @@ import { CreateBookComponent } from './create-book/create-book.component';
import { EditBookComponent } from './edit-book/edit-book.component';
const routes: Routes = [
- { path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] },
+ { path: '', component: BookComponent, canActivate: [authGuard, permissionGuard] },
{ path: 'create', component: CreateBookComponent },
{ path: 'edit/:id', component: EditBookComponent },
];
diff --git a/docs/en/Community-Articles/2023-10-23-NET-8-Feature-containers/POST.md b/docs/en/Community-Articles/2023-10-23-NET-8-Feature-containers/POST.md
new file mode 100644
index 0000000000..dc0f96e726
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-23-NET-8-Feature-containers/POST.md
@@ -0,0 +1,30 @@
+# New Containers feature with NET 8.0
+
+This article will show you the new feature of containers with NET 8.0.
+
+## Non-root user
+
+The `Non-root user` feature on net 8 is a security measure that allows users to have limited access to the system without having full administrative privileges. Hosting containers as `non-root` aligns with the principle of least privilege.
+It’s free security provided by the operating system. If you run your app as root, your app process can do anything in the container, like modify files, install packages, or run arbitrary executables.
+That’s a concern if your app is ever attacked. If you run your app as non-root, your app process cannot do much, greatly limiting what a bad actor could accomplish.
+
+## Default ASP.NET Core port changed from 80 to 8080
+
+In .NET 8, there has been a change in the default port used by ASP.NET Core applications. Previously, the default port assigned to ASP.NET Core applications was `80`. However, starting from .NET 8, the default port has been changed to `8080`.
+This change was made to avoid conflicts with other applications and services that commonly use port 80, such as web servers like IIS or Apache. By using port 8080 as the default, there is less potential for clashes and easier deployment of ASP.NET Core applications alongside other services.
+
+It's important to note that this change only affects the default port used when an ASP.NET Core application is run without explicitly specifying a port.
+
+If you want your application to continue using port 80, you can still specify it during the application launch or configure it in the application settings.
+
+* Recommended: Explicitly set the `ASPNETCORE_HTTP_PORTS`, `ASPNETCORE_HTTPS_PORTS``, and `ASPNETCORE_URLS` environment variables to the desired port. Example: `docker run --rm -it -p 9999:80 -e ASPNETCORE_HTTP_PORTS=80 ``
+* Update existing commands and configuration that rely on the expected default port of port 80 to reference port 8080 instead. Example: `docker run --rm -it -p 9999:8080 ``
+
+> The `dockerfile` of ABP templates has been updated to use port `80`.
+
+## References
+
+- [Secure your .NET cloud apps with rootless Linux Containers](https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/)
+- [Containers breaking changes](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#containers)
+- [ASP.NET Core apps use port 8080 by default](https://learn.microsoft.com/en-us/dotnet/core/compatibility/8.0#containers)
+- [Docker images for ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-8.0)
diff --git a/docs/en/Community-Articles/2023-10-23-NET-8-Feature-raw-sql-queries-for-unmapped-types/POST.md b/docs/en/Community-Articles/2023-10-23-NET-8-Feature-raw-sql-queries-for-unmapped-types/POST.md
new file mode 100644
index 0000000000..11d8e67563
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-23-NET-8-Feature-raw-sql-queries-for-unmapped-types/POST.md
@@ -0,0 +1,53 @@
+# New Raw SQL queries for unmapped types feature with EF Core 8.0
+
+## Introduction
+
+I would love to talk about the new feature in EF Core 8.0, specifically the `raw SQL queries for unmapped types`.
+This feature was recently introduced by Microsoft and is aimed at providing more flexibility and customization in database queries.
+
+## What is the raw SQL queries for the unmapped types feature?
+
+To give you a better understanding, let's look at a sample repository method with the ABP framework.
+Here is an example of a raw SQL query using the new feature:
+
+````csharp
+public interface IAuthorRepository : IRepository
+{
+ Task> GetAllAuthorNamesAsync();
+}
+
+public class AuthorIdWithNames
+{
+ public Guid Id { get; set; }
+
+ public string Name { get; set; }
+}
+
+public class EfCoreAuthorRepository : EfCoreRepository, IAuthorRepository
+{
+ public EfCoreAuthorRepository(IDbContextProvider dbContextProvider)
+ : base(dbContextProvider)
+ {
+ }
+
+ public virtual async Task> GetAllAuthorNamesAsync()
+ {
+ return await (await GetDbContextAsync()).Database.SqlQuery(@$"SELECT Id, Name FROM Authors").ToListAsync();
+ }
+}
+````
+
+In this code, we can see that we are using the `SqlQuery` method to execute a raw SQL query on a custom type, `AuthorIdWithNames` in this case. This allows us to retrieve data that may not be mapped to any of our entity classes in the context.
+
+## In summary
+
+This feature can be particularly useful in scenarios where we need to access data from tables or views that are not directly mapped to our entities. It also provides an alternative to using stored procedures for querying data.
+
+However, it's important to note that using raw SQL queries can increase the risk of SQL injection attacks. So, it's recommended to use parameterized queries to prevent this. Additionally, this feature may not work with certain database providers, so it's important to check for compatibility before implementing it.
+
+In conclusion, the raw SQL queries for unmapped types feature in EF Core 8.0 is a great addition for developers looking for more flexibility in database queries. It allows us to work with data that may not be directly mapped to our entities and can be a useful tool in certain scenarios. Just remember to use parameterized queries and check for compatibility before implementing it.
+
+## References
+
+- [Raw SQL queries for unmapped types](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#raw-sql-queries-for-unmapped-types)
+- [SQL Queries](https://learn.microsoft.com/en-us/ef/core/querying/sql-queries#querying-scalar-(non-entity)-types)
diff --git a/docs/en/Community-Articles/2023-10-28-EF-Core-8-Primitive-Collections/POST.md b/docs/en/Community-Articles/2023-10-28-EF-Core-8-Primitive-Collections/POST.md
new file mode 100644
index 0000000000..79d1b100c9
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-28-EF-Core-8-Primitive-Collections/POST.md
@@ -0,0 +1,118 @@
+# EF Core 8 Primitive collections
+
+What can we do when we want to store a list of primitive types? Before EF Core 8, there were two options:
+
+- Create a wrapper class and a related table, then add a foreign key linking each value to its owner of the collection.
+- Use [value converter](https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions) to serialize-deserialize as JSON.
+
+The first option covers most scenarios if we need to add some additional properties to this type but let's say we're never gonna need this type additionality.
+
+## Which collection types are supported ?
+
+EF Core has the capability to map the `IEnumerable` public properties that have both a getter and a setter, with the `T` representing a primitive type
+
+```csharp
+public class PrimitiveCollections
+{
+ public IEnumerable Ints { get; set; }
+ public ICollection Strings { get; set; }
+ public ISet DateTimes { get; set; }
+ public IList Dates { get; set; }
+ public uint[] UnsignedInts { get; set; }
+ public List Booleans { get; set; }
+ public List Urls { get; set; }
+}
+```
+
+> Some generic arguments are not considered primitive on the database side, such as `uint` and `Uri`. However, these types are also considered as primitive because there are [built-in value converters](https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations#built-in-converters).
+
+## Demo
+
+In this sample, we have a `Car` class with a `Color` enum, and the `Car` class has a `Colors` property.
+
+```csharp
+public enum Color
+{
+ Black,
+ White,
+ Red,
+ Blue
+}
+
+public class Car
+{
+ public int Id { get; set; }
+ public string Brand { get; set; }
+ public string Model { get; set; }
+ public ISet Colors { get; set; } = new HashSet();
+
+ public Car(string brand, string model)
+ {
+ Brand = brand;
+ Model = model;
+ }
+}
+```
+
+When we want to list the cars if they have any of the specific colors.
+
+```csharp
+var colors = new HashSet { Color.Blue, Color.White };
+var cars = await context
+ .Cars
+ .Where(x=> x.Colors.Intersect(colors).Any())
+ .ToListAsync();
+```
+
+The SQL result looks like this; as you can see, it sends colors as parameters instead of adding them inline. It also uses the `json_each` function to deserialize on the database side.
+
+```sql
+SELECT "c"."Id", "c"."Brand", "c"."Colors", "c"."Model"
+ FROM "Cars" AS "c"
+ WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT "c0"."value"
+ FROM json_each("c"."Colors") AS "c0"
+ INTERSECT
+ SELECT "c1"."value"
+ FROM json_each(@__colors_0) AS "c1"
+ ) AS "t")
+
+```
+
+When we insert to the car table.
+
+```csharp
+var car = new Car("Maserati", "GranTurismo")
+{
+ Colors = new HashSet()
+ {
+ Color.Black,
+ Color.Blue
+ }
+};
+context.Cars.Add(car);
+await context.SaveChangesAsync();
+```
+
+The SQL statement looks like this, and as you can see, it automatically serializes into the Colors parameter as JSON.
+
+```sql
+ Executed DbCommand (0ms) [Parameters=
+ [@p0='Maserati' (Nullable = false) (Size = 8),
+ @p1='[0,3]' (Nullable = false) (Size = 5),
+ @p2='Flow' (Nullable = false) (Size = 4)
+ ], CommandType='Text', CommandTimeout='30']
+ INSERT INTO "Cars" ("Brand", "Colors", "Model")
+ VALUES (@p0, @p1, @p2)
+ RETURNING "Id";
+```
+## Conclusion
+
+We don't need to do anything if we just use a collection of primitive types. It serializes and deserializes them as JSON automatically. Additionally, it sends the primitive collection as a parameter to cache the query.
+
+## References
+
+- [EF Core 8 Primitive collections](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#primitive-collection-properties)
+- [.NET Data Community Standup - Collections of primitive values in EF Core](https://www.youtube.com/watch?v=AUS2OZjsA2I)
diff --git a/docs/en/Community-Articles/2023-10-30-Enhancements-to-JSON-column-mapping/POST.md b/docs/en/Community-Articles/2023-10-30-Enhancements-to-JSON-column-mapping/POST.md
new file mode 100644
index 0000000000..db8f72ca17
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-30-Enhancements-to-JSON-column-mapping/POST.md
@@ -0,0 +1,124 @@
+# EF Core 8 - Enhancements to JSON column mapping
+
+In this article, we will examine the enhancements introduced in EF Core 8 for the JSON column feature, building upon the foundation laid by [JSON columns in Entity Framework Core 7](https://community.abp.io/posts/json-columns-in-entity-framework-core-7-cjaom76j).
+
+## The entity classes we will be using in the article
+
+```csharp
+public class Person
+{
+ public int Id { get; set; }
+ [Required]
+ public string Name { get; set; }
+ [Required]
+ public ContactDetails ContactDetails { get; set; }
+}
+
+public class ContactDetails
+{
+ public List Addresses { get; set; } = new();
+ 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 bool IsMainAddress { get; set; }
+}
+```
+
+## The DbContext class we will be using in the article
+
+```csharp
+public class AppDbContext : DbContext
+{
+ public DbSet Persons { get; set; } = null!;
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+#if SQLSERVER
+ optionsBuilder.UseSqlServer("Server=localhost;Database=EfCore8Json;Trusted_Connection=True;TrustServerCertificate=True");
+#elif SQLITE
+ optionsBuilder.UseSqlite("Data Source=EfCore8Json.db");
+#endif
+ base.OnConfiguring(optionsBuilder);
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity(b =>
+ {
+ b.ToTable("Persons");
+ b.HasKey(x => x.Id);
+ b.Property(x => x.Name).IsRequired();
+ b.OwnsOne(x => x.ContactDetails, cb =>
+ {
+ cb.ToJson();
+ cb.Property(x => x.Phone);
+ cb.OwnsMany(x => x.Addresses);
+ });
+ });
+
+ base.OnModelCreating(modelBuilder);
+ }
+}
+```
+
+## Translate element access into JSON arrays
+
+EF Core 8 supports indexing in JSON arrays when executing queries. For example, the following query returns individuals whose first address is the main address in the database:
+
+```csharp
+var query = dbContext.Persons
+ .Select(x => x.ContactDetails.Addresses[0])
+ .Where(x => x.IsMainAddress == true)
+ .ToListAsync();
+```
+
+The generated SQL query is as follows when using SQL Server:
+
+```sql
+SELECT JSON_QUERY([p].[ContactDetails], '$.Addresses[0]'), [p].[Id]
+FROM [Persons] AS [p]
+WHERE CAST(JSON_VALUE([p].[ContactDetails], '$.Addresses[0].IsMainAddress') AS bit) = CAST(1 AS bit)
+```
+
+> Note: If you attempt to access an index that is outside of the array, it will return null.
+
+## JSON Columns for SQLite
+
+In EF Core 7, JSON column mapping was supported for Azure SQL/SQL Server. In EF Core 8, this support has been extended to include SQLite as well.
+
+### Queries into JSON columns
+
+The following query returns individuals whose first address is the main address in the database:
+
+```csharp
+var query = dbContext.Persons
+ .Select(x => x.ContactDetails.Addresses[0])
+ .Where(x => x.IsMainAddress == true)
+ .ToListAsync();
+```
+
+The generated SQL query is as follows when using SQLite:
+
+```sql
+SELECT "p"."ContactDetails" ->> '$.Addresses[0]', "p"."Id"
+FROM "Persons" AS "p"
+WHERE "p"."ContactDetails" ->> '$.Addresses[0].IsMainAddress' = 0
+```
+
+## References
+
+- [EF Core 8 - Enhancements to JSON column mapping](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#enhancements-to-json-column-mapping)
\ No newline at end of file
diff --git a/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/POST.md b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/POST.md
new file mode 100644
index 0000000000..9142a36be9
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/POST.md
@@ -0,0 +1,196 @@
+# .NET 8 - ASP.NET Core Metrics
+
+In this article, I'll show you the new built-in metrics of .NET 8, which are basically numerical measurements reported over time in your application and can be used to monitor the health of your application and generate reports according to those numerical values. We will see what the metrics are, why to use them, and how to use them in detail. So, let's dive in.
+
+## What are Metrics?
+
+Metrics are numerical measurements reported over time. These measurements are crucial for monitoring the health of an application and generating alerts when necessary. In the context of a web service, various metrics can be tracked, such as:
+
+* Requests per second.
+* Response time.
+* Status code counts etc...
+
+These metrics are not just collected; they are reported to a monitoring system at regular intervals. By doing so, the development and operations teams can visualize these metrics on dashboards. These dashboards provide a real-time overview of the application's performance and health.
+
+## Pre-Built Metrics
+
+ASP.NET Core has many built-in metrics. You can see the following figure for a list of built-in metrics for ASP.NET Core:
+
+
+
+> See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-aspnetcore and https://github.com/dotnet/aspnetcore/issues/47536 for all built-in metrics and their descriptions.
+
+For example, we have the `kestrel-current-connections` metric that shows the _number of connections that are currently active on the server_ and the `http-server-request-duration` metric that shows _the duration of HTTP requests on the server_.
+
+All of these and other built-in metrics are produced by using the [**System.Diagnostics.Metrics**](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) API and with a small amount of code, we can use & view them.
+
+## Using Pre-Built Metrics
+
+Let's see the pre-built metrics in action.
+
+Create a new ASP.NET Core app with the following command and change the directory to the created application folder:
+
+```bash
+dotnet new web -o MetricsDemo
+cd MetricsDemo
+```
+
+After that, open the application in your favorite IDE and add the following service registration code to your application:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddOpenTelemetry()
+ .WithMetrics(builder =>
+ {
+ builder.AddPrometheusExporter();
+
+ builder.AddMeter("Microsoft.AspNetCore.Hosting",
+ "Microsoft.AspNetCore.Server.Kestrel");
+ builder.AddView("http.server.request.duration",
+ new ExplicitBucketHistogramConfiguration
+ {
+ Boundaries = new double[] { 0, 0.005, 0.01, 0.025, 0.05,
+ 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }
+ });
+ });
+```
+
+Here, we have done the following steps:
+
+* The `AddOpenTelemetry` method registers the required **OpenTelemetry** services into the DI container.
+* In the `WithMetrics` method, we add pre-built metrics to the `OpenTelemetryBuilder` in the `AddMeter` method (_Microsoft.AspNetCore.Hosting_ and _Microsoft.AspNetCore.Server.Kestrel_ are pre-built metrics provided by ASP.NET Core).
+* We have also used the `AddView` method to customize the output of the metrics by the SDK. It can also be used to customize which [Instruments](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument) are to be processed or ignored.
+
+After making the related configurations, let's add the middleware below and view the metrics:
+
+```csharp
+app.MapPrometheusScrapingEndpoint();
+
+app.MapGet("/", () =>
+{
+ Thread.Sleep(2000);
+
+ return "Hello, ABP Community member: " + DateTime.Now.Ticks.ToString()[^3..];
+});
+```
+
+* `MapPrometheusScrapingEndpoint`: Adds the OpenTelemetry Prometheus scraping endpoint middleware to the pipeline and then we can see the all metrics by navigating to the **/metrics** endpoints (optional):
+
+
+
+### View Metrics
+
+You can use the `dotnet-counters` command-line tool, which allows you to view live metrics for .NET Core apps. You can run the following command to install the tool:
+
+```bash
+dotnet tool update -g dotnet-counters
+```
+
+After the tool is installed, you can run the application and by running the `dotnet-counters` tool in a terminal, you can monitor for the specific metrics:
+
+```bash
+dotnet-counters monitor -n MetricsDemo --counters Microsoft.AspNetCore.Hosting
+```
+
+When you run the command, it will print a message like the one below and will wait for an initial request to your application:
+
+```txt
+Press p to pause, r to resume, q to quit.
+ Status: Waiting for initial payload...
+```
+
+If you send a request to your application, then the related built-in metrics that you have added will be collected and will be shown in the terminal instantly:
+
+
+
+Also, you can check for the other metric that we have used in the configuration, which is _Microsoft.AspNetCore.Server.Kestrel_ by setting it as a counter as in the command below, and when you send a request to your application, you will see different metrics:
+
+```bash
+dotnet-counters monitor -n MetricsDemo --counters Microsoft.AspNetCore.Server.Kestrel
+```
+
+## Creating Custom Metrics in ASP.NET Core Applications
+
+So far, we have seen what metrics are, which built-in metrics are provided by ASP.NET Core, the use of the built-in metrics and running the `dotnet-counters` global tool to view these metrics. At this point, let's see how we can create custom metrics and view them via the `dotnet-counters` tool.
+
+`IMeterFactory` is the recommended service to create *Meter* instances. ASP.NET Core registers the `IMeterFactory` in dependency injection (DI) by default. The meter factory integrates metrics with DI, making isolating and collecting metrics easy.
+
+Assume that, we want to create a metric that holds the sold products in our application. For that purpose, as a first step, we can create a metric class as follows:
+
+```csharp
+using System.Diagnostics.Metrics;
+
+namespace MetricsDemo;
+
+public class ProductMetrics
+{
+ private readonly Counter _soldProductsCount;
+
+ public ProductMetrics(IMeterFactory meterFactory)
+ {
+ var meterInstance = meterFactory.Create("MetricsDemo.ProductStore");
+ _soldProductsCount = meterInstance.CreateCounter("metricsdemo.productstore.sold_products_count");
+ }
+
+ public void IncreaseSoldProductCount(int quantity)
+ {
+ _soldProductsCount.Add(quantity);
+ }
+}
+```
+
+In this class, we are creating a counter and defining metrics that we want to collect and view. `MetricsDemo.ProductStore` is the **counter name** and `metricsdemo.productstore.sold_products_count` is the **metric**. In our example, we need to register this class with a `Singleton` lifetime because we don't want to reset the counter and want to keep it during the application runtime.
+
+> The [System.Diagnostics.Metrics.Meter](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics.meter) type is the entry point for a library to create a named group of instruments. Instruments record the numeric measurements that are needed to calculate metrics. Here we used [CreateCounter](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics.meter.createcounter) to create a Counter instrument named _metricsdemo.productstore.sold_products_count_.
+
+So, register the type with the DI container in the `Program.cs` file (or you can implement the `ISingletonDependency` interface, if you are using an ABP based application):
+
+```csharp
+builder.Services.AddSingleton();
+```
+
+Then, we can inject this class and increase the sold product count whenever it's needed. Since we have registered it in DI, we can use it in minimal APIs as in the following example:
+
+```csharp
+app.MapPost("/complete-sale", ([FromBody] ProductSoldModel model, ProductMetrics metrics) =>
+{
+ // ... business logic such as saving the product to the database etc... ...
+
+ metrics.IncreaseSoldProductCount(model.Quantity);
+});
+```
+
+Whenever a request is made to this endpoint the metric that we've defined (_metricsdemo.productstore.sold_products_count_) will be increased and we can see this metric via the `dotnet-counters` global tool. You can run the following command to see the metrics:
+
+```bash
+dotnet-counters monitor -n MetricsDemo --counters MetricsDemo.ProductStore
+```
+
+Here is an example output that you would see if you have made a POST request to the `/complete-sale` endpoint:
+
+```txt
+Press p to pause, r to resume, q to quit.
+ Status: Paused
+
+[MetricsDemo.ProductStore]
+ metricsdemo.productstore.sold_products_count (Count / 1 sec) 20
+```
+
+### Advanced: View Metrics in Prometheus & Grafana - Building Dashboards
+
+It's a common requirement to build a dashboard and monitor the metrics and check the health of your applications. At that point, you can expose the metrics and scrape them using the [Prometheus](https://learn.microsoft.com/en-us/aspnet/core/log-mon/metrics/metrics?view=aspnetcore-8.0#set-up-and-configure-prometheus) and build a dashboard by using the [Grafana](https://learn.microsoft.com/en-us/aspnet/core/log-mon/metrics/metrics?view=aspnetcore-8.0#show-metrics-on-a-grafana-dashboard).
+
+If you want to build a dashboard, you can see [this documentation from Microsoft](https://learn.microsoft.com/en-us/aspnet/core/log-mon/metrics/metrics?view=aspnetcore-8.0#view-metrics-in-grafana-with-opentelemetry-and-prometheus), which shows how to set up the **Prometheus server** and show metrics on a **Grafana** dashboard.
+
+Also, you can check [this video](https://www.youtube.com/watch?v=A2pKhNQoQUU) if you want to see it in action.
+
+## Conclusion
+
+In this article, I've shown you the new built-in metrics of .NET8, described what metrics are, which pre-built metrics are provided by ASP.NET Core, how to use & view these pre-built metrics and finally, I have shown you how to create custom metrics and view them in a dashboard. If you want to learn more about the **Metrics System of .NET**, you can check out the references that I have shared below.
+
+## References
+
+* https://learn.microsoft.com/en-us/aspnet/core/log-mon/metrics/metrics?view=aspnetcore-8.0
+* https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-5/
+* https://www.youtube.com/watch?v=A2pKhNQoQUU
diff --git a/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metric-response.png b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metric-response.png
new file mode 100644
index 0000000000..035e76de7d
Binary files /dev/null and b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metric-response.png differ
diff --git a/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metrics.png b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metrics.png
new file mode 100644
index 0000000000..7cf3a1896d
Binary files /dev/null and b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/built-in-metrics.png differ
diff --git a/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/metrics-endpoint.png b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/metrics-endpoint.png
new file mode 100644
index 0000000000..70933e0f6e
Binary files /dev/null and b/docs/en/Community-Articles/2023-10-30-NET-8-ASP-NET-Core-Metrics/metrics-endpoint.png differ
diff --git a/docs/en/Community-Articles/2023-10-31-NET-8-ASP-NET-Core-Minimal-APIs/POST.md b/docs/en/Community-Articles/2023-10-31-NET-8-ASP-NET-Core-Minimal-APIs/POST.md
new file mode 100644
index 0000000000..03ca906884
--- /dev/null
+++ b/docs/en/Community-Articles/2023-10-31-NET-8-ASP-NET-Core-Minimal-APIs/POST.md
@@ -0,0 +1,156 @@
+# New Minimal APIs features in ASP.NET Core 8.0
+
+In this article, we will see the new features of Minimal APIs in ASP.NET Core 8.0.
+
+## Binding to forms
+
+We can bind to forms using the [FromForm] attribute. Let's see an example:
+
+```csharp
+app.MapPost("/books", async ([FromForm] string name,
+ [FromForm] BookType bookType, IFormFile? cover, BookDb db) =>
+{
+ var book = new Book
+ {
+ Name = name,
+ BookType = bookType
+ };
+
+ if (cover is not null)
+ {
+ var coverName = Path.GetRandomFileName();
+
+ using var stream = File.Create(Path.Combine("wwwroot", coverName));
+ await cover.CopyToAsync(stream);
+ book.Cover = coverName;
+ }
+
+ db.Books.Add(book);
+ await db.SaveChangesAsync();
+
+ return Results.Ok();
+});
+```
+
+Another way is using the [AsParameters] attribute, the following code binds from form values to properties of the `NewBookRequest` record struct:
+
+```csharp
+public record NewBookRequest([FromForm] string Name, [FromForm] BookType BookType, IFormFile? Cover);
+
+app.MapPost("/books", async ([AsParameters] NewBookRequest request, BookDb db) =>
+{
+ var book = new Book
+ {
+ Name = request.Name,
+ BookType = request.BookType
+ };
+
+ if (request.Cover is not null)
+ {
+ var coverName = Path.GetRandomFileName();
+
+ using var stream = File.Create(Path.Combine("wwwroot", coverName));
+ await request.Cover.CopyToAsync(stream);
+ book.Cover = coverName;
+ }
+
+ db.Books.Add(book);
+ await db.SaveChangesAsync();
+
+ return Results.Ok();
+});
+```
+
+## Antiforgery
+
+ASP.NET Core 8.0 adds support for antiforgery tokens. We can call the `AddAntiforgery` method to register the antiforgery services and `WebApplicationBuilder` will automatically add the antiforgery middleware to the pipeline:
+
+```csharp
+var builder = WebApplication.CreateBuilder();
+
+builder.Services.AddAntiforgery();
+
+var app = builder.Build();
+
+// Implicitly added by WebApplicationBuilder if AddAntiforgery is called.
+// app.UseAntiforgery();
+
+app.MapGet("/", () => "Hello World!");
+
+app.Run();
+```
+
+Example of using antiforgery tokens:
+
+```csharp
+
+// Use the antiforgery service to generate tokens.
+app.MapGet("/", (HttpContext context, IAntiforgery antiforgery) =>
+{
+ var token = antiforgery.GetAndStoreTokens(context);
+ return Results.Content(...., "text/html");
+});
+
+// It will automatically validate the token.
+app.MapPost("/todo", ([FromForm] Todo todo) => Results.Ok(todo));
+
+// Disable antiforgery validation for this endpoint.
+app.MapPost("/todo2", ([FromForm] Todo todo) => Results.Ok(todo))
+ .DisableAntiforgery();
+```
+
+
+## Native AOT
+
+ASP.NET Core 8.0 adds support for Native AOT.
+
+You can add `PublishAot` to the project file to enable Native AOT:
+
+```xml
+
+ true
+
+```
+
+### Web API (native AOT) template
+
+You can use the `dotnet new webapiaot` command to create a new Minimal APIs project with Native AOT enabled.
+
+```diff
++using System.Text.Json.Serialization;
+
+-var builder = WebApplication.CreateBuilder();
++var builder = WebApplication.CreateSlimBuilder(args);
+
++builder.Services.ConfigureHttpJsonOptions(options =>
++{
++ options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
++});
+
+var app = builder.Build();
+
+var sampleTodos = TodoGenerator.GenerateTodos().ToArray();
+
+var todosApi = app.MapGroup("/todos");
+todosApi.MapGet("/", () => sampleTodos);
+todosApi.MapGet("/{id}", (int id) =>
+ sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
+ ? Results.Ok(todo)
+ : Results.NotFound());
+
+app.Run();
+
++[JsonSerializable(typeof(Todo[]))]
++internal partial class AppJsonSerializerContext : JsonSerializerContext
++{
++
++}
+```
+
+* Reflection isn't supported in native AOT, you must use the `JsonSerializable` attribute to specify the types that you want to serialize/deserialize.
+
+## References
+
+* https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0#minimal-apis
+* https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/parameter-binding?view=aspnetcore-8.0#explicit-binding-from-form-values
+* https://learn.microsoft.com/en-us/aspnet/core/fundamentals/native-aot?view=aspnetcore-8.0
diff --git a/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/POST.md b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/POST.md
new file mode 100644
index 0000000000..22c621aa1b
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/POST.md
@@ -0,0 +1,138 @@
+# ASP.NET 8: What's New About Authentication and Authorization
+
+In ASP.NET 8, the concept of authentication and authorization is undergoing a transformation. Specifically, ASP.NET Core Identity is transitioning from a focus on traditional web pages to a more API-driven approach. We will see what the Identity API endpoints are, why we need them, and how to use them in detail. So, let's dive in.
+
+## What is ASP.NET Core Identity?
+
+ASP.NET Core Identity is like an extra toolkit that comes with ASP.NET Core. It offers a bunch of services to help you deal with user accounts in your ASP.NET Core application. These services include both basic concepts and ready-made solutions for each task.
+
+With ASP.NET Core Identity, you can save user accounts in your app, handle user information, add extra security with two-factor authentication, and even connect other log in options, like social media logins, to user accounts. In simple terms, it's all about keeping user accounts in your app and making it easy for users to log in.
+
+
+
+Prior to ASP.NET 8, there were limitations on using ASP.NET Identity in Single Page Applications (SPAs). Because the ASP.NET Core Identity framework is primarily tailored for traditional server-rendered web applications, like ASP.NET Core MVC or Razor Pages apps. However, the Identity API endpoints that come with ASP.NET 8 aim to solve token-based authentication and authorization in SPA without external dependencies.
+
+## Why do we need Identity APIs?
+
+The introduction of Identity endpoints in ASP.NET 8 aims to streamline the process of integrating ASP.NET Core Identity into both API server applications and front-end SPAs like those built with JavaScript or Blazor. To understand the importance of these endpoints, let's first examine the disadvantages of using ASP.NET Core Identity before ASP.NET 8.
+
+Let's delve into a common scenario:
+
+Imagine you have a straightforward application composed of an ASP.NET Core backend that exposes APIs. On the client side, you have a SPA application that communicates with these APIs. Now, you want to incorporate user accounts, complete with authentication and authorization, into your application.
+
+Before the advent of the identity endpoints, you could add ASP.NET Core Identity and the default Razor Pages UI to your app by adding a few packages, updating your database schema, and registering some services. However, this approach had some disadvantages. From a user experience perspective, the full-page refreshes of Razor Pages can be a major drawback especially compared to the fluidity of SPAs. On the other hand, from a developer's perspective, if you want the default pages to be compatible with the rest of your application, you may need to update more than 30 pages. Moreover, the default Razor Pages UI uses traditional cookie-based authentication, not bearer tokens.
+
+In ASP.NET 8, identity endpoints were introduced to simplify the process of adding user accounts to ASP.NET Core API apps used with SPAs or mobile. These endpoints streamline the user management process by providing an alternative to the traditional Razor Page Identity UI pages. Additionally, a new endpoint is included for retrieving bearer tokens, which can be used for authentication. These changes directly address the concerns mentioned earlier, making it easier to create a user-friendly and integrated experience within your app, with no full-page refreshes or styling conflicts.
+
+## How to add Identity APIs?
+
+In this part, I'll create a demo application, add all of the required packages, and lastly map the Identity APIs.
+
+Create a new ASP.NET Core app with the following command and change the directory to the created application folder:
+
+```bash
+dotnet new webapi --name NewIdentityEndpoints -o IdentityDemo
+cd IdentityDemo
+```
+
+After that, open the application in your favorite IDE and add the required packages:
+
+```csharp
+
+
+```
+
+**Note:** I used the `.NET 8.0.100-rc.2-**` SDK for everything in this post.
+
+Then replace `Program.cs` as below:
+
+```csharp
+using System.Security.Claims;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
+builder.Services.AddAuthorizationBuilder();
+
+builder.Services.AddDbContext(options => options.UseInMemoryDatabase("AppDb"));
+
+builder.Services.AddIdentityCore()
+.AddEntityFrameworkStores()
+.AddApiEndpoints();
+
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var app = builder.Build();
+
+app.MapGroup("/my-identity-api").MapIdentityApi();
+
+app.MapGet("/", (ClaimsPrincipal user) => $"Hello {user.Identity!.Name}").RequireAuthorization();
+
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.Run();
+
+class MyCustomUser : IdentityUser { }
+
+class ApplicationDbContext : IdentityDbContext
+{
+ public ApplicationDbContext(DbContextOptions options) : base(options) { }
+}
+
+```
+
+When you run the application, you will see a result as the following:
+
+
+
+## Custom Authorization Policies
+
+Prior to ASP.NET 8, adding a parameterized authorization policy to an endpoint required writing a lot of code.
+
+- Implementing an `AuthorizeAttribute` for each policy
+- Implementing an `AuthorizationPolicyProvider` to process a custom policy from a string-based contract
+- Implementing an `AuthorizationRequirement` for the policy
+- Implementing an `AuthorizationHandler` for each requirement
+
+However, implementing the `IAuthorizationRequirementData` interface that comes with ASP.NET 8, your application code should look like this:
+
+```csharp
+class MinimumAgeAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirement, IAuthorizationRequirementData
+{
+ public MinimumAgeAuthorizeAttribute(int age) => Age =age;
+ public int Age { get; }
+
+ public IEnumerable GetRequirements()
+ {
+ yield return this;
+ }
+}
+
+class MinimumAgeAuthorizationHandler : AuthorizationHandler
+{
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeAuthorizeAttribute requirement)
+ {
+ ...
+ }
+}
+```
+
+See [here](https://gist.github.com/captainsafia/7c54e92d12df695ff0908e989fb8531f) for the complete code.
+
+## Conclusion
+
+In this article, I've shown you the Identity APIs introduced with ASP.NET 8, why we need them, and how we can add them to our application, and finally I explained how to define a custom authorization policy with fewer lines of code.
+
+## References
+- https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0#authentication-and-authorization
+- https://auth0.com/blog/whats-new-dotnet8-authentication-authorization/
+- https://andrewlock.net/exploring-the-dotnet-8-preview-introducing-the-identity-api-endpoints/
+- https://andrewlock.net/should-you-use-the-dotnet-8-identity-api-endpoints/
diff --git a/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/identity_endpoints_scaffold.png b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/identity_endpoints_scaffold.png
new file mode 100644
index 0000000000..951f6f1da6
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/identity_endpoints_scaffold.png differ
diff --git a/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/new-identity-endpoints.png b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/new-identity-endpoints.png
new file mode 100644
index 0000000000..b8ac5021ee
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-03-ASPNET8-authentication-and-authorization/new-identity-endpoints.png differ
diff --git a/docs/en/Community-Articles/2023-11-05-EF-Core-8-Complex-Types/POST.MD b/docs/en/Community-Articles/2023-11-05-EF-Core-8-Complex-Types/POST.MD
new file mode 100644
index 0000000000..06289387e5
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-05-EF-Core-8-Complex-Types/POST.MD
@@ -0,0 +1,189 @@
+# Using Complex Types as Value Objects with Entity Framework Core 8.0
+
+Entity Framework Core 8.0 is being shipped in a week as a part of .NET 8.0. In this article, I will introduce the new **[Complex Types](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#value-objects-using-complex-types)** feature of EF Core 8 and show some examples of how you can use it in your projects built with ABP Framework.
+
+## What is a Value Object?
+
+A [Value Object](https://docs.abp.io/en/abp/latest/Value-Objects) is a simple object that has no conceptual identity (Id). Instead, a Value Object is identified by its properties.
+
+A Value Object is typically owned by a parent [Entity](https://docs.abp.io/en/abp/latest/Entities) object. Using [Complex Types](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#value-objects-using-complex-types) with EF Core 8 is the best way to create Value Objects that are stored as a part of your main entity.
+
+## Creating an ABP Project
+
+> **WARNING**: ABP Framework has not a .NET 8.0 compatible version yet. It is planned to be released on November 15, 2023. I created this project with ABP 7.4.1 (based on .NET 7.0), then manually changed the `TargetFramework` (in the `csproj`) to `net8.0` and added the `Microsoft.EntityFrameworkCore.SqlServer` package with version `8.0.0-rc.2.23480.1`. After that, the project is being compiled, but some parts of this article may not work as expected until ABP Framework 8.0-RC.1 is released.
+>
+> **I will update the article once ABP Framework 8.0-RC.1 is released.**
+
+I will show code examples, so I am creating a new ABP project using the following [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) command:
+
+````bash
+abp new ComplexTypeDemo -t app-nolayers
+````
+
+> I prefer a non-layered project to keep the things simple. If you are new to ABP Framework, follow the [Getting Started](https://docs.abp.io/en/abp/latest/Getting-Started-Overall) tutorial to learn how to create a new project from scratch.
+
+Once I created the solution, I am running the following command to execute the database migrations in order to create the initial database:
+
+````bash
+dotnet run --project ComplexTypeDemo --migrate-database
+````
+
+> We could also execute the `migrate-database.ps1` (that is coming as a part of the solution) as a shortcut.
+
+## Creating a Complex Type
+
+Assume that we have a `Customer` [entity](https://docs.abp.io/en/abp/latest/Entities) as shown below:
+
+````csharp
+public class Customer : BasicAggregateRoot
+{
+ public string Name { get; set; }
+ public Address HomeAddress { get; set; }
+ public Address BusinessAddress { get; set; }
+}
+````
+
+Here, we have two address properties, one for home and the other one for business. Since an address is a multi-values property, we can define it as a separate object:
+
+````csharp
+public class Address
+{
+ public string City { get; set; }
+ public string Line1 { get; set; }
+ public string? Line2 { get; set; }
+ public string PostCode { get; set; }
+}
+````
+
+`Address` is a typical complex object. It is actually a part of the `Customer` object, but we wanted to collect its parts into a dedicated class to make it a domain concept and easily manage its properties together.
+
+## Configure EF Core Mappings
+
+We should set the `Address` class as a Complex Type in our EF Core mapping configuration. There are two ways of it.
+
+As the first way, we can add the `ComplexType` attribute on top of the `Address` class:
+
+````csharp
+[ComplexType] // Added this line
+public class Address
+{
+ ...
+}
+````
+
+As an alternative, we can configure the mapping using the fluent mapping API. You can write the following code into the `OnModelCreating` method of your `DbContext` class:
+
+````csharp
+builder.Entity(b =>
+{
+ b.ToTable("Customers");
+ b.ComplexProperty(x => x.HomeAddress); // Mapping a Complex Type
+ b.ComplexProperty(x => x.BusinessAddress); // Mapping another Complex Type
+ //... configure other properties
+});
+
+````
+
+You can further configure the properties of the `Address` class:
+
+````csharp
+b.ComplexProperty(x => x.HomeAddress, a =>
+{
+ a.Property(x => x.City).HasMaxLength(50).IsRequired();
+});
+````
+
+Once you configure the mappings, you can use the [EF Core command-line tool](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) to create a database migration:
+
+````bash
+dotnet ef migrations add "Added_Customer_And_Address"
+````
+
+And update the database:
+
+````bash
+dotnet ef database update
+````
+
+If you check the fields of the `Customers` table in your dayabase, you will see the following fields:
+
+* `Id`
+* `Name`
+* `HomeAddress_City`
+* `HomeAddress_Line1`
+* `HomeAddress_Line2`
+* `HomeAddress_PostCode`
+* `BusinessAddress_City`
+* `BusinessAddress_Line1`
+* `BusinessAddress_Line2`
+* `BusinessAddress_PostCode`
+
+As you see, EF Core stores the `Address` properties as a part of your main entity.
+
+## Querying Objects
+
+You can query entities from database using the properties of a complex type as same as you query by the properties of the main entity.
+
+**Example: Find customers by `BusinessAddress.PostCode`:**
+
+````csharp
+public class MyService : ITransientDependency
+{
+ private readonly IRepository _customerRepository;
+
+ public MyService(IRepository customerRepository)
+ {
+ _customerRepository = customerRepository;
+ }
+
+ public async Task DemoAsync()
+ {
+ var customers = await _customerRepository.GetListAsync(
+ c => c.BusinessAddress.PostCode == "12345"
+ );
+
+ //...
+ }
+}
+````
+
+## Mutable vs Immutable
+
+Entity Framework Core Complex Types can work with mutable and immutable types. In the `Address` example above, I've shown a simple mutable class - that means you can change an individual property of an `Address` object after creating it (or after querying from database). However, designing Value Objects as immutable is a highly common approach.
+
+For example, you can use C#'s `struct` type to define an immutable `Address` type:
+
+````csharp
+public readonly struct Address(string line1, string? line2, string city, string postCode)
+{
+ public string City { get; } = city;
+ public string Line1 { get; } = line1;
+ public string? Line2 { get; } = line2;
+ public string PostCode { get; } = postCode;
+}
+````
+
+See the [Microsoft's documentation](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#mutability) for more examples and different usages.
+
+## Final Notes
+
+There are more details about using Complex Types in your applications. I want to mention some of them here:
+
+* You can have nested complex types. For example, `Address` may have one or more `PhoneNumber` objects as its properties. Everything will work seamlessly.
+* A single instance of a Complex Type can be set to multiple properties (of the same or different entities). In that case, changing the Complex object's properties will affect all of the properties. However, try to avoid that since it may create unnecessary complexities in your code that is hard to understand.
+* You can manipulate mutable complex object properties just as another property in your entity. EF Core change tracking system will track them as you expect.
+* Currently, EF Core doesn't support to have a collection of complex objects in an entity. It works only for properties.
+
+For more details and examples, see the Microsoft's document in the *References* section.
+
+## Source Code
+
+You can find the sample project here:
+
+https://github.com/hikalkan/samples/tree/master/EfCoreComplexTypeDemo
+
+## References
+
+* [Value objects using Complex Types](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#value-objects-using-complex-types) in [What's new with EF Core 8.0](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew) document by Microsoft.
+* [ABP Entity Framework Core integration document](https://docs.abp.io/en/abp/latest/Entity-Framework-Core)
+
diff --git a/docs/en/Community-Articles/2023-11-06- SerializationDeserialization-Improvements/POST.md b/docs/en/Community-Articles/2023-11-06- SerializationDeserialization-Improvements/POST.md
new file mode 100644
index 0000000000..74022bd572
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-06- SerializationDeserialization-Improvements/POST.md
@@ -0,0 +1,100 @@
+# .NET 8: Serialization Improvements
+
+The [System.Text.Json](https://learn.microsoft.com/en-us/dotnet/api/system.text.json) namespace provides functionality for serializing to and deserializing from JSON. In .NET 8, there have been many improvements to the System.Text.Json serialization and deserialization functionality.
+
+## Source generator
+
+.NET 8 includes enhancements of the System.Text.Json source generator that are aimed at making the Native AOT experience on par with the reflection-based serializer. This is important because now you can select the source generator for System.Text.Json. To see a comparison of Reflection versus source generation in System.Text.Json, check out the comparison [documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/reflection-vs-source-generation?pivots=dotnet-8-0#reflection).
+
+## Interface hierarchies
+
+.NET 8 adds support for serializing properties from interface hierarchies. Here is a code sample that shows this feature.
+
+```csharp
+ICar car = new MyCar { Color = "Red", NumberOfWheels = 4 };
+JsonSerializer.Serialize(car); // {"Color":"Red","NumberOfWheels":4}
+
+public interface IVehicle
+{
+ public string Color { get; set; }
+}
+
+public interface ICar : IVehicle
+{
+ public int NumberOfWheels { get; set; }
+}
+
+public class MyCar : ICar
+{
+ public string Color { get; set; }
+ public int NumberOfWheels { get; set; }
+}
+```
+
+## Naming policies
+
+Two new naming policies `snake_case` and `kebab-case` have been added. You can use them as shown below:
+
+```csharp
+var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
+JsonSerializer.Serialize(new { CreationTime = new DateTime(2023,11,6) }, options); // {"creation_time":"2023-11-06T00:00:00"}
+```
+
+## Read-only properties
+
+With .NET 8, you can deserialize onto read-only fields or properties. This feature can be globally enabled by setting `PreferredObjectCreationHandling` to `JsonObjectCreationHandling.Populate`. You can also enable this feature for a class or one of its members by adding the `[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]` attribute. Here is a sample:
+
+```csharp
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+var book = JsonSerializer.Deserialize("""{"Contributors":["John Doe"],"Author":{"Name":"Sample Author"}}""")!;
+Console.WriteLine(JsonSerializer.Serialize(book));
+
+class Author
+{
+ public required string Name { get; set; }
+}
+
+[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
+class Book
+{
+ // Both of these properties are read-only.
+ public List Contributors { get; } = new();
+ public Author Author { get; } = new() {Name = "Undefined"};
+}
+```
+
+Before .NET 8, the output was like this:
+
+```json
+{"Contributors":[],"Author":{"Name":"Undefined"}}
+```
+
+With .NET 8, the output now looks like this:
+
+```json
+{"Contributors":["John Doe"],"Author":{"Name":"Sample Author"}}
+```
+
+## Disable reflection-based default
+
+One of the nice features about Serialization is, now you can disable using the reflection-based serializer by default. To disable default reflection-based serialization, set the `JsonSerializerIsReflectionEnabledByDefault` MSBuild property to `false` in your project file.
+
+## Streaming deserialization APIs
+
+.NET 8 includes new `IAsyncEnumerable` streaming deserialization extension methods. The new extension methods invoke streaming APIs and return `IAsyncEnumerable`. Here is a sample code that uses this new feature.
+
+```csharp
+const string RequestUri = "https://yourwebsite.com/api/saas/tenants?skipCount=0&maxResultCount=10";
+using var client = new HttpClient();
+IAsyncEnumerable tenants = client.GetFromJsonAsAsyncEnumerable(RequestUri);
+
+await foreach (Tenant tenant in tenants)
+{
+ Console.WriteLine($"* '{tenant.name}' uses '{tenant.editionName
+}' edition");
+}
+```
+
+I have mentioned some of the items for Serialization Improvements in .NET 8. If you want to check the full list, you can read it in the .NET 8's [What's new document](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#serialization).
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/Post.md b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/Post.md
new file mode 100644
index 0000000000..a346b6f084
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/Post.md
@@ -0,0 +1,89 @@
+# Blazor's History and Full-stack Web UI
+
+Blazor is a web framework that allows developers to build interactive web applications using .NET instead of JavaScript. The first version of Blazor was **released on May 14, 2020**. Since its initial release, Blazor has evolved with the new versions. Until now, six different versions have been declared. Sometimes, it can be not very clear to see the differences between these approaches. First, let's try to understand these.
+
+* **Blazor-Server**: >> *Loads fast at first* >> In this version, heavy things are done in the server. Browsers are thin clients and download a small page for the first load. The page updates are done via SignalR connection. This was released with .NET Core 3.
+* **Blazor WebAssembly (WASM):** >> *Loads slow at first* >> In this version, some binary files are being downloaded to the browser. This approach takes longer initialization time than the "Server" approach. The hard work is done on the browser.
+* **Blazor Hybrid:** It's a combination of Blazor and Xamarin.Forms. It allows you to run your app on iOS, Android, macOS, and Windows. Blazor Hybrid uses a WebView component to host the Blazor-based user interface within the mobile app.
+* **Blazor Native**: It runs natively on the devices and uses a common UI abstraction to render native controls for that device. This is very similar to how frameworks like Xamarin Forms or React Native work today. It has also been considered but has not reached [the planning stage](https://devblogs.microsoft.com/dotnet/blazor-server-in-net-core-3-0-scenarios-and-performance/).
+
+* **Blazor United**: Recently, Microsoft updated this name to "Fullstack web UI". Blazor-Server and Blazor WASM both have some disadvantages and advantages. So, Microsoft decided to combine these two approaches and find an optimum solution for the entire Blazor version. We can call it *Best of Blazor* 😀
+
+## Why is "Blazor Fullstack Web UI" the best?
+
+These apps are a combination of both Blazor Server and Blazor WASM. It provides the advantages of Blazor Server and WASM. Developers would be able to more fine-tune the rendering mode. **This approach overcomes the large binary downloads of Blazor WASM, and it resolves the Blazor Server's problem, which always needs to be connected to the server via SignalR.**
+
+> Blazor Fullstack Web UI comes with the .NET 8, and it will be published on November 14, 2023.
+
+I took the following photo from Steven Sanderson's talk at NDC Porto 2023. You can read my impressions of this conf at https://volosoft.com/blog/ndc-port-2023-impressions, but after reading this one.
+
+
+
+
+
+There are two basic page styles:
+
+* **HTML**; simple and loads fast! Eg: MVC, Razor Pages
+* **Single Page Apps (SPA):** high interaction with the client and loads slower! Eg: Blazor WASM, Blazor Server...
+
+## HTML + SPA => Blazor Fullstack
+
+Blazor Fullstack (formerly United) is a technology that turns Blazor Server into a SPA style.
+
+.NET 8 will combine Blazor Server's server-side rendering and WebAssembly's client-side interaction.
+
+You can switch between two rendering modes and even mix them on the same page. With .NET 8 there also comes amazing features like;
+
+* [Streaming rendering](https://github.com/dotnet/aspnetcore/issues/46352): With this feature, most of the page will be rendered, and long async operations on the server will still be in progress.
+
+* [Progressive enhancement of form submission & navigation](https://github.com/dotnet/aspnetcore/issues/46399): With this feature, it doesn't fully reload the page after submitting the form. This gives the user a better and smoother experience.
+
+
+
+ ## How it works?
+
+### Rendering on Server
+
+You can add `WebComponentRenderMode.Server` to your Blazor components so that these components will run interactively. In the below example, the list editor will make AJAX requests to the server like single-page applications.
+
+
+
+
+
+And sure you can add `WebComponentRenderMode.Server` to your page level, and the complete page will be rendered as a server component. All inputs on this page can work as an interactive server component like SPA mode.
+
+
+
+
+
+### Rendering on client
+
+You can switch to WebAssembly mode by writing `WebComponentRenderMode.WebAssembly` attribute to your page. By doing so, the whole page should run interactively using WebAssembly. This time there's no server connection anymore because it loads the binaries (WebAssembly runtimes) at the page load.
+
+
+
+## How it works?
+
+To enable Blazor Full-stack Web UI, you need to write `net8.0;net7.0-browser` into the `TargetFrameworks` area of your `csproj` file. These two keywords change your app like this; `net8.0` framework renders on the server, and `net7.0-browser` framework renders on the browser.
+
+
+
+## Let the System decide on WebAssembly or Server approach
+
+You can let the system decide whether it uses `WebAssembly` or `Server`. This can be done with the `Auto` mode of the `WebComponentRenderMode`. In this case, it will not load binary files (WebAssembly files) for the initial page that has `WebComponentRenderMode.Server` attribute, but whenever the user navigates to a page that has `WebComponentRenderMode.WebAssembly`, it will download the runtimes. This will allow us to load the initial page very fast, and when we need interactivity, we can switch to `WebAssembly` and wait for the binaries to download. But this download will be done one time because it will be cached.
+
+
+
+
+
+## Conclusion
+
+I summarized the new generation Blazor in a very simple way. This architecture will be useful to everyone who uses Blazor.
+
+
+
+*Resources:*
+
+* You can check out Dan Roth's GitHub issue 👉 [github.com/dotnet/aspnetcore/issues/46636](https://github.com/dotnet/aspnetcore/issues/46636).
+* Steven Sanderson's YouTube video is very good for understanding these concepts 👉 [Blazor United Prototype Video](https://youtu.be/48G_CEGXZZM).
+
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106163046763-1699282281622-2.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106163046763-1699282281622-2.png
new file mode 100644
index 0000000000..62c2bcea95
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106163046763-1699282281622-2.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172420148.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172420148.png
new file mode 100644
index 0000000000..62d2a704b8
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172420148.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172638604.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172638604.png
new file mode 100644
index 0000000000..f28987d536
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106172638604.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173021958.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173021958.png
new file mode 100644
index 0000000000..6120776e90
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173021958.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173411309.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173411309.png
new file mode 100644
index 0000000000..2cc92a9ee3
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173411309.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173849303.png b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173849303.png
new file mode 100644
index 0000000000..13413ca55f
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-Blazor-Fullstack-Web-Ui/image-20231106173849303.png differ
diff --git a/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/POST.md b/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/POST.md
new file mode 100644
index 0000000000..9255c39a5a
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/POST.md
@@ -0,0 +1,79 @@
+# HierarchyId Support in Entity Framework Core
+
+Entity Framework Core has official support for HierarchyId, which allows you to store and query hierarchical data in SQL Server databases. Hierarchical data is a common data structure found in many applications. Whether you are dealing with organizational structures, product categories, or threaded discussions, handling hierarchies efficiently is crucial. In this blog post, we will explore how to manage hierarchical data using Entity Framework Core (EF Core) in combination with HierarchyId.
+
+## How to use HierarchyId in EF Core
+
+To use HierarchyId in EF Core, you need to install the [Microsoft.EntityFrameworkCore.SqlServer.HierarchyId](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer.HierarchyId/) NuGet package. This package contains query and update support for HierarchyId.
+
+To configure EF Core to use HierarchyId, you need to call the `UseHierarchyId` method when configuring the SQL Server provider.
+
+```csharp
+options.UseSqlServer(
+ connectionString,
+ x => x.UseHierarchyId());
+```
+
+## Modeling Hierarchies
+
+The `HierarchyId` type can be used for properties of an entity type. For example, assume we want to model the personnel in an organization. Each person has a name and a manager. The manager is also a person. We can model this using the following entity type:
+
+```csharp
+public class Person
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public HierarchyId Manager { get; set; }
+}
+```
+
+Each person can be traced from the patriarch down the tree using its `Manager` property. SQL Server uses a compact binary format for these paths, but it is common to parse to and from a human-readable string representation when working with code. In this representation, the position at each level is separated by a `/` character.
+
+
+
+## Querying Hierarchies
+
+HierarchyId exposes several methods that can be used in LINQ queries.
+
+* `GetAncestor(int level)` returns the ancestor at the specified level.
+
+* `GetDescendant(HierarchyId? child1, HierarchyId? child2)` returns the descendant at the specified level.
+
+* `GetLevel()` returns the level of the node in the hierarchy.
+
+* `GetReparentedValue(HierarchyId? oldRoot, HierarchyId? newRoot)` returns the node with a new parent.
+
+* `IsDescendantOf(HierarchyId? parent)` returns whether the node is a descendant of the specified node.
+
+Example:
+
+```csharp
+// Get entities at a given level in the tree
+var generation = await context.Persons.Where(x => x.Manager.GetLevel() == level).ToListAsync();
+
+// Get the direct ancestor of an entity
+var parent = await context.Persons.Where(x => x.Manager.GetAncestor(1) == manager).SingleAsync();
+```
+
+For example, your company may want to change the manager of an organizational unit. To do this, you can use the `GetReparentedValue` method to update the manager for all descendants of the organizational unit.
+
+```csharp
+var newManager = await context.Persons.SingleAsync(x => x.Id == newManagerId);
+
+var descendants = await context.Persons
+ .Where(x => x.Manager.IsDescendantOf(oldManager))
+ .ToListAsync();
+
+foreach (var descendant in descendants)
+{
+ descendant.Manager = descendant.Manager.GetReparentedValue(oldManager.Manager, newManager.Manager);
+}
+
+await context.SaveChangesAsync();
+```
+
+## References
+
+For more information about hierarchy id, see the following resource:
+
+https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew#hierarchyid-in-net-and-ef-core
diff --git a/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/hierarchy-tree.png b/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/hierarchy-tree.png
new file mode 100644
index 0000000000..e22ed9781c
Binary files /dev/null and b/docs/en/Community-Articles/2023-11-06-EF-Core_Hierarchy-Id/hierarchy-tree.png differ
diff --git a/docs/en/Community-Articles/2023-11-97-AOT-Compilation/Post.md b/docs/en/Community-Articles/2023-11-97-AOT-Compilation/Post.md
new file mode 100644
index 0000000000..2b994b59c3
--- /dev/null
+++ b/docs/en/Community-Articles/2023-11-97-AOT-Compilation/Post.md
@@ -0,0 +1,75 @@
+# Native AOT Compilation in .NET 8
+Native AOT (Ahead-of-Time) compilation is a feature that allows developers to create a self-contained app compiled to native code that can run on machines without the .NET runtime installed. It results in benefits such as minimized disk footprint, reduced executable size, reduced startup time, and reduced memory demand.
+
+Native AOT compilation isn't a new feature in .NET 8. It's first introduced in .NET 7.
+
+
+Differences between the AOT Compilation of .NET 7 and .NET 8 are:
+
+
+- **System.Text.Json improvements**: .NET 8 adds support for more types, source generation, interface hierarchies, naming policies, read-only properties, and more.
+- **New types for performance**: .NET 8 introduces new types such as FrozenDictionary, FrozenSet, SearchValues, CompositeFormat, TimeProvider, and ITimer to improve the app performance.
+- **System.Numerics and System.Runtime.Intrinsics enhancements**: .NET 8 adds support for Vector512, AVX-512, IUtf8SpanFormattable, Lerp, and more.
+- **System.ComponentModel.DataAnnotations additions**: .NET 8 adds new data validation attributes for cloud-native services and a new ValidateOptionsResultBuilder type.
+- **Hosted services lifecycle methods**: .NET 8 adds new methods such as StartAsync, StopAsync, StartBackgroundAsync, and StopBackgroundAsync for hosted services.
+
+It's important to note that not all features in ASP.NET Core are currently compatible with native AOT. For more information, see [Native AOT deployment overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/).
+
+## How to use Native AOT Compilation in .NET 8
+
+You can add `true` in your project .csproj file to enable Native AOT Compilation.
+
+ - For the new projects, you can create them with the `--aot` parameter. Example: `dotnet new console --aot`.
+
+By default, the compiler chooses a blended approach code optimization but you can specify an optimization preference inside your .csproj file. You can choose **size** or **speed** according your requirements.
+
+```xml
+Size
+```
+
+or
+
+```xml
+Speed
+```
+
+### Results
+
+I have created a simple console application to test the Native AOT Compilation. I have used a simple console application that writes "Hello World!" to the console 100 times. I have tested the application with different optimization preferences. I have used the following results:
+
+
+| | Size | Speed |
+| --- | --- | --- |
+| .NET 8 _(Self-Contained, Single File)_ | 65938 kb | 00.0051806 ~5ms |
+| .NET 7 AOT (default) | 4452 kb | 00.0029823 ~2ms |
+| .NET 8 AOT (default) | 1242 kb | 00.0028638 ~2ms |
+| AOT (Speed)| 1280 kb | 00.0023838 ~2ms |
+| AOT (Size) | 1111 kb | 00.0025145 ~2ms |
+
+Most of existing libraries don't support AOT compilation yet, so I couldn't use [BenchmarkDotnet](https://github.com/dotnet/BenchmarkDotNet) to measure the performance. I have used [Stopwatch](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=net-8.0) to measure the performance. So the performance results may not be accurate but gives insight about the performance difference.
+
+## AOT Support in MAUI
+You can now use Native AOT Compilation on iOS-like target frameworks in .NET MAUI. You can enable AOT compilation with the exact same method by adding `true` to your project .csproj file. According to the dotnet team, apps sizes reduced by 35% and startup times reduced by 28% with AOT compilation. And runtime performance is also improved by 50%.
+
+But there are some limitations in MAUI AOT Compilation. A lot of libraries still don't support AOT compilation and some of platform-specific feaetures may not work at the moment.
+
+## When to use Native AOT Compilation?
+
+Native AOT Compilation is beneficial when you need to optimize your .NET application for speed and size. It's particularly useful for applications that require quick startup times and efficient runtime performance, such as mobile apps or high-performance computing applications.
+
+However, due to its current limitations, it might not be suitable for all projects. If your project relies heavily on libraries that do not support AOT compilation, or if it uses platform-specific features that are not yet compatible with AOT, you might want to hold off on using Native AOT Compilation until further improvements are made.
+
+Always consider the specific needs and constraints of your project before deciding to use Native AOT Compilation.
+
+## Conclusion
+
+Native AOT Compilation is a great feature that improves the performance of .NET applications. It's still in early-stages and not all libraries support it yet. But it's a great beginning for the future of .NET 🚀
+
+
+## Links
+- Native AOT deployment overview - .NET | Microsoft Learn. https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/.
+-
+- Optimize AOT deployments https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/optimizing
+-
+- What's new in .NET 8 | Microsoft Learn. https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8.
+
diff --git a/docs/en/Deployment/Index.md b/docs/en/Deployment/Index.md
index 37d6c5bcc5..cf7137464f 100644
--- a/docs/en/Deployment/Index.md
+++ b/docs/en/Deployment/Index.md
@@ -7,7 +7,7 @@ However, there are some topics that you should care about when you are deploying
## Guides
* [Configuring SSL certificate(HTTPS)](SSL.md): Explains how to configure SSL certificate(HTTPS) for your application.
-* [Configuring for OpenIddict](Configuring-OpenIddict.md): Notes for some essential configurations for OpenIddict.
+* [Configuring OpenIddict](Configuring-OpenIddict.md): Notes for some essential configurations for OpenIddict.
* [Configuring for Production](Configuring-Production.md): Notes for some essential configurations for production environments.
* [Optimization for Production](Optimizing-Production.md): Tips and suggestions for optimizing your application on production environments.
* [Deploying to a Clustered Environment](Clustered-Environment.md): Explains how to configure your application when you want to run multiple instances of your application concurrently.
diff --git a/docs/en/Migration-Guides/Abp-8_0.md b/docs/en/Migration-Guides/Abp-8_0.md
index e90d9f04f0..108de6ba98 100644
--- a/docs/en/Migration-Guides/Abp-8_0.md
+++ b/docs/en/Migration-Guides/Abp-8_0.md
@@ -66,7 +66,7 @@ The following NuGet libraries have been upgraded:
| Serilog.Sinks.Async | 1.4.0 | 1.5.0 |
| SharpZipLib | 1.3.3 | 1.4.2 |
| Shouldly | 4.1.0 | 4.2.1 |
-| SixLabors.ImageSharp | 1.0.4 | 2.1.6 |
+| SixLabors.ImageSharp | 1.0.4 | 3.0.2 |
| Slugify.Core | 3.0.0 | 4.0.1 |
| Spectre.Console | 0.46.1-preview.0.7 | 0.47.0 |
| Swashbuckle.AspNetCore | 6.2.1 | 6.5.0 |
diff --git a/docs/en/Modules/Cms-Kit/Blogging.md b/docs/en/Modules/Cms-Kit/Blogging.md
index d45598be52..eb6619a4bc 100644
--- a/docs/en/Modules/Cms-Kit/Blogging.md
+++ b/docs/en/Modules/Cms-Kit/Blogging.md
@@ -29,7 +29,16 @@ A screenshot from the new blog creation modal:

-**Slug** is the URL part of the blog. For this example, the root URL of the blog becomes *https://your-domain.com/blogs/technical-blog/*.
+**Slug** is the URL part of the blog. For this example, the root URL of the blog becomes `your-domain.com/blogs/technical-blog/`.
+
+- You can change the default slug by using `CmsBlogsWebConsts.BlogRoutePrefix` constant. For example, if you set it to `foo`, the root URL of the blog becomes `your-domain.com/foo/technical-blog/`.
+
+ ```csharp
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ CmsBlogsWebConsts.BlogsRoutePrefix = "foo";
+ }
+ ```
#### Blog Features
diff --git a/docs/en/Modules/Cms-Kit/Comments.md b/docs/en/Modules/Cms-Kit/Comments.md
index cc8d01249b..ddd39fc675 100644
--- a/docs/en/Modules/Cms-Kit/Comments.md
+++ b/docs/en/Modules/Cms-Kit/Comments.md
@@ -53,6 +53,7 @@ The comment system provides a commenting [widget](../../UI/AspNetCore/Widgets.md
{
entityType = "Product",
entityId = "...",
+ isReadOnly = false,
referralLinks = new [] {"nofollow"}
})
```
diff --git a/docs/en/Modules/Cms-Kit/Ratings.md b/docs/en/Modules/Cms-Kit/Ratings.md
index db58602c15..e6a2ba97fd 100644
--- a/docs/en/Modules/Cms-Kit/Ratings.md
+++ b/docs/en/Modules/Cms-Kit/Ratings.md
@@ -41,7 +41,8 @@ The ratings system provides a rating widget to allow users send ratings to resou
@await Component.InvokeAsync(typeof(RatingViewComponent), new
{
entityType = "Product",
- entityId = "entityId"
+ entityId = "entityId",
+ isReadOnly = false
})
```
diff --git a/docs/en/Startup-Templates/Application.md b/docs/en/Startup-Templates/Application.md
index 0c5e9c4c38..fdb7569ca0 100644
--- a/docs/en/Startup-Templates/Application.md
+++ b/docs/en/Startup-Templates/Application.md
@@ -316,7 +316,7 @@ You should add `routes` property in the `data` object to add a link on the menu
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
- canActivate: [AuthGuard, PermissionGuard],
+ canActivate: [authGuard, permissionGuard],
data: {
routes: {
name: 'ProjectName::Menu:Dashboard',
@@ -328,7 +328,7 @@ You should add `routes` property in the `data` object to add a link on the menu
}
```
In the above example;
-* If the user is not logged in, AuthGuard blocks access and redirects to the login page.
+* If the user is not logged in, authGuard blocks access and redirects to the login page.
* PermissionGuard checks the user's permission with the `requiredPolicy` property of the `routes` object. If the user is not authorized to access the page, the 403 page appears.
* The `name` property of `routes` is the menu link label. A localization key can be defined.
* The `iconClass` property of the `routes` object is the menu link icon class.
diff --git a/docs/en/Timing.md b/docs/en/Timing.md
index fce17de4cf..9b3eb0cca9 100644
--- a/docs/en/Timing.md
+++ b/docs/en/Timing.md
@@ -102,7 +102,7 @@ This section covers the ABP Framework infrastructure related to managing time zo
### TimeZone Setting
-ABP Framework defines **a setting**, named `Abp.Timing.Timezone`, that can be used to set and get the time zone for a user, [tenant](Multi-Tenancy.md) or globally for the application. The default value is `UTC`.
+ABP Framework defines **a setting**, named `Abp.Timing.TimeZone`, that can be used to set and get the time zone for a user, [tenant](Multi-Tenancy.md) or globally for the application. The default value is `UTC`.
See the [setting documentation](Settings.md) to learn more about the setting system.
diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md
index f33a41f392..f306b7f4a2 100644
--- a/docs/en/Tutorials/Part-5.md
+++ b/docs/en/Tutorials/Part-5.md
@@ -323,11 +323,11 @@ Open the `/src/app/book/book-routing.module.ts` and replace with the following c
````js
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
-import { AuthGuard, PermissionGuard } from '@abp/ng.core';
+import { authGuard, permissionGuard } from '@abp/ng.core';
import { BookComponent } from './book.component';
const routes: Routes = [
- { path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] },
+ { path: '', component: BookComponent, canActivate: [authGuard, permissionGuard] },
];
@NgModule({
@@ -337,8 +337,8 @@ const routes: Routes = [
export class BookRoutingModule {}
````
-* Imported `AuthGuard` and `PermissionGuard` from the `@abp/ng.core`.
-* Added `canActivate: [AuthGuard, PermissionGuard]` to the route definition.
+* Imported `authGuard` and `permissionGuard` from the `@abp/ng.core`.
+* Added `canActivate: [authGuard, permissionGuard]` to the route definition.
Open the `/src/app/route.provider.ts` and add `requiredPolicy: 'BookStore.Books'` to the `/books` route. The `/books` route block should be following:
diff --git a/docs/en/UI/Angular/Entity-Action-Extensions.md b/docs/en/UI/Angular/Entity-Action-Extensions.md
index 3ab035b04a..d93b2045f6 100644
--- a/docs/en/UI/Angular/Entity-Action-Extensions.md
+++ b/docs/en/UI/Angular/Entity-Action-Extensions.md
@@ -16,19 +16,16 @@ In this example, we will add a "Click Me!" action and alert the current row's `u
The following code prepares a constant named `identityEntityActionContributors`, ready to be imported and used in your root module:
-```js
+```ts
// src/app/entity-action-contributors.ts
-import {
- eIdentityComponents,
- IdentityEntityActionContributors,
- IdentityUserDto,
-} from '@abp/ng.identity';
+import { eIdentityComponents, IdentityEntityActionContributors } from '@abp/ng.identity';
+import { IdentityUserDto } from '@abp/ng.identity/proxy';
import { EntityAction, EntityActionList } from '@abp/ng.theme.shared/extensions';
const alertUserName = new EntityAction({
text: 'Click Me!',
- action: data => {
+ action: (data) => {
// Replace alert with your custom code
alert(data.record.userName);
},
diff --git a/docs/en/UI/Angular/OAuth-Module.md b/docs/en/UI/Angular/OAuth-Module.md
index e7a62d1d2e..b747c5221d 100644
--- a/docs/en/UI/Angular/OAuth-Module.md
+++ b/docs/en/UI/Angular/OAuth-Module.md
@@ -6,7 +6,6 @@ If your app is version 7.0 or higher, you should include "AbpOAuthModule.forRoot
Those abstractions can be found in the @abp/ng-core packages.
- `AuthService` (the class that implements the IAuthService interface).
- `NAVIGATE_TO_MANAGE_PROFILE` Inject token.
-- `AuthGuard` (the class that implements the IAuthGuard interface).
- `ApiInterceptor` (the class that implements the IApiInterceptor interface).
Those base classes are overridden by the "AbpOAuthModule" for oAuth. There are also three functions provided with AbpOAuthModule.
diff --git a/docs/en/UI/Angular/Permission-Management.md b/docs/en/UI/Angular/Permission-Management.md
index dd373d6274..63d919f599 100644
--- a/docs/en/UI/Angular/Permission-Management.md
+++ b/docs/en/UI/Angular/Permission-Management.md
@@ -54,20 +54,20 @@ As shown above you can remove elements from DOM with `abpPermission` structural
## Permission Guard
-You can use `PermissionGuard` if you want to control authenticated user's permission to access to the route during navigation.
+You can use `permissionGuard` if you want to control authenticated user's permission to access to the route during navigation.
* Import the PermissionGuard from @abp/ng.core.
-* Add `canActivate: [PermissionGuard]` to your route object.
+* Add `canActivate: [permissionGuard]` to your route object.
* Add `requiredPolicy` to the `data` property of your route in your routing module.
```js
-import { PermissionGuard } from '@abp/ng.core';
+import { permissionGuard } from '@abp/ng.core';
// ...
const routes: Routes = [
{
path: 'path',
component: YourComponent,
- canActivate: [PermissionGuard],
+ canActivate: [permissionGuard],
data: {
requiredPolicy: 'YourProjectName.YourComponent', // policy key for your component
},
diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md
index 6cd79505c2..43b8dc8b0f 100644
--- a/docs/en/UI/Angular/Service-Proxies.md
+++ b/docs/en/UI/Angular/Service-Proxies.md
@@ -73,6 +73,16 @@ export const environment: Config.Environment = {
> The destination the `proxy` folder is created and the paths above may change based on your project structure.
+## Parameters of generate-proxy
+
+- **module or -m:** The backend module name. The default is `app`. The object key of the modules defined in response of `api/abp/api-definition`. For example, if you want to generate-proxy of PermissionManagement, you should pass `permissionManagement` as a value.
+- **apiName or -a:** The Backend api name, also known as remoteServiceName. It is defined in the selected module (in response of `api/abp/api-definition`). The property(key) name is `remoteServiceName`. For example for the PermissionManagement, you should pass `AbpPermissionManagement`
+- **source:** Source of the Angular project for the API definition URL & root namespace resolution.
+- **target:** Target for the Angular project to place the generated code. For example, if it's `permission-management`, it'll look like this (npm/ng-packs/packages/*permission-management*).
+- **entryPoint:** To create the generated proxy folder in the target. The directory is `permission-management/proxy/src/lib/proxy` and the `permission-management` is the value of target. If you want to create a folder for the generated proxy, there are two options, you should either set the value `proxy` as the entryPoint or go to project.json and change the `sourceRoot` from `packages/permission-management/src` to `packages/permission-management/proxy/src`. No need to change the sourceRoot of project with the property. if you keep it empty, the proxy will be generated into the folder defined in the sourceRoot property.
+- **serviceType:** The service type of the generated proxy. The options are `application`, `integration` and `all`. The default value is `application`. A developer can mark a service "integration service". If you want to skip proxy generation for the service, then this is the correct setting. More info about [Integration Services](../../Integration-Services)
+
+
### Services
The `generate-proxy` command generates one service per back-end controller and a method (property with a function value actually) for each action in the controller. These methods call backend APIs via [RestService](./Http-Requests#restservice).
diff --git a/docs/zh-Hans/Startup-Templates/Application.md b/docs/zh-Hans/Startup-Templates/Application.md
index 91d7308226..d6733b8633 100644
--- a/docs/zh-Hans/Startup-Templates/Application.md
+++ b/docs/zh-Hans/Startup-Templates/Application.md
@@ -302,7 +302,7 @@ ABP 配置模块也已经导入到 `AppModule` 中, 以满足可延迟加载 ABP
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
- canActivate: [AuthGuard, PermissionGuard],
+ canActivate: [authGuard, permissionGuard],
data: {
routes: {
name: 'ProjectName::Menu:Dashboard',
@@ -315,7 +315,7 @@ ABP 配置模块也已经导入到 `AppModule` 中, 以满足可延迟加载 ABP
```
在上面的例子中;
* 如果用户没有登录, AuthGuard 会阻塞访问并重定向到登录页面.
-* PermissionGuard 使用 `rotues` 对象的 `requiredPolicy` 属性检查用户的权限. 如果用户未被授权访问该页, 则显示403页.
+* permissionGuard 使用 `rotues` 对象的 `requiredPolicy` 属性检查用户的权限. 如果用户未被授权访问该页, 则显示403页.
* `routes` 的 `name` 属性是菜单链接标签. 可以定义本地化 key.
* `routes` 对象的 `iconClass` 属性是菜单链接图标类.
* `routes` 对象的 `requiredPolicy` 属性是访问页面所需的策略 key.
diff --git a/docs/zh-Hans/Timing.md b/docs/zh-Hans/Timing.md
index 71ddbb9058..1d220a7aad 100644
--- a/docs/zh-Hans/Timing.md
+++ b/docs/zh-Hans/Timing.md
@@ -102,7 +102,7 @@ var normalizedDateTime = Clock.Normalize(dateTime)
### 时区设置
-ABP框架定义了一个名为 `Abp.Timing.Timezone` 的**设置**,可用于为应用程序的用户,[租户](Multi-Tenancy.md)或全局设置和获取时区. 默认值为 `UTC`.
+ABP框架定义了一个名为 `Abp.Timing.TimeZone` 的**设置**,可用于为应用程序的用户,[租户](Multi-Tenancy.md)或全局设置和获取时区. 默认值为 `UTC`.
参阅[设置系统]了解更多关于设置系统.
@@ -110,4 +110,4 @@ ABP框架定义了一个名为 `Abp.Timing.Timezone` 的**设置**,可用于为
`ITimezoneProvider` 是一个服务,可将[Windows时区ID](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values)值简单转换为[Iana时区名称](https://www.iana.org/time-zones)值,反之亦然. 它还提供了获取这些时区列表与获取具有给定名称的 `TimeZoneInfo` 的方法.
-它已使用[TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter)库实现.
\ No newline at end of file
+它已使用[TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter)库实现.
diff --git a/docs/zh-Hans/Tutorials/Part-5.md b/docs/zh-Hans/Tutorials/Part-5.md
index 5bdad55c1c..ec308f3ee7 100644
--- a/docs/zh-Hans/Tutorials/Part-5.md
+++ b/docs/zh-Hans/Tutorials/Part-5.md
@@ -389,11 +389,11 @@ UI的第一步是防止未认证用户看见"图书"菜单项并进入图书管
````js
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
-import { AuthGuard, PermissionGuard } from '@abp/ng.core';
+import { authGuard, permissionGuard } from '@abp/ng.core';
import { BookComponent } from './book.component';
const routes: Routes = [
- { path: '', component: BookComponent, canActivate: [AuthGuard, PermissionGuard] },
+ { path: '', component: BookComponent, canActivate: [authGuard, permissionGuard] },
];
@NgModule({
@@ -403,8 +403,8 @@ const routes: Routes = [
export class BookRoutingModule {}
````
-* 从 `@abp/ng.core` 引入 `AuthGuard` 和 `PermissionGuard`.
-* 在路由定义中添加 `canActivate: [AuthGuard, PermissionGuard]`.
+* 从 `@abp/ng.core` 引入 `authGuard` 和 `permissionGuard`.
+* 在路由定义中添加 `canActivate: [authGuard, permissionGuard]`.
打开 `/src/app/route.provider.ts`, 在 `/books` 路由中添加 `requiredPolicy: 'BookStore.Books'`. `/books` 路由应该如以下配置:
diff --git a/docs/zh-Hans/UI/Angular/Permission-Management.md b/docs/zh-Hans/UI/Angular/Permission-Management.md
index 08496f03fe..05e3dacbf1 100644
--- a/docs/zh-Hans/UI/Angular/Permission-Management.md
+++ b/docs/zh-Hans/UI/Angular/Permission-Management.md
@@ -53,20 +53,20 @@ export class YourComponent {
## 权限守卫
-如果你想要在导航过程中控制经过身份验证的用户对路由的访问权限,可以使用 `PermissionGuard`.
+如果你想要在导航过程中控制经过身份验证的用户对路由的访问权限,可以使用 `permissionGuard`.
-* 从@abp/ng.core导入PermissionGuard.
-* 添加 `canActivate: [PermissionGuard]` 到你的路由对象.
+* 从@abp/ng.core导入permissionGuard.
+* 添加 `canActivate: [permissionGuard]` 到你的路由对象.
* 添加 `requiredPolicy` 到路由模块路由的 `data` 属性.
```js
-import { PermissionGuard } from '@abp/ng.core';
+import { permissionGuard } from '@abp/ng.core';
// ...
const routes: Routes = [
{
path: 'path',
component: YourComponent,
- canActivate: [PermissionGuard],
+ canActivate: [permissionGuard],
data: {
requiredPolicy: 'YourProjectName.YourComponent', // policy key for your component
},
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs
index 5b568e16a6..dee16585b7 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardTagHelper.cs
@@ -4,6 +4,8 @@ public class AbpCardTagHelper : AbpTagHelper
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
- output.Attributes.AddClass("card mb-3");
+ output.Attributes.AddClass("card");
+ if (TagHelper.AddMarginBottomClass)
+ {
+ output.Attributes.AddClass("mb-3");
+ }
SetBorder(context, output);
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
index a426b50d33..62cae94413 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
@@ -52,6 +52,8 @@ public class AbpInputTagHelper : AbpTagHelper
{
output.Attributes.AddClass("form-floating");
}
- output.Attributes.AddClass(isCheckBox ? "mb-2" : "mb-3");
+ if (TagHelper.AddMarginBottomClass)
+ {
+ output.Attributes.AddClass(isCheckBox ? "mb-2" : "mb-3");
+ }
if (isCheckBox)
{
output.Attributes.AddClass("custom-checkbox");
@@ -110,7 +113,8 @@ public class AbpInputTagHelperService : AbpTagHelperService
protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox)
{
- return "