@ -1,165 +1,89 @@ |
|||
GNU LESSER GENERAL PUBLIC LICENSE |
|||
Version 3, 29 June 2007 |
|||
|
|||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. |
|||
|
|||
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public. |
|||
The license is supplemented by the additional permissions listed below. |
|||
|
|||
This version of the GNU Lesser General Public License incorporates |
|||
the terms and conditions of version 3 of the GNU General Public |
|||
License, supplemented by the additional permissions listed below. |
|||
|
|||
0. Additional Definitions. |
|||
|
|||
As used herein, "this License" refers to version 3 of the GNU Lesser |
|||
General Public License, and the "GNU GPL" refers to version 3 of the GNU |
|||
General Public License. |
|||
**0. Additional Definitions.** |
|||
|
|||
As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. |
|||
|
|||
"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. |
|||
|
|||
An "Application" is any work that makes use of an interface provided by the Library but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. |
|||
|
|||
A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". |
|||
|
|||
The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application and not on the Linked Version. |
|||
|
|||
The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. |
|||
|
|||
|
|||
|
|||
**1. Exception to Section 3 of the GNU GPL.** |
|||
|
|||
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. |
|||
|
|||
|
|||
|
|||
**2. Conveying Modified Versions.** |
|||
|
|||
If you modify a copy of the Library and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: |
|||
|
|||
**a)** under this License, provided that you make a good-faith effort to ensure that, in the event an Application does not supply the |
|||
function or data, the facility still operates and performs whatever part of its purpose remains meaningful, or |
|||
|
|||
**b)** under the GNU GPL, with none of the additional permissions of this License is applicable to that copy. |
|||
|
|||
|
|||
|
|||
**3. Object Code Incorporating Material from Library Header Files.** |
|||
|
|||
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such an object code under terms of your choice, provided that if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: |
|||
|
|||
**a)** Give prominent notice with each copy of the object code that the Library is used in it, and the Library and its use are covered by this License. |
|||
|
|||
**b)** Accompany the object code with a copy of the GNU GPL and this license document. |
|||
|
|||
|
|||
|
|||
**4. Combined Works.** |
|||
|
|||
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: |
|||
|
|||
**a)** Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. |
|||
|
|||
**b)** Accompany the Combined Work with a copy of the GNU GPL and this license document. |
|||
|
|||
**c)** For a Combined Work that displays copyright notices during execution, including the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. |
|||
|
|||
**d)** Do one of the following: |
|||
|
|||
0) Convey the Minimal Corresponding Source under the terms of this License and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. |
|||
1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. |
|||
|
|||
**e)** Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying the Corresponding Source.) |
|||
|
|||
|
|||
"The Library" refers to a covered work governed by this License, |
|||
other than an Application or a Combined Work as defined below. |
|||
|
|||
An "Application" is any work that makes use of an interface provided |
|||
by the Library, but which is not otherwise based on the Library. |
|||
Defining a subclass of a class defined by the Library is deemed a mode |
|||
of using an interface provided by the Library. |
|||
|
|||
A "Combined Work" is a work produced by combining or linking an |
|||
Application with the Library. The particular version of the Library |
|||
with which the Combined Work was made is also called the "Linked |
|||
Version". |
|||
|
|||
The "Minimal Corresponding Source" for a Combined Work means the |
|||
Corresponding Source for the Combined Work, excluding any source code |
|||
for portions of the Combined Work that, considered in isolation, are |
|||
based on the Application, and not on the Linked Version. |
|||
|
|||
The "Corresponding Application Code" for a Combined Work means the |
|||
object code and/or source code for the Application, including any data |
|||
and utility programs needed for reproducing the Combined Work from the |
|||
Application, but excluding the System Libraries of the Combined Work. |
|||
|
|||
1. Exception to Section 3 of the GNU GPL. |
|||
|
|||
You may convey a covered work under sections 3 and 4 of this License |
|||
without being bound by section 3 of the GNU GPL. |
|||
|
|||
2. Conveying Modified Versions. |
|||
|
|||
If you modify a copy of the Library, and, in your modifications, a |
|||
facility refers to a function or data to be supplied by an Application |
|||
that uses the facility (other than as an argument passed when the |
|||
facility is invoked), then you may convey a copy of the modified |
|||
version: |
|||
|
|||
a) under this License, provided that you make a good faith effort to |
|||
ensure that, in the event an Application does not supply the |
|||
function or data, the facility still operates, and performs |
|||
whatever part of its purpose remains meaningful, or |
|||
|
|||
b) under the GNU GPL, with none of the additional permissions of |
|||
this License applicable to that copy. |
|||
|
|||
3. Object Code Incorporating Material from Library Header Files. |
|||
|
|||
The object code form of an Application may incorporate material from |
|||
a header file that is part of the Library. You may convey such object |
|||
code under terms of your choice, provided that, if the incorporated |
|||
material is not limited to numerical parameters, data structure |
|||
layouts and accessors, or small macros, inline functions and templates |
|||
(ten or fewer lines in length), you do both of the following: |
|||
|
|||
a) Give prominent notice with each copy of the object code that the |
|||
Library is used in it and that the Library and its use are |
|||
covered by this License. |
|||
|
|||
b) Accompany the object code with a copy of the GNU GPL and this license |
|||
document. |
|||
|
|||
4. Combined Works. |
|||
|
|||
You may convey a Combined Work under terms of your choice that, |
|||
taken together, effectively do not restrict modification of the |
|||
portions of the Library contained in the Combined Work and reverse |
|||
engineering for debugging such modifications, if you also do each of |
|||
the following: |
|||
|
|||
a) Give prominent notice with each copy of the Combined Work that |
|||
the Library is used in it and that the Library and its use are |
|||
covered by this License. |
|||
|
|||
b) Accompany the Combined Work with a copy of the GNU GPL and this license |
|||
document. |
|||
|
|||
c) For a Combined Work that displays copyright notices during |
|||
execution, include the copyright notice for the Library among |
|||
these notices, as well as a reference directing the user to the |
|||
copies of the GNU GPL and this license document. |
|||
|
|||
d) Do one of the following: |
|||
|
|||
0) Convey the Minimal Corresponding Source under the terms of this |
|||
License, and the Corresponding Application Code in a form |
|||
suitable for, and under terms that permit, the user to |
|||
recombine or relink the Application with a modified version of |
|||
the Linked Version to produce a modified Combined Work, in the |
|||
manner specified by section 6 of the GNU GPL for conveying |
|||
Corresponding Source. |
|||
|
|||
1) Use a suitable shared library mechanism for linking with the |
|||
Library. A suitable mechanism is one that (a) uses at run time |
|||
a copy of the Library already present on the user's computer |
|||
system, and (b) will operate properly with a modified version |
|||
of the Library that is interface-compatible with the Linked |
|||
Version. |
|||
|
|||
e) Provide Installation Information, but only if you would otherwise |
|||
be required to provide such information under section 6 of the |
|||
GNU GPL, and only to the extent that such information is |
|||
necessary to install and execute a modified version of the |
|||
Combined Work produced by recombining or relinking the |
|||
Application with a modified version of the Linked Version. (If |
|||
you use option 4d0, the Installation Information must accompany |
|||
the Minimal Corresponding Source and Corresponding Application |
|||
Code. If you use option 4d1, you must provide the Installation |
|||
Information in the manner specified by section 6 of the GNU GPL |
|||
for conveying Corresponding Source.) |
|||
|
|||
5. Combined Libraries. |
|||
|
|||
You may place library facilities that are a work based on the |
|||
Library side by side in a single library together with other library |
|||
facilities that are not Applications and are not covered by this |
|||
License, and convey such a combined library under terms of your |
|||
choice, if you do both of the following: |
|||
|
|||
a) Accompany the combined library with a copy of the same work based |
|||
on the Library, uncombined with any other library facilities, |
|||
conveyed under the terms of this License. |
|||
|
|||
b) Give prominent notice with the combined library that part of it |
|||
is a work based on the Library, and explaining where to find the |
|||
accompanying uncombined form of the same work. |
|||
|
|||
6. Revised Versions of the GNU Lesser General Public License. |
|||
|
|||
The Free Software Foundation may publish revised and/or new versions |
|||
of the GNU Lesser General Public License from time to time. Such new |
|||
versions will be similar in spirit to the present version, but may |
|||
differ in detail to address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the |
|||
Library as you received it specifies that a certain numbered version |
|||
of the GNU Lesser General Public License "or any later version" |
|||
applies to it, you have the option of following the terms and |
|||
conditions either of that published version or of any later version |
|||
published by the Free Software Foundation. If the Library as you |
|||
received it does not specify a version number of the GNU Lesser |
|||
General Public License, you may choose any version of the GNU Lesser |
|||
General Public License ever published by the Free Software Foundation. |
|||
|
|||
If the Library as you received it specifies that a proxy can decide |
|||
whether future versions of the GNU Lesser General Public License shall |
|||
apply, that proxy's public statement of acceptance of any version is |
|||
permanent authorization for you to choose that version for the |
|||
Library. |
|||
You may place library facilities that are work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License and convey such a combined library under the terms of your choice if you do both of the following: |
|||
|
|||
**a)** Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. |
|||
|
|||
**b)** Give prominent notice with the combined library that part of it is a work based on the Library and explains where to find the accompanying uncombined form of the same work. |
|||
|
|||
|
|||
|
|||
**6. Revised Versions of the GNU Lesser General Public License.** |
|||
|
|||
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version but may differ in detail to address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received, it does not specify a version number of the GNU Lesser |
|||
General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. |
|||
|
|||
@ -0,0 +1,269 @@ |
|||
# ABP.IO Platform 8.2 RC Has Been Released |
|||
|
|||
Today, we are happy to release the [ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) version **8.2 RC** (Release Candidate). This blog post introduces the new features and important changes in this new version. |
|||
|
|||
Try this version and provide feedback for a more stable version of ABP v8.2! Thanks to all of you. |
|||
|
|||
## Get Started with the 8.2 RC |
|||
|
|||
Follow the steps below to try version 8.2.0 RC today: |
|||
|
|||
1) **Upgrade** the ABP CLI to version `8.2.0-rc.3` using a command line terminal: |
|||
|
|||
````bash |
|||
dotnet tool update Volo.Abp.Cli -g --version 8.2.0-rc.3 |
|||
```` |
|||
|
|||
**or install** it if you haven't before: |
|||
|
|||
````bash |
|||
dotnet tool install Volo.Abp.Cli -g --version 8.2.0-rc.3 |
|||
```` |
|||
|
|||
2) Create a **new application** with the `--preview` option: |
|||
|
|||
````bash |
|||
abp new BookStore --preview |
|||
```` |
|||
|
|||
See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. |
|||
|
|||
> You can also use the [Get Started](https://abp.io/get-started) page to generate a CLI command to create a new application. |
|||
|
|||
You can use any IDE that supports .NET 8.x, like [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/). |
|||
|
|||
## Migration Guides |
|||
|
|||
There are a few breaking changes in this version that may affect your application. |
|||
Please see the following migration documents, if you are upgrading from v8.x or earlier: |
|||
|
|||
* [ABP Framework 8.x to 8.2 Migration Guide](https://docs.abp.io/en/abp/8.2/Migration-Guides/Abp-8_2) |
|||
* [ABP Commercial 8.x to 8.2 Migration Guide](https://docs.abp.io/en/commercial/8.2/migration-guides/v8_2) |
|||
|
|||
## What's New with ABP Framework 8.2? |
|||
|
|||
In this section, I will introduce some major features released in this version. |
|||
Here is a brief list of titles explained in the next sections: |
|||
|
|||
* Blazor Full-Stack Web App UI |
|||
* Introducing the `IBlockUiService` for Blazor UI |
|||
* Allowing Case-Insensitive Indexes for MongoDB |
|||
* Other News... |
|||
|
|||
### Blazor Full-Stack Web App UI |
|||
|
|||
ASP.NET Blazor in .NET 8 allows you to use a single powerful component model to handle all of your web UI needs, including server-side rendering, client-side rendering, streaming rendering, progressive enhancement, and much more! |
|||
|
|||
ABP v8.2.x supports the new Blazor Web App template, which you can directly create with the following command: |
|||
|
|||
```bash |
|||
abp new BookStore -t app -u blazor-webapp |
|||
``` |
|||
|
|||
When you create the project, you will typically see two main projects for Blazor UI, besides other projects: |
|||
|
|||
* **MyCompanyName.MyProjectName.Blazor.WebApp** (startup project of your application, and contains `App.razor` component, which is the root component of your application) |
|||
* **MyCompanyName.MyProjectName.Blazor.WebApp.Client** |
|||
|
|||
This new template overcomes the disadvantages of both Blazor WASM and Blazor Server applications and allows you to decide which approaches to use for a specific page or component. Therefore, you can imagine this new web UI as a combination of both Blazor Server and Blazor WASM. |
|||
|
|||
> This approach mainly 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**. |
|||
|
|||
> If you are considering migrating your existing Blazor project to Blazor WebApp or want to learn more about this new template, please read the [Migrating to Blazor Web App](https://docs.abp.io/en/abp/8.2/Migration-Guides/Abp-8-2-Blazor-Web-App) guide. |
|||
|
|||
### Introducing the `IBlockUiService` for Blazor UI |
|||
|
|||
In this version, ABP Framework introduces the [`IBlockUiService`](https://docs.abp.io/en/abp/8.2/UI/Blazor/Block-Busy) for Blazor UI. This service uses UI Block API to disable/block the page or a part of the page. |
|||
|
|||
You just need to simply inject the `IBlockUiService` to your page or component and call the `Block` or `UnBlock` method to block/unblock the specified element: |
|||
|
|||
```csharp |
|||
namespace MyProject.Blazor.Pages |
|||
{ |
|||
public partial class Index |
|||
{ |
|||
private readonly IBlockUiService _blockUiService; |
|||
|
|||
public Index(IBlockUiService _blockUiService) |
|||
{ |
|||
_blockUiService = blockUiService; |
|||
} |
|||
|
|||
public async Task BlockForm() |
|||
{ |
|||
/* |
|||
Parameters of Block method: |
|||
selectors: A string containing one or more selectors to match. https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector#selectors |
|||
busy : Set to true to show a progress indicator on the blocked area. |
|||
*/ |
|||
await _blockUiService.Block(selectors: "#MySelectors", busy: true); |
|||
|
|||
//Unblocking the element |
|||
await _blockUiService.UnBlock(selectors: "#MySelectors"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
Here is the resulting UI with all possible options (**block**, **block busy**, and **unblock**): |
|||
|
|||
 |
|||
|
|||
### Allowing Case-Insensitive Indexes for MongoDB |
|||
|
|||
MongoDB allows case-insensitive string comparisons by using case-insensitive indexes. You can create a case-insensitive index by specifying a **collation**. |
|||
|
|||
To do that, you should override the `CreateModal` method, configure the `CreateCollectionOptions`, and specify the **collation** as below: |
|||
|
|||
```csharp |
|||
protected override void CreateModel(IMongoModelBuilder modelBuilder) |
|||
{ |
|||
base.CreateModel(modelBuilder); |
|||
|
|||
modelBuilder.Entity<Question>(b => |
|||
{ |
|||
b.CreateCollectionOptions.Collation = new Collation(locale:"en_US", strength: CollationStrength.Secondary); |
|||
|
|||
b.ConfigureIndexes(indexes => |
|||
{ |
|||
indexes.CreateOne( |
|||
new CreateIndexModel<BsonDocument>( |
|||
Builders<BsonDocument>.IndexKeys.Ascending("MyProperty"), |
|||
new CreateIndexOptions { Unique = true } |
|||
) |
|||
); |
|||
} |
|||
); |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
After this configuration, a unique index will be created for the `MyProperty` property and then you can perform case-insensitive string comparisons without the need to worry. See [#19073](https://github.com/abpframework/abp/pull/19073) for more information. |
|||
|
|||
### Other News |
|||
|
|||
* Angular package version has been updated to v17.3.0. See [#19915](https://github.com/abpframework/abp/pull/19915) for more info. |
|||
* OpenIddict [5.4.0 has been released on March 26](https://github.com/openiddict/openiddict-core/releases/tag/5.4.0). Therefore, we decided to upgrade the OpenIddict packages to v5.4.0. See [#19427](https://github.com/abpframework/abp/issues/19427). |
|||
* AutoMapper [13.0.1 was released on February 8](https://github.com/AutoMapper/AutoMapper/releases/tag/v13.0.1) and in this version, we upgraded AutoMapper packages to v13.0.1. See [#19564](https://github.com/abpframework/abp/pull/19564/). |
|||
* See other completed tasks in this version: [https://github.com/abpframework/abp/releases?q=8.2.0-rc](https://github.com/abpframework/abp/releases?q=8.2.0-rc&expanded=true) |
|||
|
|||
## What's New with ABP Commercial 8.2? |
|||
|
|||
We've also worked on ABP Commercial to align the features and changes made in the ABP Framework. The following sections introduce a few new features coming with ABP Commercial 8.2. |
|||
|
|||
### Session Management |
|||
|
|||
The [Session Management](https://docs.abp.io/en/commercial/8.2/modules/identity/session-management) feature allows you to prevent concurrent login and manage user sessions. You can allow concurrent login, allow only one session of the same device type, or logout from all other devices when a new session is created, by specifying in the settings page: |
|||
|
|||
 |
|||
|
|||
Also, you can view and manage users sessions on the `Users` page of the [Identity Module](https://docs.abp.io/en/commercial/8.2/modules/identity): |
|||
|
|||
 |
|||
 |
|||
|
|||
### Suite: File/Image Property |
|||
|
|||
In this version, ABP Suite allows you to add a file/image property for an entity. You can select "File" as the property type for your properties as in the following figure: |
|||
|
|||
 |
|||
|
|||
Then, when you generate your entity and try to insert a record, you will see the file upload component on the create/update models: |
|||
|
|||
 |
|||
|
|||
You can upload a file with any supported extensions and under 10MB (this can be increased in the generated code, if you wish) and after that, you can download, delete and update the existing file any time you want: |
|||
|
|||
 |
|||
|
|||
> **Note:** This feature has already been implemented for MVC & Blazor UIs, but not implemented for Angular UI yet. We aim to implement it for Angular UI with v8.2.0. |
|||
|
|||
### Suite: DateOnly & TimeOnly Types |
|||
|
|||
In this version on, ABP Suite provides `DateOnly` and `TimeOnly` types as property types. You can select these types when you create an entity: |
|||
|
|||
 |
|||
|
|||
Then, all related configurations (including db configurations) will be made by ABP Suite, and you will be able to see the fields in the UI: |
|||
|
|||
 |
|||
|
|||
> **Note**: The `DateOnly` and `TimeOnly` types were introduced with .NET 6. Therefore, please make sure that all of your projects' target frameworks are .NET8+. With ABP v8.2, all startup templates target a single target framework, which is .NET8, so if you created your project with version 8.2+, you don't need to make any changes. |
|||
|
|||
### Periodic Log Deletion for Audit Logs |
|||
|
|||
In this version, the [Audit Logging Module](https://docs.abp.io/en/commercial/8.2/modules/audit-logging) provides a built-in periodic log deletion system. You can enable/disable the clean-up service system wide, in this way, you can turn off the clean up service for all tenants and their hosts: |
|||
|
|||
 |
|||
|
|||
> If the system wide clean up service is enabled, you can configure the global *Expired Item Deletion Period* for all tenants and hosts. |
|||
|
|||
When configuring the global settings for the audit log module from the host side in this manner, ensure that each tenant and host uses the global values. If you want to set tenant/host-specific values, you can do so under *Settings* -> *Audit Log* -> *General*. This way, you can disable the clean up service for specific tenants or host. It overrides the global settings: |
|||
|
|||
 |
|||
|
|||
> **Note**: To view the audit log settings, you need to enable the feature. For the host side, navigate to *Settings* -> *Feature Management* -> *Manage Host Features* -> *Audit Logging* -> *Enable audit log setting management*. |
|||
|
|||
## Community News |
|||
|
|||
### ABP Dotnet Conf 2024 Wrap Up |
|||
|
|||
 |
|||
|
|||
We organized [ABP Dotnet Conference 2024](https://abp.io/conference/2024) on May 2024 and we are happy to share the success of the conference, which captivated overwhelmingly interested live viewers from all over the world. 29 great line up of speakers which includes .NET experts and Microsoft MVPs delivered captivating talks that resonated with the audiences. Each of the talks attracted a great amount of interest and a lot of questions, sparking curiosity in the attendees. |
|||
|
|||
Thanks to all speakers and attendees for joining our event. 🙏 |
|||
|
|||
> We shared our takeaways in a blog post, which you can read at [https://blog.abp.io/abp/ABP-Dotnet-Conference-2024-Wrap-Up](https://blog.abp.io/abp/ABP-Dotnet-Conference-2024-Wrap-Up). |
|||
|
|||
### DevDays Europe 2024 |
|||
|
|||
 |
|||
|
|||
Co-founder of [Volosoft](https://volosoft.com/), [Alper Ebiçoğlu](https://twitter.com/alperebicoglu) gave a speech about "How to Build a Multi-Tenant ASP.NET Core Application" at the [DevDays Europe 2024](https://devdays.lt/) on the 20th of May. |
|||
|
|||
### DevOps Pro Europe 2024 |
|||
|
|||
 |
|||
|
|||
We are thrilled to announce that the co-founder of [Volosoft](https://volosoft.com/) and Lead Developer of the [ABP Framework](https://abp.io/), [Halil Ibrahim Kalkan](https://x.com/hibrahimkalkan) gave a speech about "Building a Kubernetes Integrated Local Development Environment" in the [DevOps Pro Europe](https://devopspro.lt/) on the 24th of May. |
|||
|
|||
### Devnot Dotnet Conference 2024 |
|||
|
|||
We are happy to announce that core team members of the [ABP Framework](https://abp.io/), [Alper Ebiçoğlu](https://twitter.com/alperebicoglu) and [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) will give speeches at the [Devnot Dotnet Conference 2024](https://dotnet.devnot.com/) on 25th of May. |
|||
|
|||
[Alper Ebiçoğlu](https://twitter.com/alperebicoglu) will talk about **"AspNet Core & Multitenancy"**: |
|||
|
|||
 |
|||
|
|||
On the other hand, [Enis Necipoğlu](https://twitter.com/EnisNecipoglu) will talk about **"Reactive Programming with .NET MAUI"**: |
|||
|
|||
 |
|||
|
|||
### New ABP Community Articles |
|||
|
|||
There are exciting articles contributed by the ABP community as always. I will highlight some of them here: |
|||
|
|||
* [Ahmed Tarek](https://twitter.com/AhmedTarekHasa1) has created **four** new community articles: |
|||
* [🤔 When Implementations Affect Abstractions ⁉️](https://community.abp.io/posts/-when-implementations-affect-abstractions--ekx1o5xn) |
|||
* [👍 Design Best Practices In .NET C# 👀](https://community.abp.io/posts/design-best-practices-in-.net-c--eg8q8xh0) |
|||
* [👍 Chain of Responsibility Design Pattern In .NET C# 👀](https://community.abp.io/posts/chain-of-responsibility-design-pattern-in-.net-c--djmvkug1) |
|||
* [Flagged Enumerations: How To Represent Features Combinations Into One Field](https://community.abp.io/posts/flagged-enumerations-how-to-represent-features-combinations-into-one-field-9gj4l670) |
|||
* [Engincan Veske](https://github.com/EngincanV) has created **three** new community articles: |
|||
* [Performing Case-Insensitive Search in ABP Based-PostgreSQL Application: Using citext and Collation](https://community.abp.io/posts/caseinsensitive-search-in-abp-basedpostgresql-application-c9kb05dc) |
|||
* [Sentiment Analysis Within ABP-Based Application](https://community.abp.io/posts/sentiment-analysis-within-abpbased-application-lbsfkoxq) |
|||
* [Reusing and Optimizing Machine Learning Models in .NET](https://community.abp.io/posts/reusing-and-optimizing-machine-learning-models-in-.net-qj4ycnwu) |
|||
* [Unlocking Modularity in ABP.io A Closer Look at the Contributor Pattern](https://community.abp.io/posts/unlocking-modularity-in-abp.io-a-closer-look-at-the-contributor-pattern-ixf6wgbw) by [Qais Al khateeb](https://community.abp.io/members/qais.alkhateeb@devnas-jo.com) |
|||
* [Deploy Your ABP Framework MVC Project to Azure Container Apps](https://community.abp.io/posts/deploy-your-abp-framework-mvc-project-to-azure-container-apps-r93u9c6d) by [Selman Koç](https://community.abp.io/members/selmankoc) |
|||
* [How claim type works in ASP NET Core and ABP Framework](https://community.abp.io/posts/how-claim-type-works-in-asp-net-core-and-abp-framework-km5dw6g1) by [Liming Ma](https://github.com/maliming) |
|||
* [Using FluentValidation with ABP Framework](https://community.abp.io/posts/using-fluentvalidation-with-abp-framework-2cxuwl70) by [Enes Döner](https://community.abp.io/members/Enes) |
|||
* [Using Blob Storage with ABP](https://community.abp.io/posts/using-blob-storage-with-abp-framework-jygtmhn4) by [Emre Kendirli](https://community.abp.io/members/emrekenderli) |
|||
|
|||
Thanks to the ABP Community for all the content they have published. You can also [post your ABP-related (text or video) content](https://community.abp.io/posts/submit) to the ABP Community. |
|||
|
|||
## Conclusion |
|||
|
|||
This version comes with some new features and a lot of enhancements to the existing features. You can see the [Road Map](https://docs.abp.io/en/abp/8.2/Road-Map) documentation to learn about the release schedule and planned features for the next releases. Please try ABP v8.2 RC and provide feedback to help us release a more stable version. |
|||
|
|||
Thanks for being a part of this community! |
|||
|
After Width: | Height: | Size: 443 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,134 @@ |
|||
# What is Object to Object Mapping? |
|||
|
|||
Mapping the properties of one object to the properties of another object is called **Object to Object Mapping**. Most of the time, you don't want to show the data you store in your database to end users as it is. Instead, you only return users the information they need for that operation and reduce the output size. |
|||
|
|||
For example, in database tables that contain relationships, we analyze the relationships and present meaningful data to users. Suppose we have a product and a category object, we keep a property called `categoryId` in the `Product` entity. However, it would be illogical to show the `categoryId ` property to users. Therefore, we can create a DTO (data transfer object) and show the **category name** to the end users, instead of the `categoryId` directly. |
|||
|
|||
DTOs are used to transfer data of objects from one object to another one. We often need to map our entities to DTOs and DTOs to entities. For example, consider the code below: |
|||
|
|||
````csharp |
|||
public virtual async Task<CustomerDto> CreateAsync(CustomerCreateDto input) |
|||
{ |
|||
|
|||
var customer = await _customerManager.CreateAsync( |
|||
input.BirthDay, input.MembershipDate, input.FirstName, input.LastName |
|||
); |
|||
CustomerDto customerDto = new CustomerDto |
|||
{ |
|||
Id = customer.Id, |
|||
FirstName = input.FirstName, |
|||
LastName = input.LastName, |
|||
// ...other |
|||
}; |
|||
|
|||
return customerDto; |
|||
} |
|||
|
|||
```` |
|||
As can be seen here, it's repetitive and tedious to manually map an object to another similar object. Also, it violates the [DRY principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself), makes your code more complicated and reduces readability. Instead of manually mapping objects, you can use the [AutoMapper](https://automapper.org/) library to automatically map two objects' properties: |
|||
|
|||
|
|||
````csharp |
|||
public virtual async Task<CustomerDto> CreateAsync(CustomerCreateDto input) |
|||
{ |
|||
|
|||
var customer = await _customerManager.CreateAsync( |
|||
input.BirthDay, input.MembershipDate, input.FirstName, input.LastName |
|||
); |
|||
|
|||
return ObjectMapper.Map<Customer, CustomerDto>(customer); |
|||
} |
|||
```` |
|||
The **ObjectMapper.Map** method allows you to convert your `Customer` entity to `CustomerDto`. `IObjectMapper` interface is a service, that comes from the **AutoMapper** library, so let's learn more about **AutoMapper** in the next section. |
|||
|
|||
# What is AutoMapper? |
|||
|
|||
Automapper is a .NET library that automates object-to-object mapping. ABP provides abstractions for object-to-object mapping and has an integration package to use [AutoMapper](http://automapper.org/) as the object mapper. |
|||
|
|||
Automapper is a library that transforms similar objects into each other. We can imagine Automapper as a machine that transforms an apple with a hat into an apple without a hat: |
|||
|
|||
 |
|||
|
|||
In this chapter, I will show you how to use the AutoMapper library in an ABP-based application. For this reason, I assume that you already have an ABP-based application created. If you have not yet created an ABP-based application, please follow the [Getting Started documentation](https://docs.abp.io/en/abp/latest/Getting-Started-Create-Solution?UI=MVC&DB=EF&Tiered=No). |
|||
|
|||
Create a domain entity similar to this one: |
|||
|
|||
````csharp |
|||
public class Customer : FullAuditedAggregateRoot<Guid> |
|||
{ |
|||
public string? FirstName { get; set; } |
|||
public string? LastName { get; set; } |
|||
public DateTime BirthDay { get; set; } |
|||
public DateTime MembershipDate { get; set; } |
|||
} |
|||
```` |
|||
`Customer` entity contains some properties (such as `FirstName`, `LastName`, ... and other audited properties coming from the base class - `DeleterId`, `IsDeleted`, `CreationTime` etc. -). Typically, you would not want to show/return all of these properties to end users, at that point, you can create a DTO class and only define the properties that you want to return to the end users. |
|||
|
|||
Let's create the `CustomerGetDto` class in the `*.Application.Contracts` project as follows: |
|||
|
|||
````csharp |
|||
public class CustomerGetDto |
|||
{ |
|||
public string? FirstName { get; set; } |
|||
public string? LastName { get; set; } |
|||
public DateTime BirthDay { get; set; } |
|||
} |
|||
```` |
|||
|
|||
After creating our entity and output DTO classes, now in the application service implementation, we can return the `CustomerGetDto` class, as a result of listing the customers. For that reason, we can write a code as follows: |
|||
|
|||
````csharp |
|||
public virtual async Task<PagedResultDto<CustomerGetDto>> GetListAsync(GetCustomersInput input) |
|||
{ |
|||
var totalCount = await _customerRepository.GetCountAsync(input.FilterText, input.FirstName, input.LastName, input.BirthDayMin, input.BirthDayMax, input.MembershipDateMin, input.MembershipDateMax); |
|||
var items = await _customerRepository.GetListAsync(input.FilterText, input.FirstName, input.LastName, input.BirthDayMin, input.BirthDayMax, input.MembershipDateMin, input.MembershipDateMax, input.Sorting, input.MaxResultCount, input.SkipCount); |
|||
|
|||
return new PagedResultDto<CustomerGetDto> |
|||
{ |
|||
TotalCount = totalCount, |
|||
Items = ObjectMapper.Map<List<Customer>, List<CustomerGetDto>>(items) |
|||
}; |
|||
} |
|||
```` |
|||
In this code, we first get the total number of our customers and all customers according to the specified filters, then we map `List<Customer>` to `List<CustomerGetDto>` using the `ObjectMapper.Map` method from the **ApplicationService** base class. This way we have full control over which properties are returned to the end users. |
|||
|
|||
After mapping the `Customer` entity to the `CustomerGetDto` class. Before running our application, we should define the mappings in the `*AutoMapperProfile` class in the `*.Application` project as follows: |
|||
|
|||
````csharp |
|||
public class YourApplicationAutoMapperProfile : Profile |
|||
{ |
|||
public YourApplicationAutoMapperProfile() |
|||
{ |
|||
CreateMap<Customer, CustomerGetDto>(); |
|||
} |
|||
} |
|||
```` |
|||
Finally, we can run our application and navigate to the **/swagger** endpoint to try our endpoint. When we send a request, we should get the result as follows: |
|||
|
|||
 |
|||
|
|||
## Advanced: Mapping Configurations |
|||
|
|||
In some scenarios, you may want to make some customizations when mapping two objects. For example, let's assume that you want to create the `CustomerGetDto` class as follows: |
|||
|
|||
````csharp |
|||
public class CustomerGetDto |
|||
{ |
|||
public string? FullName { get; set; } |
|||
public int Age { get; set; } |
|||
} |
|||
```` |
|||
AutoMapper can't map these properties automatically, because they do not exist in the source object, which is the `Customer` entity. Therefore, you need to specify the exception and update your `YourApplicationAutoMapperProfile` class as follows: |
|||
|
|||
````csharp |
|||
|
|||
CreateMap<Customer, CustomerGetDto>().ForMember(c=>c.FullName,opt=> opt.MapFrom(src => src.FirstName + " " + src.LastName)) |
|||
.ForMember(c=>c.Age, opt=> opt.MapFrom(src=> DateTime.UtcNow.Year -src.BirthDay.Year)); |
|||
|
|||
```` |
|||
This configuration concatenates and matches **FirstName** and **LastName** properties into the **FullName** property and subtracts **BirthDate** from today's year and assigns it to the customer's **Age**. |
|||
After these configurations, if you make a request to the relevant endpoint, the output will look like: |
|||
|
|||
 |
|||
|
|||
For more information on object-to-object mapping with [ABP Framework](https://abp.io/), see the [documentation](https://docs.abp.io/en/abp/latest/Object-To-Object-Mapping). |
|||
@ -0,0 +1,228 @@ |
|||
# Deploy Your ABP Framework Angular Project to Azure Kubernetes Service (AKS) |
|||
|
|||
 |
|||
|
|||
In my previous article on [Deploy Your ABP Framework MVC Project to Azure Container Apps](https://community.abp.io/posts/deploy-your-abp-framework-mvc-project-to-azure-container-apps-r93u9c6d), I talked about how ABP Mvc project can be easily deployed to Azure Container Apps. Now I will show how we can deploy to kubernetes environment, which looks a bit more complex but is more preferred for production, using a Helm chart. |
|||
|
|||
### Getting Started with ABP Framework Angular and Azure Kubernetes Service |
|||
|
|||
To get started, you will need an ABP Framework Angular project that you want to deploy. If you don't have one, you can [create a new project using the ABP CLI](https://docs.abp.io/en/abp/latest/Startup-Templates/Application). You will also need [an Azure subscription](https://azure.microsoft.com) and [an Azure Kubernetes Service](https://azure.microsoft.com/en-gb/services/kubernetes-service/). |
|||
|
|||
### Configuring Your ABP Framework Angular Project |
|||
|
|||
We have a sample ABP Framework Angular project that we will use for this deployment. Before creating the Docker image and Helm chart, you just need to configure `aspnet-core/src/***.HttpApi.Host/***.HttpApiHostModule.cs` file to allow CORS requests from your frontend application. You can do this by updating the following code to the `ConfigureServices` method: |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
|||
|
|||
if (!configuration.GetValue<bool>("App:DisablePII")) |
|||
{ |
|||
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true; |
|||
} |
|||
|
|||
if (!configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata")) |
|||
{ |
|||
Configure<OpenIddictServerAspNetCoreOptions>(options => |
|||
{ |
|||
options.DisableTransportSecurityRequirement = true; |
|||
}); |
|||
} |
|||
context.Services.Configure<ForwardedHeadersOptions>(options => |
|||
{ |
|||
options.ForwardedHeaders = ForwardedHeaders.XForw |
|||
``` |
|||
|
|||
 |
|||
|
|||
If your ABP Framework Angular project and Azure kubernetes cluster ready, we can start to build the docker images and pushing them to any container registry. In this article, I will use DockerHub as the container registry. |
|||
|
|||
I will also show you how I automated the steps that I originally did manually to make it simpler in the beginning and then automated them in Azure Devops. |
|||
|
|||
### Creating a Docker Image for ABP Framework Angular |
|||
|
|||
To create a Docker image for your ABP Framework Angular project, navigate to `etc/build/build-images-locally.ps1` and fix the script to match your Docker Hub username and image name. Then, run the script to build the Docker image locally. |
|||
|
|||
 |
|||
|
|||
At the end of this process, check your Docker Hub repository to confirm that the image has been pushed successfully. My Docker Hub repository looks like this. Also you can use these my public images to test the deployment. |
|||
|
|||
 |
|||
|
|||
### Creating Helm Chart for ABP Framework Angular |
|||
|
|||
To deploy your ABP Framework Angular project to Azure Kubernetes Service, you need to create a Helm chart. Helm is a package manager for Kubernetes that allows you to define, install, and upgrade even the most complex Kubernetes applications. Helm uses a packaging format called charts, which are a collection of files that describe a related set of Kubernetes resources. |
|||
|
|||
These helm charts are prepared to create a deployment, configmap, service and ingress for your ABP Framework Angular project. It is prepared not only for migration, frontend and backend, but also to create the sqlserver and redis that the application needs as a kubernetes service. If you already have redis and sqlserver, you don't need to stand them up in kubernetes, of course. |
|||
|
|||
 |
|||
|
|||
You can find the helm chart in the https://github.com/skoc10/k8s_works/tree/main/demo/helm/k8s/angular repository. You can configure the `values-azure.yaml` file according to your needs. |
|||
|
|||
### Deploying to Azure Kubernetes Service |
|||
|
|||
`Note:` You need to have nginx-ingress-controller and cert-manager installed for letsencrypt certificate in your kubernetes cluster. |
|||
|
|||
Now that you have Docker images for your ABP Framework Angular project and a Helm chart, you can proceed to deploy it to Azure Kubernetes Service. To do this, you need to create a new Azure Kubernetes Service resource and configure the `values-azure.yaml` file according to your needs. If you want, you can deploy a single Helm with `demo/helm/k8s/deploy-staging.ps1` script you can deploy each chart separately. |
|||
|
|||
 |
|||
|
|||
After deploying the Helm chart, you can check the deployment status to confirm that the deployment was successful. You can also check the logs of the pods to see if there are any errors. |
|||
|
|||
 |
|||
|
|||
Finally, you can navigate to the web url to see your ABP Framework Angular project running in Azure Kubernetes Service. |
|||
|
|||
 |
|||
|
|||
### CI/CD with Azure DevOps |
|||
|
|||
I have automated the steps that I originally did manually to make it simpler in the beginning and then automated them in Azure Devops. You can find the `azure-pipelines.yml` file in the https://github.com/skoc10/k8s_works/blob/main/demo/azure/azure-pipelines.yml repository. You can configure the `azure-pipelines.yml` file according to your needs. |
|||
|
|||
```yaml |
|||
trigger: |
|||
tags: |
|||
include: |
|||
- "*.*.*" |
|||
|
|||
variables: |
|||
dockerRegistryServiceConnection: 'demo-reg' |
|||
buildContextBasePath: '$(Build.SourcesDirectory)' |
|||
tag: $(Build.BuildNumber) |
|||
DOCKER_BUILDKIT: 1 |
|||
|
|||
pool: |
|||
name: ubuntu |
|||
|
|||
stages: |
|||
- stage: Build |
|||
displayName: Build |
|||
jobs: |
|||
- job: CheckChanges |
|||
displayName: Check if Angular or ASP.NET Core has changed |
|||
pool: |
|||
name: ubuntu |
|||
steps: |
|||
- checkout: self |
|||
|
|||
# Migration |
|||
- task: Docker@2 |
|||
displayName: 'Build Migration Docker image' |
|||
inputs: |
|||
command: build |
|||
repository: demo-angular-apppro/migration |
|||
dockerfile: $(buildContextBasePath)/aspnet-core/src/Demo.AzureAppsAngular.DbMigrator/Dockerfile.azure |
|||
buildContext: $(buildContextBasePath)/aspnet-core |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: Docker@2 |
|||
displayName: 'Push Migration Docker image' |
|||
inputs: |
|||
command: push |
|||
repository: demo-angular-apppro/migration |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: HelmDeploy@0 |
|||
displayName: 'Delete Migrator' |
|||
inputs: |
|||
connectionType: Kubernetes Service Connection |
|||
kubernetesServiceConnection: 'aks-demoms' |
|||
namespace: 'angular' |
|||
command: delete |
|||
arguments: dbmigrator |
|||
continueOnError: true |
|||
|
|||
- task: HelmDeploy@0 |
|||
displayName: 'Deploy Migration to AKS' |
|||
inputs: |
|||
connectionType: Kubernetes Service Connection |
|||
kubernetesServiceConnection: 'aks-demoms' |
|||
namespace: 'angular' |
|||
command: 'upgrade' |
|||
chartType: 'FilePath' |
|||
chartPath: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/dbmigrator' |
|||
releaseName: 'dbmigrator' |
|||
overrideValues: 'image.tag=$(tag)' |
|||
valueFile: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/dbmigrator/values.yaml' |
|||
waitForExecution: false |
|||
|
|||
# Backend |
|||
- task: Docker@2 |
|||
displayName: 'Build Backend Docker image' |
|||
inputs: |
|||
command: build |
|||
repository: demo-angular-apppro/backend |
|||
dockerfile: $(buildContextBasePath)/aspnet-core/src/Demo.AzureAppsAngular.HttpApi.Host/Dockerfile.azure |
|||
buildContext: $(buildContextBasePath)/aspnet-core |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: Docker@2 |
|||
displayName: 'Push Backend Docker image' |
|||
inputs: |
|||
command: push |
|||
repository: demo-angular-apppro/backend |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: HelmDeploy@0 |
|||
displayName: 'Deploy Backend to AKS' |
|||
inputs: |
|||
connectionType: Kubernetes Service Connection |
|||
kubernetesServiceConnection: 'aks-demoms' |
|||
namespace: 'angular' |
|||
command: 'upgrade' |
|||
chartType: 'FilePath' |
|||
chartPath: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/backend' |
|||
releaseName: 'backend' |
|||
overrideValues: 'image.tag=$(tag)' |
|||
valueFile: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/backend/values.yaml' |
|||
waitForExecution: false |
|||
|
|||
# Frontend |
|||
- task: Docker@2 |
|||
displayName: 'Build Frontend Docker image' |
|||
inputs: |
|||
command: build |
|||
repository: demo-angular-apppro/frontend |
|||
dockerfile: $(buildContextBasePath)/angular/Dockerfile.azure |
|||
buildContext: $(buildContextBasePath)/angular |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: Docker@2 |
|||
displayName: 'Push Frontend Docker image' |
|||
inputs: |
|||
command: push |
|||
repository: demo-angular-apppro/frontend |
|||
containerRegistry: $(dockerRegistryServiceConnection) |
|||
tags: | |
|||
$(tag) |
|||
|
|||
- task: HelmDeploy@0 |
|||
displayName: 'Deploy Frontend to AKS' |
|||
inputs: |
|||
connectionType: Kubernetes Service Connection |
|||
kubernetesServiceConnection: 'aks-demoms' |
|||
namespace: 'angular' |
|||
command: 'upgrade' |
|||
chartType: 'FilePath' |
|||
chartPath: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/angular' |
|||
releaseName: 'frontend' |
|||
overrideValues: 'image.tag=$(tag)' |
|||
valueFile: '$(buildContextBasePath)/aspnet-core/etc/k8s/angular/charts/angular/values.yaml' |
|||
waitForExecution: false |
|||
``` |
|||
|
|||
### Conclusion |
|||
|
|||
In this article, I showed you how you can deploy your ABP Framework Angular project to Azure Kubernetes Service using Helm chart. I also showed you how you can automate the deployment process using Azure DevOps. I hope you found this article helpful. If you have any questions or feedback, please feel free to leave a comment below. |
|||
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1,61 @@ |
|||
# ABP Version 8.2 Migration Guide |
|||
|
|||
This document is a guide for upgrading ABP v8.x solutions to ABP v8.2. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. |
|||
|
|||
## Updated target frameworks to net8.0 for template projects |
|||
|
|||
`TargetFrameworks` of the following template projects are upgraded to **net8.0**: |
|||
|
|||
* `*.Application.Contracts` |
|||
* `*.Domain.Shared` |
|||
* `*.Domain` |
|||
* `*.MongoDB` |
|||
* `*.HttpApi.Client` |
|||
* `*.Host.Shared` (for module template) |
|||
|
|||
Before this version, all of the projects above were targeting multiple frameworks (**netstandard2.0**, **netstandard2.1** and **net8.0**), with this version, we started to only target **net8.0** for these template projects. Note that, all other shared libraries still target multiple frameworks. |
|||
|
|||
> This change should not affect your pre-existing solutions and you don't need to make any changes in your application. See the PR for more info: https://github.com/abpframework/abp/pull/19565 |
|||
|
|||
## Upgraded AutoMapper to 13.0.1 |
|||
|
|||
In this version, **AutoMapper** library version upgraded to 13.0.1. See [the release notes of AutoMapper v13.0.1](https://github.com/AutoMapper/AutoMapper/releases/tag/v13.0.1) for more information. |
|||
|
|||
## Added default padding to `.tab-content` class for Basic Theme |
|||
|
|||
In this version, default padding (padding-top: 1.5rem and padding-bottom: 1.5rem) has been added to the tab-content for the Basic Theme. See [#19475](https://github.com/abpframework/abp/pull/19475) for more information. |
|||
|
|||
## Moved members page directory for Blogging Module |
|||
|
|||
With this version on, ABP Framework allows you to use single blog mode, without needing to define a blog and a prefix. With this change, the following namespace changes were done: |
|||
* `Volo.Blogging.Pages.Blog` -> `Volo.Blogging.Pages.Blogs` |
|||
* `Volo.Blogging.Pages.Members` -> `Volo.Blogging.Pages.Blogs.Members` (members folder) |
|||
|
|||
> If you haven't overridden the pages above, then you don't need to make any additional changes. See [#19418](https://github.com/abpframework/abp/pull/19418) for more information. |
|||
|
|||
## Removed `FlagIcon` property from the `ILanguageInfo` |
|||
|
|||
The `FlagIcon` property has been removed from the `ILanguageInfo` interface since we removed the flag icon library in the earlier versions from all of our themes and none of them using it now. |
|||
|
|||
If the flag icon has been specified while defining the localization languages, then it should be removed: |
|||
|
|||
```diff |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
- options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi", "in")); |
|||
+ options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi")); |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
## Blazor Full-Stack Web UI |
|||
|
|||
In this version, ABP Framework provides a new UI option called **Blazor Full-Stack WebApp**. We have already created an introduction/migration guide for you to check it: [Migrating to Blazor Web App](Abp-8-2-Blazor-Web-App.md) |
|||
|
|||
> Please read the documentation carefully if you are considering migrating your existing **Blazor** project to **Blazor WebApp**. |
|||
|
|||
## Session Management Infrastructure |
|||
|
|||
The **Session Management** feature allows you to prevent concurrent login and manage user sessions. |
|||
|
|||
In this version, a new entity called `IdentitySession` has been added to the framework and you should create a new migration and apply it to your database. |
|||
@ -0,0 +1,59 @@ |
|||
# Title Strategy For Angular |
|||
|
|||
## **ABP has a default title strategy for Angular UI**. |
|||
|
|||
This strategy is based on the title property. Provide a title property when setting a new route. |
|||
|
|||
**Example** |
|||
|
|||
```ts |
|||
{ |
|||
path: 'customers', |
|||
component: CustomersComponent, |
|||
title: 'AbpCustomers::Roles' |
|||
}, |
|||
``` |
|||
|
|||
- It is better to use localized text in the title property. It will be translated by **LocalizationService**. |
|||
- The **`title`** property is already set in **ABP internal packages**. |
|||
|
|||
## How it looks |
|||
|
|||
When you create a new route and provide a **`title`** property, it will look like this **`<title> | <projectName>`** |
|||
|
|||
### What is `projectName` and How to Customize |
|||
|
|||
- **`projectName`** is the name of your application. By default, ABP sets a [**`projectName`**](https://github.com/abpframework/abp/blob/f48f78618a326644843c01424b093f0d79448769/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json#L4) for your application. This localization text is added for customization and can be changed for different languages. |
|||
|
|||
### Disable `projectName` |
|||
|
|||
- If you don't want to show **`projectName`** in the title, you can disable it. |
|||
|
|||
**app.module.ts** |
|||
|
|||
```ts |
|||
import { DISABLE_PROJECT_NAME } from '@abp/ng.core'; |
|||
|
|||
providers: [ |
|||
..., |
|||
{ provide: DISABLE_PROJECT_NAME, useValue: true} |
|||
], |
|||
``` |
|||
|
|||
- Now only title will be shown. |
|||
|
|||
## Override ABP's Default Title Strategy |
|||
|
|||
**app.module.ts** |
|||
|
|||
```ts |
|||
import { TitleStrategy } from '@angular/router'; |
|||
import { YourCustomStrategy } from './title-strategy.service.ts'; |
|||
|
|||
providers: [ |
|||
..., |
|||
{ provide: TitleStrategy, useExisting: YourCustomStrategy }, |
|||
], |
|||
``` |
|||
|
|||
- You can check [Angular Documentation](https://angular.io/api/router/TitleStrategy) to write a custom **`TitleStrategy`**. |
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using Microsoft.Extensions.FileProviders; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class CachedBundleDynamicFileProvider : DynamicFileProvider |
|||
{ |
|||
protected IDistributedCache<InMemoryFileInfoCacheItem> Cache { get; } |
|||
protected IOptions<AbpBundlingOptions> BundlingOptions { get; } |
|||
|
|||
public CachedBundleDynamicFileProvider( |
|||
IDistributedCache<InMemoryFileInfoCacheItem> cache, |
|||
IOptions<AbpBundlingOptions> bundlingOptions) |
|||
{ |
|||
Cache = cache; |
|||
BundlingOptions = bundlingOptions; |
|||
} |
|||
|
|||
public override IFileInfo GetFileInfo(string? subpath) |
|||
{ |
|||
var fileInfo = base.GetFileInfo(subpath); |
|||
|
|||
if (!subpath.IsNullOrWhiteSpace() && fileInfo is NotFoundFileInfo && |
|||
subpath.Contains(BundlingOptions.Value.BundleFolderName, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
var filePath = NormalizePath(subpath); |
|||
var cacheItem = Cache.Get(filePath); |
|||
if (cacheItem == null) |
|||
{ |
|||
return fileInfo; |
|||
} |
|||
|
|||
fileInfo = new InMemoryFileInfo(filePath, cacheItem.FileContent, cacheItem.Name); |
|||
DynamicFiles.AddOrUpdate(filePath, fileInfo, (key, value) => fileInfo); |
|||
} |
|||
|
|||
return fileInfo; |
|||
} |
|||
|
|||
public override void AddOrUpdate(IFileInfo fileInfo) |
|||
{ |
|||
var filePath = fileInfo.GetVirtualOrPhysicalPathOrNull(); |
|||
Cache.GetOrAdd(filePath!, () => new InMemoryFileInfoCacheItem(filePath!, fileInfo.ReadBytes(), fileInfo.Name)); |
|||
base.AddOrUpdate(fileInfo); |
|||
} |
|||
|
|||
public override bool Delete(string filePath) |
|||
{ |
|||
Cache.Remove(filePath); |
|||
return base.Delete(filePath); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
[Serializable] |
|||
[IgnoreMultiTenancy] |
|||
public class InMemoryFileInfoCacheItem |
|||
{ |
|||
public InMemoryFileInfoCacheItem(string dynamicPath, byte[] fileContent, string name) |
|||
{ |
|||
DynamicPath = dynamicPath; |
|||
Name = name; |
|||
FileContent = fileContent; |
|||
} |
|||
|
|||
public string DynamicPath { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public byte[] FileContent { get; set; } |
|||
} |
|||