diff --git a/Directory.Packages.props b/Directory.Packages.props index c155d760ae..854c3879ee 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json index 6ebb308c74..4af2cafcd3 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/ar.json @@ -91,7 +91,7 @@ "JoinOurMarketingNewsletter": "انضم إلى النشرة الإخبارية التسويقية", "CommunityPrivacyPolicyConfirmation": "أوافق على الشروط والأحكام و سياسة الخصوصية.", "WouldLikeToReceiveNotification": "أرغب في تلقي آخر الأخبار من مواقع abp.io.", - "CommercialNewsletterConfirmationMessage": "أنا أوافق على البنود و الظروف و سياسة الخصوصية.", + "CommercialNewsletterConfirmationMessage": "أنا أوافق على البنود و الظروف و سياسة الخصوصية.", "FreeDDDEBook": "كتاب تصميم مقاد بالنطاق DDD إلكتروني مجاني", "AdditionalServices": "خدمات إضافية", "Learn": "تعلم", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json index 02bafb53c3..eafcd1b458 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/en.json @@ -91,7 +91,7 @@ "JoinOurMarketingNewsletter": "Join our marketing newsletter", "CommunityPrivacyPolicyConfirmation": "I agree to the Terms & Conditions and Privacy Policy.", "WouldLikeToReceiveNotification": "I would like to receive the latest news from abp.io websites.", - "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", + "CommercialNewsletterConfirmationMessage": "I agree to the Terms & Conditions and Privacy Policy.", "FreeDDDEBook": "Free DDD E-Book", "AdditionalServices": "Additional Services", "Learn": "Learn", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/fi.json index cfd1a41846..49830c2902 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/fi.json @@ -91,7 +91,7 @@ "JoinOurMarketingNewsletter": "Liity markkinointiuutiskirjeeseemme", "CommunityPrivacyPolicyConfirmation": "Hyväksyn käyttöehdot ja tietosuojakäytännön .", "WouldLikeToReceiveNotification": "Haluan saada viimeisimmät uutiset abp.io-sivustoilta.", - "CommercialNewsletterConfirmationMessage": "Hyväksyn käyttöehdot ja tietosuojakäytännön.", + "CommercialNewsletterConfirmationMessage": "Hyväksyn käyttöehdot ja tietosuojakäytännön.", "FreeDDDEBook": "Ilmainen DDD e-kirja", "AdditionalServices": "Lisäpalvelut", "Learn": "Opi", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json index 74b73630b2..ff8849ea1f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/hu.json @@ -91,7 +91,7 @@ "JoinOurMarketingNewsletter": "Csatlakozzon marketing hírlevelünkhöz", "CommunityPrivacyPolicyConfirmation": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot.", "WouldLikeToReceiveNotification": "Szeretném megkapni a legfrissebb híreket az abp.io webhelyekről.", - "CommercialNewsletterConfirmationMessage": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot .", + "CommercialNewsletterConfirmationMessage": "Elfogadom az Általános Szerződési Feltételeket és az Adatvédelmi szabályzatot .", "FreeDDDEBook": "Ingyenes DDD e-könyv", "AdditionalServices": "További szolgáltatások", "Learn": "Tanulás", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json index ee4f7ca6e9..0e47312764 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/tr.json @@ -83,7 +83,7 @@ "JoinOurMarketingNewsletter": "Pazarlama bültenimize katılın", "CommunityPrivacyPolicyConfirmation": "Şartları, Koşulları ve Gizlilik Politikasını kabul ediyorum.", "WouldLikeToReceiveNotification": "abp.io web sitelerinden en son haberleri almak istiyorum.", - "CommercialNewsletterConfirmationMessage": "Şartlar ve Koşulları ve Gizlilik Politikasını kabul ediyorum.", + "CommercialNewsletterConfirmationMessage": "Şartlar ve Koşulları ve Gizlilik Politikasını kabul ediyorum.", "FreeDDDEBook": "Ücretsiz DDD E-Kitabı", "AdditionalServices": "Ek Hizmetler", "Learn": "Öğren", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json index f4b905eb81..f162a5f5f6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Base/Localization/Resources/zh-Hans.json @@ -91,7 +91,7 @@ "JoinOurMarketingNewsletter": "加入我们市场推广时事通讯", "CommunityPrivacyPolicyConfirmation": "我同意条款 & 条件和隐私政策 。", "WouldLikeToReceiveNotification": "我想从 abp.io 网站接收最新消息。", - "CommercialNewsletterConfirmationMessage": "我同意条款 & 条件隐私政策。", + "CommercialNewsletterConfirmationMessage": "我同意条款 & 条件隐私政策。", "FreeDDDEBook": "免费 DDD 电子书", "AdditionalServices": "额外服务", "Learn": "学习", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 856d4d755c..23e81238cd 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -849,7 +849,7 @@ "BlazoriseSupportExplanation5": "You can post your questions on the support website and generate a product token for your application.", "AbpLiveTrainingPackages": "ABP Live Training Packages", "Releases": "Releases", - "ReleasesDescription": "Release logs of ABP Commercial.", + "ReleasesDescription": "This page contains detailed information about each release. You can see all the closed pull requests for a specific release. For overall milestone developments, you can check out the brief release notes page.", "ReleaseDate": "Release Date", "Labels": "Labels", "PreRelease": "Pre-release", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index f063df215e..efbdd38748 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -191,7 +191,7 @@ "DiscordPageTitle": "ABP Discord Community", "ViewVideo": "View Video", "AbpCommunityTitleContent": "ABP Community - Open Source ABP Framework", - "CommunitySlogan": "A unique community platform for ABP Lovers", + "CommunitySlogan": "A unique community platform for ABP Lovers", "RaffleIsNotActive": "Raffle is not active", "YouAreAlreadyJoinedToThisRaffle": "You are already joined to this raffle", "InvalidSubscriptionCode": "Invalid subscription code", diff --git a/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/POST.md b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/POST.md new file mode 100644 index 0000000000..2f40b61851 --- /dev/null +++ b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/POST.md @@ -0,0 +1,84 @@ +# ABP.IO Platform 8.0 Has Been Released Based on .NET 8.0 + +Today, [ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 8.0 versions have been released based on [.NET 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0). + +## What's New With Version 8.0? + +All the new features were explained in detail in the [8.0 RC Announcement Post](https://blog.abp.io/abp/announcing-abp-8-0-release-candidate), so there is no need to review them again. You can check it out for more details. + +## Getting Started with 8.0 + +### Creating New Solutions + +You can create a new solution with the ABP Framework version 8.0 by either using the `abp new` command or generating the CLI command on the [get started page](https://abp.io/get-started). + +> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for more. + +### How to Upgrade an Existing Solution + +#### Install/Update the ABP CLI + +First, install the ABP CLI or upgrade it to the latest version. + +If you haven't installed it yet: + +```bash +dotnet tool install -g Volo.Abp.Cli +``` + +To update the existing CLI: + +```bash +dotnet tool update -g Volo.Abp.Cli +``` + +#### Upgrading Existing Solutions with the ABP Update Command + +[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command: + +```bash +abp update +``` + +Run this command in the root folder of your solution. + +## Migration Guides + +There are breaking changes in this version that may affect your application. +Please see the following migration documents if you are upgrading from v7.x or earlier: + +* [ABP Framework 7.x to 8.0 Migration Guide](https://docs.abp.io/en/abp/8.0/Migration-Guides/Abp-8_0) +* [ABP Commercial 7.x to 8.0 Migration Guide](https://docs.abp.io/en/commercial/8.0/migration-guides/v8_0) + +## Community News + +### We were at China .NET Conf 2023 + +China's most influential .NET event officially kicked off on December 16, 2023. The conference has invited 30+ technical experts from various fields to share the new features of .NET 8, full-stack Blazor, AI, .NET MAUI, and more... + +![](abp-china-team.jpeg) + +As one of the community partners of .NET Conf China 2023, our ABP.IO China team was at the event. At the event, we showed developers the latest news and related updates on ABP.IO. + +Through this event, we gained a lot and felt the enthusiasm and support of the developers community for ABP.IO. If you want to learn more, we have shared our impressions and takeaways in a blog post, which you can find at [https://blog.abp.io/abp/ABP-at-China-NET-Conf-2023](https://blog.abp.io/abp/ABP-at-China-NET-Conf-2023). + +### ABP Commercial Won 5 Recognitions from Gartner + +![](awards.jpeg) + +2023 was the year for ABP.IO to strive with the community. On top of last year's [Software Advice's Front Runner of Application Development in 2022](https://blog.abp.io/abp/abpcommercial-2022-front-runner-in-app-development-category) and [GetApp's Application Development Category Leader in 2022](https://blog.abp.io/abp/abpcommercial-2022-category-leader-in-app-development-category), we won **5 awards** this year from **Gartner**! + +> If you are interested in these awards and want to learn more, you can check out our [blog post](https://blog.abp.io/abp/ABP-Commercial-Won-5-Recognitions-from-Gartner-in-2023)! + +### New ABP Community Posts + +There are exciting articles contributed by the ABP community, as always. I will highlight some of them here: + +* [Performance Optimization of .NET-based application](https://community.abp.io/posts/performance-optimization-of-.netbased-and-also-abpbased-application-pmdwhwxc) by [Leon Košak](https://github.com/leonkosak) +* [Video: ABP Framework Consuming HTTP APIs from a .NET Client](https://community.abp.io/videos/abp-framework-consuming-http-apis-from-a-.net-client-uzul9og4) by [Hamza Albreem](https://github.com/braim23) + +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/articles/submit) to the ABP Community. + +## About the Next Version + +The next feature version will be 8.1. You can follow the [release planning here](https://github.com/abpframework/abp/milestones). Please [submit an issue](https://github.com/abpframework/abp/issues/new) if you have any problems with this version. diff --git a/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/abp-china-team.jpeg b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/abp-china-team.jpeg new file mode 100644 index 0000000000..d9f062c4cc Binary files /dev/null and b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/abp-china-team.jpeg differ diff --git a/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/awards.jpeg b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/awards.jpeg new file mode 100644 index 0000000000..42dc55846f Binary files /dev/null and b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/awards.jpeg differ diff --git a/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/cover-image.png b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/cover-image.png new file mode 100644 index 0000000000..5ebaa2ba36 Binary files /dev/null and b/docs/en/Blog-Posts/2023-12-20 v8_0_Release_Stable/cover-image.png differ diff --git a/docs/en/Entity-Cache.md b/docs/en/Entity-Cache.md index 834a0d5313..6eaa186e53 100644 --- a/docs/en/Entity-Cache.md +++ b/docs/en/Entity-Cache.md @@ -45,13 +45,13 @@ public class ProductAppService : ApplicationService, IProductAppService } ``` -> Note that we've used the `ObjectMapper` service to map from `Product` to `ProductDto`. You should configure that [object mapping](Object-To-Object-Mapping.md) to make that example service properly works. +> Note that we've used the `ObjectMapper` service to map from `Product` to `ProductDto`. You should configure that [object mapping](Object-To-Object-Mapping.md) to make that example service properly work. -That's all. The cache name (in the distributed cache server) will be full name (with namespace) of the `Product` class. You can use the `[CacheName]` attribute to change it. Please refer to the [caching document](Caching.md) for details. +That's all. The cache name (in the distributed cache server) will be the full name (with namespace) of the `Product` class. You can use the `[CacheName]` attribute to change it. Please refer to the [caching document](Caching.md) for details. ## Using a Cache Item Class -In the previous section, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable to JSON (and deserializable from JSON). Sometimes that might not be possible or you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object if the `Product` entity. +In the previous section, we've directly cached the `Product` entity. In that case, the `Product` class must be serializable to JSON (and deserializable from JSON). Sometimes that might not be possible or you may want to use another class to store the cache data. For example, we may want to use the `ProductDto` class instead of the `Product` class for the cached object of the `Product` entity. Assume that we've created a `ProductDto` class as shown below: @@ -102,7 +102,7 @@ public class ProductAppService : ApplicationService, IProductAppService } ``` -Notice that the `_productCache.GetAsync` method already returns a `ProductDto` object, so we could directly return it from out application service. +Notice that the `_productCache.GetAsync` method already returns a `ProductDto` object, so we could directly return it from our application service. ## Configuration @@ -122,7 +122,7 @@ context.Services.AddEntityCache( ## Additional Notes * Entity classes should be serializable/deserializable to/from JSON to be cached (because it's serialized to JSON when saving in the [Distributed Cache](Caching.md)). If your entity class is not serializable, you can consider using a cache-item/DTO class instead, as explained before. -* Entity Caching System is designed as **read-only**. You should use the standard [repository](Repositories.md) methods to manipulate the entity if you need. If you need to manipulate (update) the entity, do not get it from the entity cache. Instead, read it from the repository, change it and update using the repository. +* Entity Caching System is designed as **read-only**. You should use the standard [repository](Repositories.md) methods to manipulate the entity if you need to. If you need to manipulate (update) the entity, do not get it from the entity cache. Instead, read it from the repository, change it and update using the repository. ## See Also diff --git a/docs/en/Entity-Framework-Core-Oracle-Devart.md b/docs/en/Entity-Framework-Core-Oracle-Devart.md index 872c36a01e..92cecadec9 100644 --- a/docs/en/Entity-Framework-Core-Oracle-Devart.md +++ b/docs/en/Entity-Framework-Core-Oracle-Devart.md @@ -60,6 +60,78 @@ This will scaffold a new migration for Oracle. Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. +## ORA-12899: value too large for column fix + +Oracle limits strings to `NVARCHAR2(2000)` when the migration is created. Some of the entity properties may extend it. You can check the known and reported properties of ABP modules entities that can extend this limit. To prevent this problem, you need to convert the `string` type to `long` type first and generate a new migration. Then convert the `long` type to `clob` type with maximum length. + +### First Migration + +Update you application DbContext `OnModelCreating` method: + +```csharp +protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.Entity(b => + { + b.Property(q => q.Payload).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + builder.Entity(b => + { + b.Property(x => x.Exceptions).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + builder.Entity(b => + { + b.Property(x => x.Parameters).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + /* Configure your own tables/entities inside here */ + } +``` + +Create a new migration using dotnet tooling under EntityFrameworkCore layer of your solution: `dotnet ef migrations add Oracle_Long_Conversion` + +### Second Migration + +Update you application DbContext `OnModelCreating` method: + +```csharp +protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.Entity(b => + { + b.Property(q => q.Payload).HasColumnType("clob").HasMaxLength(4000); + }); + + builder.Entity(b => + { + b.Property(x => x.Exceptions).HasColumnType("clob").HasMaxLength(4000); + }); + + builder.Entity(b => + { + b.Property(x => x.Parameters).HasColumnType("clob").HasMaxLength(4000); + }); + + /* Configure your own tables/entities inside here */ + } +``` + +Create a new migration using dotnet tooling under EntityFrameworkCore layer of your solution: `dotnet ef migrations add Oracle_Clob_Conversion` + +Run DbMigrator (or use dotnet tooling) to migrate the oracle database. + + + ## Run the Application It is ready. Just run the application and enjoy coding. diff --git a/docs/en/Entity-Framework-Core-Oracle-Official.md b/docs/en/Entity-Framework-Core-Oracle-Official.md index a3bb0f7b1b..fcc776f2c1 100644 --- a/docs/en/Entity-Framework-Core-Oracle-Official.md +++ b/docs/en/Entity-Framework-Core-Oracle-Official.md @@ -55,6 +55,76 @@ This will scaffold a new migration for Oracle. Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. +## ORA-12899: value too large for column fix + +Oracle limits strings to `NVARCHAR2(2000)` when the migration is created. Some of the entity properties may extend it. You can check the known and reported properties of ABP modules entities that can extend this limit. To prevent this problem, you need to convert the `string` type to `long` type first and generate a new migration. Then convert the `long` type to `clob` type with maximum length. + +### First Migration + +Update you application DbContext `OnModelCreating` method: + +```csharp +protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.Entity(b => + { + b.Property(q => q.Payload).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + builder.Entity(b => + { + b.Property(x => x.Exceptions).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + builder.Entity(b => + { + b.Property(x => x.Parameters).HasColumnType("long").HasMaxLength(int.MaxValue); + }); + + /* Configure your own tables/entities inside here */ + } +``` + +Create a new migration using dotnet tooling under EntityFrameworkCore layer of your solution: `dotnet ef migrations add Oracle_Long_Conversion` + +### Second Migration + +Update you application DbContext `OnModelCreating` method: + +```csharp +protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.Entity(b => + { + b.Property(q => q.Payload).HasColumnType("clob").HasMaxLength(4000); + }); + + builder.Entity(b => + { + b.Property(x => x.Exceptions).HasColumnType("clob").HasMaxLength(4000); + }); + + builder.Entity(b => + { + b.Property(x => x.Parameters).HasColumnType("clob").HasMaxLength(4000); + }); + + /* Configure your own tables/entities inside here */ + } +``` + +Create a new migration using dotnet tooling under EntityFrameworkCore layer of your solution: `dotnet ef migrations add Oracle_Clob_Conversion` + +Run DbMigrator (or use dotnet tooling) to migrate the oracle database. + ## Run the Application It is ready. Just run the application and enjoy coding. diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 6795204936..e5a8750f39 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -368,7 +368,7 @@ public class CreateUpdateBookDto { [Required] [StringLength(128)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] public BookType Type { get; set; } = BookType.Undefined; diff --git a/docs/en/Tutorials/Part-10.md b/docs/en/Tutorials/Part-10.md index e71beccd6f..601905f522 100644 --- a/docs/en/Tutorials/Part-10.md +++ b/docs/en/Tutorials/Part-10.md @@ -614,19 +614,19 @@ using System.Threading.Tasks; using Acme.BookStore.Authors; using Shouldly; using Volo.Abp.Application.Dtos; +using Volo.Abp.Modularity; using Volo.Abp.Validation; using Xunit; namespace Acme.BookStore.Books; - {{if DB=="Mongo"}} -[Collection(BookStoreTestConsts.CollectionDefinitionName)]{{end}} -public class BookAppService_Tests : BookStoreApplicationTestBase +public abstract class BookAppService_Tests : BookStoreApplicationTestBase + where TStartupModule : IAbpModule { private readonly IBookAppService _bookAppService; private readonly IAuthorAppService _authorAppService; - public BookAppService_Tests() + protected BookAppService_Tests() { _bookAppService = GetRequiredService(); _authorAppService = GetRequiredService(); @@ -643,7 +643,7 @@ public class BookAppService_Tests : BookStoreApplicationTestBase //Assert result.TotalCount.ShouldBeGreaterThan(0); result.Items.ShouldContain(b => b.Name == "1984" && - b.AuthorName == "George Orwell"); + b.AuthorName == "George Orwell"); } [Fact] @@ -788,7 +788,7 @@ public class CreateModalModel : BookStorePageModel [Required] [StringLength(128)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] public BookType Type { get; set; } = BookType.Undefined; @@ -871,7 +871,7 @@ public class EditModalModel : BookStorePageModel [Required] [StringLength(128)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] public BookType Type { get; set; } = BookType.Undefined; diff --git a/docs/en/Tutorials/Part-4.md b/docs/en/Tutorials/Part-4.md index 072392e82b..c97bce6bd7 100644 --- a/docs/en/Tutorials/Part-4.md +++ b/docs/en/Tutorials/Part-4.md @@ -82,19 +82,18 @@ using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Application.Dtos; +using Volo.Abp.Modularity; using Volo.Abp.Validation; using Xunit; namespace Acme.BookStore.Books; -{{if DB=="Mongo"}} -[Collection(BookStoreTestConsts.CollectionDefinitionName)] -{{end}} -public class BookAppService_Tests : BookStoreApplicationTestBase +public abstract class BookAppService_Tests : BookStoreApplicationTestBase + where TStartupModule : IAbpModule { private readonly IBookAppService _bookAppService; - public BookAppService_Tests() + protected BookAppService_Tests() { _bookAppService = GetRequiredService(); } @@ -114,6 +113,41 @@ public class BookAppService_Tests : BookStoreApplicationTestBase } ```` +{{if DB == "EF"}} +Add a new implementation class of `BookAppService_Tests` class, named `EfCoreBookAppService_Tests` in the `EntityFrameworkCore\Applications\Books` namespace (folder) of the `Acme.BookStore.EntityFrameworkCore.Tests` project: + +````csharp +using Acme.BookStore.Books; +using Xunit; + +namespace Acme.BookStore.EntityFrameworkCore.Applications.Books; + +[Collection(BookStoreTestConsts.CollectionDefinitionName)] +public class EfCoreBookAppService_Tests : BookAppService_Tests +{ + +} +```` +{{end}} + +{{if DB == "Mongo"}} +Add a new implementation class of `BookAppService_Tests` class, named `MongoDBBookAppService_Tests` in the `MongoDb\Applications\Books` namespace (folder) of the `Acme.BookStore.MongoDB.Tests` project: + +````csharp +using Acme.BookStore.MongoDB; +using Acme.BookStore.Books; +using Xunit; + +namespace Acme.BookStore.MongoDb.Applications.Books; + +[Collection(BookStoreTestConsts.CollectionDefinitionName)] +public class MongoDBBookAppService_Tests : BookAppService_Tests +{ + +} +```` +{{end}} + * `Should_Get_List_Of_Books` test simply uses `BookAppService.GetListAsync` method to get and check the list of books. * We can safely check the book "1984" by its name, because we know that this books is available in the database since we've added it in the seed data. @@ -174,19 +208,18 @@ using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Application.Dtos; +using Volo.Abp.Modularity; using Volo.Abp.Validation; using Xunit; namespace Acme.BookStore.Books; -{{if DB=="Mongo"}} -[Collection(BookStoreTestConsts.CollectionDefinitionName)] -{{end}} -public class BookAppService_Tests : BookStoreApplicationTestBase +public abstract class BookAppService_Tests : BookStoreApplicationTestBase + where TStartupModule : IAbpModule { private readonly IBookAppService _bookAppService; - public BookAppService_Tests() + protected BookAppService_Tests() { _bookAppService = GetRequiredService(); } @@ -203,7 +236,7 @@ public class BookAppService_Tests : BookStoreApplicationTestBase result.TotalCount.ShouldBeGreaterThan(0); result.Items.ShouldContain(b => b.Name == "1984"); } - + [Fact] public async Task Should_Create_A_Valid_Book() { diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 40b334808c..2cb525b4a4 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -75,9 +75,9 @@ public class Author : FullAuditedAggregateRoot internal Author( Guid id, - [NotNull] string name, + string name, DateTime birthDate, - [CanBeNull] string shortBio = null) + string? shortBio = null) : base(id) { SetName(name); @@ -85,13 +85,13 @@ public class Author : FullAuditedAggregateRoot ShortBio = shortBio; } - internal Author ChangeName([NotNull] string name) + internal Author ChangeName(string name) { SetName(name); return this; } - private void SetName([NotNull] string name) + private void SetName(string name) { Name = Check.NotNullOrWhiteSpace( name, @@ -146,9 +146,9 @@ public class AuthorManager : DomainService } public async Task CreateAsync( - [NotNull] string name, + string name, DateTime birthDate, - [CanBeNull] string shortBio = null) + string? shortBio = null) { Check.NotNullOrWhiteSpace(name, nameof(name)); @@ -167,8 +167,8 @@ public class AuthorManager : DomainService } public async Task ChangeNameAsync( - [NotNull] Author author, - [NotNull] string newName) + Author author, + string newName) { Check.NotNull(author, nameof(author)); Check.NotNullOrWhiteSpace(newName, nameof(newName)); diff --git a/docs/en/Tutorials/Part-8.md b/docs/en/Tutorials/Part-8.md index 9d638e909e..a1563f6b2c 100644 --- a/docs/en/Tutorials/Part-8.md +++ b/docs/en/Tutorials/Part-8.md @@ -123,12 +123,12 @@ public class CreateAuthorDto { [Required] [StringLength(AuthorConsts.MaxNameLength)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] public DateTime BirthDate { get; set; } - public string ShortBio { get; set; } + public string? ShortBio { get; set; } } ```` @@ -146,12 +146,12 @@ public class UpdateAuthorDto { [Required] [StringLength(AuthorConsts.MaxNameLength)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] public DateTime BirthDate { get; set; } - public string ShortBio { get; set; } + public string? ShortBio { get; set; } } ```` @@ -481,18 +481,17 @@ Finally, we can write some tests for the `IAuthorAppService`. Add a new class, n using System; using System.Threading.Tasks; using Shouldly; +using Volo.Abp.Modularity; using Xunit; namespace Acme.BookStore.Authors; - {{if DB=="Mongo"}} -[Collection(BookStoreTestConsts.CollectionDefinitionName)] -{{end}} -public class AuthorAppService_Tests : BookStoreApplicationTestBase +public abstract class AuthorAppService_Tests : BookStoreApplicationTestBase + where TStartupModule : IAbpModule { private readonly IAuthorAppService _authorAppService; - public AuthorAppService_Tests() + protected AuthorAppService_Tests() { _authorAppService = GetRequiredService(); } @@ -554,6 +553,41 @@ public class AuthorAppService_Tests : BookStoreApplicationTestBase } ```` +{{if DB == "EF"}} +Add a new implementation class of `AuthorAppService_Tests` class, named `EfCoreAuthorAppService_Tests` in the `EntityFrameworkCore\Applications\Authors` namespace (folder) of the `Acme.BookStore.EntityFrameworkCore.Tests` project: + +````csharp +using Acme.BookStore.Authors; +using Xunit; + +namespace Acme.BookStore.EntityFrameworkCore.Applications.Authors; + +[Collection(BookStoreTestConsts.CollectionDefinitionName)] +public class EfCoreAuthorAppService_Tests : AuthorAppService_Tests +{ + +} +```` +{{end}} + +{{if DB == "Mongo"}} +Add a new implementation class of `AuthorAppService_Tests` class, named `MongoDBAuthorAppService_Tests` in the `MongoDb\Applications\Authors` namespace (folder) of the `Acme.BookStore.MongoDB.Tests` project: + +````csharp +using Acme.BookStore.MongoDB; +using Acme.BookStore.Authors; +using Xunit; + +namespace Acme.BookStore.MongoDb.Applications.Authors; + +[Collection(BookStoreTestConsts.CollectionDefinitionName)] +public class MongoDBAuthorAppService_Tests : AuthorAppService_Tests +{ + +} +```` +{{end}} + Created some tests for the application service methods, which should be clear to understand. ## The Next Part diff --git a/docs/en/Tutorials/Part-9.md b/docs/en/Tutorials/Part-9.md index 704d5f016b..358448c4d2 100644 --- a/docs/en/Tutorials/Part-9.md +++ b/docs/en/Tutorials/Part-9.md @@ -331,14 +331,14 @@ public class CreateModalModel : BookStorePageModel { [Required] [StringLength(AuthorConsts.MaxNameLength)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] [DataType(DataType.Date)] public DateTime BirthDate { get; set; } [TextArea] - public string ShortBio { get; set; } + public string? ShortBio { get; set; } } } ``` @@ -454,14 +454,14 @@ public class EditModalModel : BookStorePageModel [Required] [StringLength(AuthorConsts.MaxNameLength)] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; [Required] [DataType(DataType.Date)] public DateTime BirthDate { get; set; } [TextArea] - public string ShortBio { get; set; } + public string? ShortBio { get; set; } } } ``` diff --git a/docs/en/UI/Common/Utils/Linked-List.md b/docs/en/UI/Common/Utils/Linked-List.md index 0c047d28e5..8c6c425e5c 100644 --- a/docs/en/UI/Common/Utils/Linked-List.md +++ b/docs/en/UI/Common/Utils/Linked-List.md @@ -958,7 +958,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); +list.dropByValueAll(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":3} ``` diff --git a/docs/zh-Hans/UI/Common/Utils/Linked-List.md b/docs/zh-Hans/UI/Common/Utils/Linked-List.md index 53c356628f..216e2d6a2d 100644 --- a/docs/zh-Hans/UI/Common/Utils/Linked-List.md +++ b/docs/zh-Hans/UI/Common/Utils/Linked-List.md @@ -925,7 +925,7 @@ list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} -list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); +list.dropByValueAll(0, (value, searchedValue) => value.x === searchedValue); // {"x":1} <-> {"x":2} <-> {"x":3} ``` diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs index af3c37c14f..0130c6e950 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs @@ -73,6 +73,7 @@ public class ProjectReferenceReplaceStep : ProjectBuildPipelineStep private readonly List _entries; private readonly bool _isMicroserviceServiceTemplate; private readonly string _projectName; + protected bool CentralPackageManagement { get; } protected ProjectReferenceReplacer( ProjectBuildContext context, @@ -81,6 +82,7 @@ public class ProjectReferenceReplaceStep : ProjectBuildPipelineStep _entries = context.Files; _isMicroserviceServiceTemplate = MicroserviceServiceTemplateBase.IsMicroserviceServiceTemplate(context.Template?.Name); _projectName = projectName; + CentralPackageManagement = context.Files.Any(x => x.Name.EndsWith("Directory.Packages.props")); } public void Run() @@ -165,7 +167,7 @@ public class ProjectReferenceReplaceStep : ProjectBuildPipelineStep includeAttr.Value = ConvertToNugetReference(oldNodeIncludeValue); newNode.Attributes.Append(includeAttr); - var versionAttr = doc.CreateAttribute("Version"); + var versionAttr = doc.CreateAttribute(CentralPackageManagement ? "VersionOverride" : "Version"); versionAttr.Value = _nugetPackageVersion; newNode.Attributes.Append(versionAttr); return newNode; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs index e434ebd822..1e849c05ef 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/App/AppTemplateBase.cs @@ -431,7 +431,6 @@ public abstract class AppTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); - context.Symbols.Add("HostWithIds"); steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); } } @@ -457,7 +456,6 @@ public abstract class AppTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); - context.Symbols.Add("HostWithIds"); steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); } @@ -551,7 +549,6 @@ public abstract class AppTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); - context.Symbols.Add("HostWithIds"); steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); } @@ -589,7 +586,6 @@ public abstract class AppTemplateBase : TemplateInfo steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.AuthServer")); steps.Add(new TemplateProjectRenameStep("MyCompanyName.MyProjectName.HttpApi.HostWithIds", "MyCompanyName.MyProjectName.HttpApi.Host")); - context.Symbols.Add("HostWithIds"); steps.Add(new AppTemplateChangeConsoleTestClientPortSettingsStep("44305")); } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RandomizeAuthServerPassPhraseStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RandomizeAuthServerPassPhraseStep.cs index a41c870f41..b29998438a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RandomizeAuthServerPassPhraseStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RandomizeAuthServerPassPhraseStep.cs @@ -12,6 +12,8 @@ public class RandomizeAuthServerPassPhraseStep : ProjectBuildPipelineStep private const string LocalhostPfx = "localhost.pfx -p 00000000-0000-0000-0000-000000000000"; private const string DotnetDevCerts = "openiddict.pfx -p 00000000-0000-0000-0000-000000000000"; private const string ProductionEncryptionAndSigningCertificate = "AddProductionEncryptionAndSigningCertificate(\"openiddict.pfx\", \"00000000-0000-0000-0000-000000000000\");"; + private const string ReadmeCallout = "`00000000-0000-0000-0000-000000000000`"; + private readonly static string RandomPassword = Guid.NewGuid().ToString("D"); public readonly static string RandomOpenIddictPassword = Guid.NewGuid().ToString("D"); @@ -59,6 +61,13 @@ public class RandomizeAuthServerPassPhraseStep : ProjectBuildPipelineStep RandomOpenIddictPassword)); } + if (lines[i].Contains(ReadmeCallout)) + { + lines[i] = lines[i].Replace(ReadmeCallout, + ReadmeCallout.Replace(DefaultPassword, + RandomOpenIddictPassword)); + } + if (lines[i].Contains(ProductionEncryptionAndSigningCertificate)) { lines[i] = lines[i].Replace(ProductionEncryptionAndSigningCertificate, diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs index 9bb6a6d95c..c867469a22 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/RemoveUnnecessaryPortsStep.cs @@ -12,7 +12,6 @@ public class RemoveUnnecessaryPortsStep : ProjectBuildPipelineStep { public override void Execute(ProjectBuildContext context) { - RemoveUnnecessaryDbMigratorClients(context); RemoveUnnecessaryHttpApiHostPorts(context); } @@ -66,36 +65,4 @@ public class RemoveUnnecessaryPortsStep : ProjectBuildPipelineStep httpApiHostAppSettings.SetLines(newlines); } - - private static void RemoveUnnecessaryDbMigratorClients(ProjectBuildContext context) - { - var dbMigratorAppSettings = context.Files - .FirstOrDefault(f => - f.Name.Contains("MyCompanyName.MyProjectName.DbMigrator") && f.Name.EndsWith("appsettings.json")); - - if (dbMigratorAppSettings == null) - { - return; - } - - var appSettingsJsonObject = JObject.Parse(dbMigratorAppSettings.Content); - var authServerJsonObject = (JObject)appSettingsJsonObject?["IdentityServer"] ?? (JObject)appSettingsJsonObject["OpenIddict"]; - var clientsJsonObject = (JObject)authServerJsonObject?["Clients"] ?? (JObject)authServerJsonObject?["Applications"]; - - if (clientsJsonObject == null) - { - return; - } - - if (context.BuildArgs.UiFramework != UiFramework.Blazor) - { - clientsJsonObject.Remove("MyProjectName_Blazor"); - } - if (!context.BuildArgs.PublicWebSite) - { - clientsJsonObject.Remove("MyProjectName_Web_Public"); - } - - dbMigratorAppSettings.SetContent(appSettingsJsonObject.ToString(Formatting.Indented)); - } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs index 88ce572d33..c899270ad2 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/CSharp/CSharpServiceProxyGenerator.cs @@ -220,7 +220,7 @@ public class CSharpServiceProxyGenerator : ServiceProxyGeneratorBase x).Select(x => x))); - classTemplate.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty); + classTemplate.Replace($"{Environment.NewLine}{Environment.NewLine} {MethodPlaceholder}", string.Empty).Replace(MethodPlaceholder, string.Empty); filePath = Path.Combine(args.WorkDirectory, folder, $"{clientProxyName}.Generated.cs"); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); diff --git a/latest-versions.json b/latest-versions.json index 1dae5941de..fc41690d54 100644 --- a/latest-versions.json +++ b/latest-versions.json @@ -1,6 +1,6 @@ [ { - "version": "7.4.4", + "version": "8.0.0", "releaseDate": "", "type": "stable", "message": "" diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml index 9ac8ee9462..432364daa3 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml @@ -26,73 +26,81 @@
-
- -
- @if (Model.User.Name != null && Model.User.Surname != null) - { -

@Model.User.Name @Model.User.Surname

- } - @if (Model.User.Company != null) - { -

@Model.User.Company

- } - @if (Model.User.JobTitle != null) - { -

@Model.User.JobTitle

- } - - @L["UserName"].Value.ToUpper() -
@Model.User.UserName
- - @if (Model.User.WebSite != null) - { - @L["PersonalWebsite"].Value.ToUpper() -
- @Model.User.WebSite -
- } - - - @if (Model.User.Twitter != null || Model.User.Github != null || Model.User.Linkedin != null) - { - @L["Social"].Value.ToUpper() -
    - @if (Model.User.Twitter != null) - { -
  • - -
    -
    -
  • - } - @if (Model.User.Github != null) - { -
  • - -
    -
    -
  • - } - @if (Model.User.Linkedin != null) - { -
  • - -
    -
    -
  • - } -
- } - - @if (Model.User.Biography != null) - { -
- @L["Biography"].Value.ToUpper() -

@Model.User.Biography

+
+
+
- } +
+
+ @if (Model.User.Name != null && Model.User.Surname != null) + { +

+ @Model.User.Name @Model.User.Surname +

} + @if (Model.User.Company != null) + { +

@Model.User.Company

+ } + + @if (Model.User.JobTitle != null) + { +

@Model.User.JobTitle

+ } + + @L["UserName"].Value.ToUpper() +
@Model.User.UserName
+ + @if (Model.User.WebSite != null) + { + @L["PersonalWebsite"].Value.ToUpper() +
+ @Model.User.WebSite +
+ } + + + @if (Model.User.Twitter != null || Model.User.Github != null || Model.User.Linkedin != null) + { + @L["Social"].Value.ToUpper() +
    + @if (Model.User.Twitter != null) + { +
  • + +
    +
    +
  • + } + @if (Model.User.Github != null) + { +
  • + +
    +
    +
  • + } + @if (Model.User.Linkedin != null) + { +
  • + +
    +
    +
  • + } +
+ } + + @if (Model.User.Biography != null) + { +
+ @L["Biography"].Value.ToUpper() +

@Model.User.Biography

+
+ } +
+ +
@@ -106,16 +114,17 @@ @foreach (var post in Model.Posts) {
-
+
- + - + @L["Blog"].Value.ToUpper()
+