Browse Source

Merge remote-tracking branch 'abpframework/dev' into Translate

pull/1711/head
梁士伟 7 years ago
parent
commit
d54735d5ec
  1. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  2. 1
      build-all.ps1
  3. 29
      docs/cs/CLI.md
  4. 2
      docs/cs/Contribution/Index.md
  5. 2
      docs/cs/Getting-Started-AspNetCore-MVC-Template.md
  6. 5
      docs/cs/Index.md
  7. 26
      docs/cs/docs-nav.json
  8. BIN
      docs/cs/images/MonthlyProfitWidgetFiles.png
  9. BIN
      docs/cs/images/bookstore-swagger-ui-host.png
  10. BIN
      docs/cs/images/bookstore-visual-studio-solution-for-spa.png
  11. BIN
      docs/cs/images/dashboard1.png
  12. 8
      docs/en/Application-Services.md
  13. 2
      docs/en/Best-Practices/Index.md
  14. 35
      docs/en/Best-Practices/PostgreSQL-Integration.md
  15. BIN
      docs/en/Best-Practices/images/postgresql-delete-initial-migrations.png
  16. BIN
      docs/en/Best-Practices/images/postgresql-update-database.png
  17. 46
      docs/en/Blog-Posts/2019-08-16 v0_19_Release/Post.md
  18. 2
      docs/en/CLI.md
  19. 61
      docs/en/Dapper.md
  20. 5
      docs/en/Index.md
  21. 3
      docs/en/Startup-Templates/Application.md
  22. 69
      docs/en/Tutorials/Angular/Part-I.md
  23. 354
      docs/en/Tutorials/Angular/Part-II.md
  24. 178
      docs/en/Tutorials/Angular/Part-III.md
  25. BIN
      docs/en/Tutorials/Angular/images/bookstore-confirmation-popup.png
  26. BIN
      docs/en/Tutorials/Angular/images/bookstore-final-actions-dropdown.png
  27. BIN
      docs/en/Tutorials/Angular/images/bookstore-test-explorer.png
  28. BIN
      docs/en/Tutorials/Angular/images/bookstore-test-projects-v3.png
  29. 38
      docs/en/docs-nav.json
  30. 8
      docs/zh-Hans/Application-Services.md
  31. 19
      docs/zh-Hans/CLI.md
  32. 61
      docs/zh-Hans/Dapper.md
  33. 2
      docs/zh-Hans/Entity-Framework-Core.md
  34. 2
      docs/zh-Hans/Startup-Templates/Module.md
  35. 4
      docs/zh-Hans/docs-nav.json
  36. 14
      framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs
  37. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml
  38. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml
  39. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  40. 4
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs
  41. 2
      framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
  42. 36
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
  43. 45
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperOptions.cs
  44. 29
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapAttribute.cs
  45. 17
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapAttributeBase.cs
  46. 36
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapFromAttribute.cs
  47. 36
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapToAttribute.cs
  48. 17
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperConfigurationExtensions.cs
  49. 2
      framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj
  50. 6
      framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
  51. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs
  52. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs
  53. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs
  54. 31
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs
  55. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs
  56. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Analyticses/CliAnalyticsCollectInputDto.cs
  57. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuilder.cs
  58. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs
  59. 19
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleProTemplate.cs
  60. 53
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplate.cs
  61. 64
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs
  62. 55
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  63. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackageJsonFileFinder.cs
  64. 2
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  65. 2
      framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj
  66. 4
      framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj
  67. 2
      framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj
  68. 4
      framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj
  69. 16
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  70. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  71. 2
      framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj
  72. 2
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs
  73. 63
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
  74. 2
      framework/src/Volo.Abp.Security/Volo/Abp/Users/CurrentUser.cs
  75. 11
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs
  76. 4
      framework/test/AbpTestBase/AbpTestBase.csproj
  77. 2
      framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj
  78. 2
      framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj
  79. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj
  80. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj
  81. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json
  82. 192
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock
  83. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj
  84. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj
  85. 2
      framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj
  86. 2
      framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
  87. 2
      framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj
  88. 2
      framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj
  89. 5
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperTestModule.cs
  90. 4
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_ConfigurationValidation_Tests.cs
  91. 75
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Inheritance_Tests.cs
  92. 157
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapping_Tests.cs
  93. 1
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityDto.cs
  94. 12
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyMapProfile.cs
  95. 2
      framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj
  96. 2
      framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj
  97. 2
      framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj
  98. 2
      framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj
  99. 2
      framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj
  100. 2
      framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj

3
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -154,6 +154,7 @@
"Result": "Result",
"SeeTheDocumentForMoreInformation": "See the <a href=\"{1}\">{0} document</a> for more information",
"IndexPageHeroSection": "<span class=\"first-line shine\"><strong>open source</strong></span><span class=\"second-line text-uppercase\">Web Application<br />Framework </span><span class=\"third-line shine2\"><strong>for asp.net core</strong></span>",
"UiFramework": "UI Framework"
"UiFramework": "UI Framework",
"EmailAddress": "Email address"
}
}

1
build-all.ps1

@ -21,7 +21,6 @@ $solutionPaths = (
"modules/client-simulation",
"templates/module/aspnet-core",
"templates/app/aspnet-core",
"samples/MicroserviceDemo",
"abp_io/AbpIoLocalization"
)

29
docs/cs/CLI.md

@ -34,26 +34,32 @@ Příklad:
abp new Acme.BookStore
````
* Acme.BookStore je tady název řešení.
* `Acme.BookStore` je tady název řešení.
* Běžná konvence je nazvat řešení stylem *VaseSpolecnost.VasProjekt*. Nicméně můžete použít i jiné pojmenování jako *VasProjekt* (jednostupňový jmenný prostor) nebo *VaseSpolecnost.VasProjekt.VasModul* (třístupňový jmenný prostor).
#### Možnosti
* `--template` nebo `-t`: Určuje název šablony. Výchozí šablona je `mvc`. Dostupné šablony:
* `mvc` (výchozí): ASP.NET Core [MVC aplikační šablona](Startup-Templates/Mvc.md). Dodatečné možnosti:
* `--database-provider` nebo `-d`: Určuje poskytovatele databáze. Vychozí poskytovatel je `ef`. Dostupní poskytovatelé:
* `--template` nebo `-t`: Určuje název šablony. Výchozí šablona je `app`, která generuje webovou aplikaci. Dostupné šablony:
* `app` (výchozí): [Aplikační šablona](Startup-Templates/Application.md). Dodatečné možnosti:
* `--ui` nebo `-u`: Určuje UI framework. Výchozí framework je `mvc`. Dostupné frameworky:
* `mvc`: ASP.NET Core MVC. Pro tuto šablonu jsou dostupné dodatečné možnosti:
* `--tiered`: Vytvoří stupňovité řešení, kde jsou vrstvy Web a Http API fyzicky odděleny. Pokud není uvedeno, tak vytvoří vrstvené řešení, které je méně složité a vhodné pro většinu scénářů.
* `angular`: Angular. Pro tuto šablonu jsou dostupné dodatečné možnosti:
* `--separate-identity-server`: Oddělí identity server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod.
* `--database-provider` nebo `-d`: Určuje poskytovatele databáze. Výchozí poskytovatel je `ef`. Dostupní poskytovatelé:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--tiered`: Vytvoří stupňovité řešení, kde jsou vrstvy Web a Http API fyzicky odděleny. Pokud není uvedeno tak vytvoří vrstvené řešení, které je méně složité a vhodné pro většinu scénářů.
* `mvc-module`: ASP.NET Core [MVC modulová šablona](Startup-Templates/Mvc-Module.md). Dodatečné možnosti:
* `--no-ui`: Určuje, že nebude zahrnuto uživatelské rozhraní. To umožňuje vytvářet moduly pouze pro služby (a.k.a. mikroslužby - bez UI).
* `module`: [Šablona modulu](Startup-Templates/Module.md). Dodatečné možnosti:
* `--no-ui`: Určuje nezahrnutí uživatelského rozhraní. Umožňuje vytvořit moduly pouze pro služby (a.k.a. mikroslužby - bez uživatelského rozhraní).
* `--output-folder` nebo `-o`: Určuje výstupní složku. Výchozí hodnota je aktuální adresář.
* `--version` nebo `-v`: Určuje verzi ABP & šablony. Může to být [štítek vydání](https://github.com/abpframework/abp/releases) nebo [název větve](https://github.com/abpframework/abp/branches). Pokud není uvedeno, používá nejnovější vydání. Většinou budete chtít použít nejnovější verzi.
### add-package
Přidá nový balíček ABP do projektu pomocí,
Přidá ABP balíček do projektu,
* Přidání souvisejícícho nuget balíčku jako závislost do projektu.
* Přidáním souvisejícícho nuget balíčku jako závislost do projektu.
* Přidáním `[DependsOn(...)]` atributu k modulové tříde v projektu (podívejte se na [dokument vývoje modulu](Module-Development-Basics.md)).
> Všimněte si, že přidaný modul může vyžadovat další konfiguraci, která je obecně uvedena v dokumentaci příslušného balíčku.
@ -78,7 +84,7 @@ abp add-package Volo.Abp.MongoDB
### add-module
Přidá více-balíčkový modul k řešení tím, že najde všechny balíčky modulu, vyhledá související projekty v řešení a přidá každý balíček do odpovídajícího projektu v řešení.
Přidá [více-balíčkový aplikační modul](Modules/Index) k řešení tím, že najde všechny balíčky modulu, vyhledá související projekty v řešení a přidá každý balíček do odpovídajícího projektu v řešení.
> Modul se obecně skládá z několika balíčků (z důvodu vrstvení, různých možností poskytovatele databáze nebo jiných důvodů). Použití příkazu `add-module` dramaticky zjednodušuje přidání modulu do řešení. Každý modul však může vyžadovat další konfiguraci, která je obecně uvedena v dokumentaci příslušného modulu.
@ -100,6 +106,7 @@ abp add-module Volo.Blogging
* `--solution` nebo `-s`: Určuje cestu k řešení (.sln). Pokud není zadáno, CLI se pokusí najít soubor .sln v aktuálním adresáři.
* `--skip-db-migrations`: Pro poskytovatele databáze EF Core automaticky přidá nový kód první migrace (`Add-Migration`) a v případě potřeby aktualizuje databázi (`Update-Database`). Tuto možnost určete k vynechání této operace.
* `-sp` nebo `--startup-project`: Relativní cesta ke složce spouštěcího projektu. Výchozí hodnota je aktuální adresář.
### update
@ -117,6 +124,8 @@ abp update [možnosti]
#### Možnosti
* `--include-previews` nebo `-p`: Zahrne náhledové, beta a rc balíčky při kontrole nových verzí.
* `--npm`: Aktualizuje pouze balíčky NPM.
* `--nuget`: Aktualizuje pouze balíčky NuGet.
### help

2
docs/cs/Contribution/Index.md

@ -35,7 +35,7 @@ Existuje několik základních dokumentů, které je třeba přeložit než bude
* Tutoriály
* CLI
Nový jazyk je publikován jakmile budou minimálně tyto překlady dokončeny.
Nový jazyk je publikován jakmile jsou minimálně tyto překlady dokončeny.
### Lokalizace zdrojů

2
docs/cs/Getting-Started-AspNetCore-MVC-Template.md

@ -37,7 +37,7 @@ Otevřete řešení ve **Visual Studio**:
Řešení má vrstvenou strukturu (založenou na [Domain Driven Design](Domain-Driven-Design.md)) a obsahuje projekty jednotkovových a integračních testů předkonfigurované pro práci s **EF Core** & **SQLite in-memory** databází.
> Podívejte se na [dokument šablony MVC aplikace](Startup-Templates/Mvc.md) k detailnímu pochopení struktury řešení.
> Podívejte se na [dokument šablony aplikace](Startup-Templates/Application.md) k detailnímu pochopení struktury řešení.
### Connection string databáze

5
docs/cs/Index.md

@ -6,7 +6,7 @@ K procházení dokumentace využijte navigační nabídky vlevo.
## Stav projektu
ABP je **novou generací** open source frameworku [ASP.NET Boilerplate](https://aspnetboilerplate.com/). V současné době je v ranné fázi a není připraven k ostrému nasazení. Na dokumentaci se stále pracuje a ještě zdaleka není dokončena.
ABP je **novou generací** open source frameworku [ASP.NET Boilerplate](https://aspnetboilerplate.com/). V současné době je v náhledové fázi a není připraven k ostrému nasazení. Na dokumentaci se stále pracuje a ještě zdaleka není dokončena.
Pro aplikace na krátkodobé a ostré nasazení je doporučeno použít [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework, který je bohatý na funkce, vyspělý, aktivně udržovaný a aktuální.
@ -14,7 +14,8 @@ Pro aplikace na krátkodobé a ostré nasazení je doporučeno použít [ASP.NET
Nejsnazší cestou jak začít nový projekt s ABP je užití startovací šablony:
* [ASP.NET Core MVC Šablona](Getting-Started-AspNetCore-MVC-Template.md)
* [ASP.NET Core MVC (Razor Pages) UI Šablona](Getting-Started-AspNetCore-MVC-Template.md)
* [Angular UI Šablona](Getting-Started-Angular-Template.md)
Pokud chcete začít od nuly (s prázdným projektem) tak manuálně nainstalujte ABP Framework s pomocí následujících tutoriálů:

26
docs/cs/docs-nav.json

@ -7,8 +7,12 @@
"text": "Ze startovacích šablon",
"items": [
{
"text": "ASP.NET Core MVC",
"text": "Aplikace s MVC (Razor Pages) UI",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
},
{
"text": "Aplikace s Angular UI",
"path": "Getting-Started-Angular-Template.md"
}
]
},
@ -34,8 +38,12 @@
"text": "Vývoj aplikace",
"items": [
{
"text": "S ASP.NET Core MVC",
"text": "S ASP.NET Core MVC UI",
"path": "Tutorials/AspNetCore-Mvc/Part-I.md"
},
{
"text": "S Angular UI",
"path": "Tutorials/Angular/Part-I.md"
}
]
}
@ -264,6 +272,20 @@
}
]
},
{
"text": "Startovací šablony",
"path": "Startup-Templates/Index.md",
"items": [
{
"text": "Aplikace",
"path": "Startup-Templates/Application.md"
},
{
"text": "Modul",
"path": "Startup-Templates/Module.md"
}
]
},
{
"text": "Vzorky",
"items": [

BIN
docs/cs/images/MonthlyProfitWidgetFiles.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
docs/cs/images/bookstore-swagger-ui-host.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/cs/images/bookstore-visual-studio-solution-for-spa.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/cs/images/dashboard1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

8
docs/en/Application-Services.md

@ -146,7 +146,7 @@ public interface IBookAppService : IApplicationService
`BookDto` is a simple [DTO](Data-Transfer-Objects.md) class defined as below:
````csharp
[AutoMapFrom(typeof(Book))] //Defines the mapping
[AbpAutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto
{
public Guid Id { get; set; }
@ -159,7 +159,7 @@ public class BookDto
}
````
* `BookDto` defines `[AutoMapFrom(typeof(Book))]` attribute to create the object mapping from `Book` to `BookDto`.
* `BookDto` defines `[AbpAutoMapFrom(typeof(Book))]` attribute to create the object mapping from `Book` to `BookDto`.
Then you can implement the `GetAsync` method as shown below:
@ -248,7 +248,7 @@ public interface IAsyncCrudAppService<
DTO classes used in this example are `BookDto` and `CreateUpdateBookDto`:
````csharp
[AutoMapFrom(typeof(Book))]
[AbpAutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -258,7 +258,7 @@ public class BookDto : AuditedEntityDto<Guid>
public float Price { get; set; }
}
[AutoMapTo(typeof(Book))]
[AbpAutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]

2
docs/en/Best-Practices/Index.md

@ -23,5 +23,5 @@ Also, this guide is mostly usable for general **application development**.
* [Data Transfer Objects](Data-Transfer-Objects.md)
* Data Access
* [Entity Framework Core Integration](Entity-Framework-Core-Integration.md)
* [MongoDB Integration](MongoDB-Integration.md)
* [MongoDB Integration](MongoDB-Integration.md)

35
docs/en/Best-Practices/PostgreSQL-Integration.md

@ -0,0 +1,35 @@
## Entity Framework Core PostgreSQL Integration
> See [Entity Framework Core Integration document](../Entity-Framework-Core.md) for the basics of the EF Core integration.
### EntityFrameworkCore Project Update
- In `Acme.BookStore.EntityFrameworkCore` project replace package `Volo.Abp.EntityFrameworkCore.SqlServer` with `Volo.Abp.EntityFrameworkCore.PostgreSql`
- Update to use PostgreSQL in `BookStoreEntityFrameworkCoreModule`.
- Replace the `AbpEntityFrameworkCoreSqlServerModule` with the `AbpEntityFrameworkCorePostgreSqlModule`
- Replace the `options.UseSqlServer()` with the `options.UsePostgreSql()`
- In other projects update the PostgreSQL connection string in necessary `appsettings.json` files.
#### Delete Existing Migrations
Delete all existing migration files (including `DbContextModelSnapshot`)
![postgresql-delete-initial-migrations](images/postgresql-delete-initial-migrations.png)
#### Regenerate Initial Migration & Update the Database
Set the correct startup project (usually a web project),
Open the **Package Manager Console** (Tools -> Nuget Package Manager -> Package Manager Console), select the `Acme.BookStore.EntityFrameworkCore.DbMigrations` as the **Default project** and execute the following command:
Run `Add-Migration` command.
````
PM> Add-Migration Initial
````
Then execute the `Update-Database` command to update the database schema:
````
PM> Update-Database
````
![postgresql-update-database](images/postgresql-update-database.png)

BIN
docs/en/Best-Practices/images/postgresql-delete-initial-migrations.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Best-Practices/images/postgresql-update-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

46
docs/en/Blog-Posts/2019-08-16 v0_19_Release/Post.md

@ -0,0 +1,46 @@
# ABP v0.19 Release With New Angular UI
ABP v0.19 has been released with [90 issues](https://github.com/abpframework/abp/milestone/17?closed=1) resolved and [650+ commits](https://github.com/abpframework/abp/compare/0.18.1...0.19.0) pushed.
## New Features
### Angular UI
Finally, ABP has a **SPA UI** option with the latest [Angular](https://angular.io/) framework. Angular integration was not simply creating a startup template.
* Created a base infrastructure to handle ABP's modularity, theming and some other features. This infrastructure has been deployed as [NPM packages](https://github.com/abpframework/abp/tree/dev/npm/ng-packs/packages).
* Created Angular UI packages for the modules like account, identity and tenant-management.
* Created a minimal startup template that authenticates using IdentityServer and uses the ASP.NET Core backend. This template uses the packages mentioned above.
* Worked on the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) and the [download page](https://abp.io/get-started) to be able to generate projects with the new UI option.
* Created a [tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Angular/Part-I) to jump start with the new UI option.
We've created the template, document and infrastructure based on the latest Angular tools and trends:
* Uses [NgBootstrap](https://ng-bootstrap.github.io/) and [PrimeNG](https://www.primefaces.org/primeng/) as the UI component libraries. You can use your favorite library, no problem, but pre-built modules work with these libraries.
* Uses [NGXS](https://ngxs.gitbook.io/ngxs/) as the state management library.
Angular was the first SPA UI option, but it is not the last. After v1.0 release, we will start to work on a second UI option. Not decided yet, but candidates are Blazor, React and Vue.js. Waiting your feedback. You can thumb up using the following issues:
* [Blazor](https://github.com/abpframework/abp/issues/394)
* [Vue.js](https://github.com/abpframework/abp/issues/1168)
* [React](https://github.com/abpframework/abp/issues/1638)
### Widget System
[Widget system](https://docs.abp.io/en/abp/latest/AspNetCore/Widgets) allows to **define and reuse** widgets for ASP.NET Core MVC applications. Widgets may have their own script and style resources and dependencies to 3rd-party libraries which are managed by the ABP framework.
### Others
We've solved many bugs and worked on existing features based on the community feedback. See the [v0.19 milestone](https://github.com/abpframework/abp/milestone/17?closed=1) for all the closed issues.
## Road Map
We had decided to wait for **ASP.NET Core 3.0** final release. Microsoft has announced to released it at [.NET Conf](https://www.dotnetconf.net/), between 23-25 September.
We have planned to finalize our work and move to ASP.NET Core 3.0 (with preview or RC) before its release. Once Microsoft releases it, we will immediately start to upgrade and test with the final release.
So, you can expect ABP **v1.0** to be released in the **first half of the October**. We are very excited and working hard on it.
You can follow the progress from [the GitHub milestones](https://github.com/abpframework/abp/milestones).
We will not add major features until v1.0.

2
docs/en/CLI.md

@ -123,6 +123,8 @@ abp update [options]
#### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
* `--npm`: Only updates NPM packages.
* `--nuget`: Only updates NuGet packages.
### help

61
docs/en/Dapper.md

@ -0,0 +1,61 @@
# Dapper Integration
Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface.
Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository<TDbContext>` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper.
These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md).
## Installation
Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer):
```shell
Install-Package Volo.Abp.Dapper
```
Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md):
````C#
using Volo.Abp.Dapper;
using Volo.Abp.Modularity;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpDapperModule))]
public class MyModule : AbpModule
{
//...
}
}
````
## Implement Dapper Repository
The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods.
`DbConnection` and `DbTransaction` are from the `DapperRepository` base class.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency
{
public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetAllPersonNames()
{
return (await DbConnection.QueryAsync<string>("select Name from People", transaction: DbTransaction))
.ToList();
}
public virtual async Task<int> UpdatePersonNames(string name)
{
return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name },
DbTransaction);
}
}
```

5
docs/en/Index.md

@ -6,7 +6,7 @@ Explore the left navigation menu to deep dive in the documentation.
## Project Status
ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is far from complete.
ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in preview stage and not ready to use in production. The documentation is still in progress and it is far from complete.
For short-term and production level applications, it's suggested to use [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework which has rich feature set, mature, actively maintained and up-to-date.
@ -14,7 +14,8 @@ For short-term and production level applications, it's suggested to use [ASP.NET
Easiest way to start a new project with ABP is to use the startup templates:
* [ASP.NET Core MVC Template](Getting-Started-AspNetCore-MVC-Template.md)
* [ASP.NET Core MVC (Razor Pages) UI Template](Getting-Started-AspNetCore-MVC-Template.md)
* [Angular UI Template](Getting-Started-Angular-Template.md)
If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials:

3
docs/en/Startup-Templates/Application.md

@ -5,7 +5,8 @@
This template provides a layered application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. This document explains the solution structure and projects in details. If you want to start quickly, follow the guides below:
* See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template (uses MVC as the UI framework and Entity Framework Core as the database provider).
* See the [ASP.NET Core MVC Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template (uses MVC as the UI framework and Entity Framework Core as the database provider).
* See the [ASP.NET Core MVC Application Development Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) to learn how to develop applications using this template (uses MVC as the UI framework and Entity Framework Core as the database provider).
* See the [Angular Application Development Tutorial](../Tutorials/Angular/Part-I.md) to learn how to develop applications using this template (uses Angular as the UI framework and MongoDB as the database provider).
## How to Start With?

69
docs/en/Tutorials/Angular/Part-I.md

@ -8,6 +8,7 @@ This is the first part of the Angular tutorial series. See all parts:
- **Part I: Create the project and a book list page (this tutorial)**
- [Part II: Create, Update and Delete books](Part-II.md)
- [Part III: Integration Tests](Part-III.md)
You can access to the **source code** of the application from the [GitHub repository](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb).
@ -19,7 +20,7 @@ Create a new project named `Acme.BookStore` by selecting the Angular as the UI f
This is how the layered solution structure looks after it's created:
![bookstore-backend-solution](images\bookstore-backend-solution-v2.png)
![bookstore-backend-solution](images/bookstore-backend-solution-v2.png)
> You can see the [Application template document](../../Startup-Templates/Application.md) to understand the solution structure in details. However, you will understand the basics with this tutorial.
@ -341,13 +342,13 @@ Run `yarn start`, wait Angular to run the application and open `http://localhost
Open the `app-routing.module.ts` and replace `books` as shown below:
```typescript
import { LayoutApplicationComponent } from '@abp/ng.theme.basic';-
```js
import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';-
//...
{
path: 'books',
component: LayoutApplicationComponent,
component: ApplicationLayoutComponent,
loadChildren: () => import('./books/books.module').then(m => m.BooksModule),
data: {
routes: {
@ -357,7 +358,7 @@ import { LayoutApplicationComponent } from '@abp/ng.theme.basic';-
},
```
`LayoutApplicationComponent` configuration sets the application layout to the new page. If you would like to see your route on the navigation bar (main menu) you must also add the `data` object with `name` property in your route.
`ApplicationLayoutComponent` configuration sets the application layout to the new page. If you would like to see your route on the navigation bar (main menu) you must also add the `data` object with `name` property in your route.
![initial-books-page](images/bookstore-initial-books-page-with-layout.png)
@ -379,7 +380,7 @@ yarn ng generate component books/book-list
Import the `SharedModule` to the `BooksModule` to reuse some components and services defined in:
```typescript
```js
import { SharedModule } from '../shared/shared.module';
@NgModule({
@ -394,7 +395,7 @@ export class BooksModule {}
Then, update the `routes` in the `books-routing.module.ts` to add the new book-list component:
```typescript
```js
import { BookListComponent } from './book-list/book-list.component';
const routes: Routes = [
@ -424,7 +425,7 @@ yarn ng generate ngxs-schematic:state books
This command creates several new files and edits `app.modules.ts` to import the `NgxsModule` with the new state:
```typescript
```js
// app.module.ts
import { BooksState } from './store/states/books.state';
@ -445,19 +446,20 @@ First, create data types to map data returning from the backend (you can check s
Modify the `books.ts` as shown below:
```typescript
import { ABP } from '@abp/ng.core';
```js
export namespace Books {
export interface State {
books: Response;
}
export type Response = ABP.PagedResponse<Book>;
export interface Response {
items: Book[];
totalCount: number;
}
export interface Book {
name: string;
type: Type;
type: BookType;
publishDate: string;
price: number;
lastModificationTime: string;
@ -493,11 +495,11 @@ yarn ng generate service books/shared/books
![service-terminal-output](images/bookstore-service-terminal-output.png)
Modify `book.service.ts` as shown below:
Modify `books.service.ts` as shown below:
```typescript
```js
import { Injectable } from '@angular/core';
import { RestService, Rest } from '@abp/ng.core';
import { RestService } from '@abp/ng.core';
import { Books } from '../../store/models';
import { Observable } from 'rxjs';
@ -505,15 +507,13 @@ import { Observable } from 'rxjs';
providedIn: 'root',
})
export class BooksService {
constructor(private rest: RestService) {}
constructor(private restService: RestService) {}
get(): Observable<Books.Response> {
const request: Rest.Request<null> = {
return this.restService.request<void, Books.Response>({
method: 'GET',
url: '/api/app/book',
};
return this.rest.request<null, Books.Response>(request);
url: '/api/app/book'
});
}
}
```
@ -522,7 +522,7 @@ Added the `get` method to get the list of books by performing an HTTP request to
Replace `books.actions.ts` content as shown below:
```typescript
```js
export class GetBooks {
static readonly type = '[Books] Get';
}
@ -532,7 +532,7 @@ export class GetBooks {
Open the `books.state.ts` and change the file as shown below:
```typescript
```js
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { GetBooks } from '../actions/books.actions';
import { Books } from '../models/books';
@ -545,18 +545,18 @@ import { tap } from 'rxjs/operators';
})
export class BooksState {
@Selector()
static getBooks({ books }: Books.State) {
return books.items || [];
static getBooks(state: Books.State) {
return state.books.items || [];
}
constructor(private booksService: BooksService) {}
@Action(GetBooks)
get({ patchState }: StateContext<Books.State>) {
get(ctx: StateContext<Books.State>) {
return this.booksService.get().pipe(
tap(books => {
patchState({
books,
tap(booksResponse => {
ctx.patchState({
books: booksResponse,
});
}),
);
@ -572,7 +572,7 @@ Added the `GetBooks` action that uses the `BookService` defined above to get the
Modify the `book-list.component.ts` as shown below:
```typescript
```js
import { Component, OnInit } from '@angular/core';
import { Store, Select } from '@ngxs/store';
import { BooksState } from '../../store/states';
@ -597,10 +597,9 @@ export class BookListComponent implements OnInit {
ngOnInit() {
this.loading = true;
this.store.dispatch(new GetBooks())
.subscribe(() => {
this.loading = false
};
this.store.dispatch(new GetBooks()).subscribe(() => {
this.loading = false;
});
}
}
```

354
docs/en/Tutorials/Angular/Part-II.md

@ -6,6 +6,7 @@ This is the second part of the Angular tutorial series. See all parts:
- [Part I: Create the project and a book list page](Part-I.md)
- **Part II: Create, Update and Delete books (this tutorial)**
- [Part III: Integration Tests](Part-III.md)
You can access to the **source code** of the application from the [GitHub repository](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb).
@ -17,7 +18,7 @@ In this section, you will learn how to create a new modal dialog form to create
Create an interface, named `CreateUpdateBookInput` in the `books.ts` as shown below:
```typescript
```js
export namespace Books {
//...
export interface CreateUpdateBookInput {
@ -35,23 +36,23 @@ export namespace Books {
Open the `books.service.ts` and add a new method, named `create` to perform an HTTP POST request to the server:
```typescript
create(body: Books.CreateUpdateBookInput): Observable<Books.Book> {
const request: Rest.Request<Books.CreateUpdateBookInput> = {
```js
create(createBookInput: Books.CreateUpdateBookInput): Observable<Books.Book> {
return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
method: 'POST',
url: '/api/app/book',
body,
};
return this.rest.request<Books.CreateUpdateBookInput, Books.Book>(request);
body: createBookInput
});
}
```
- `restService.request` function gets generic parameters for the types sent to and received from the server. This example sends a `CreateUpdateBookInput` object and receives a `Book` object (you can set `void` for request or return type if not used).
#### State Definitions
Add the `CreateUpdateBook` action to `books.actions.ts` as shown below:
Add the `CreateUpdateBook` action to the `books.actions.ts` as shown below:
```typescript
```js
import { Books } from '../models';
export class CreateUpdateBook {
@ -60,24 +61,25 @@ export class CreateUpdateBook {
}
```
<!-- Added an `id` parameter to differentiate between create or update actions -->
Open `books.state.ts` and define the `save` method that will listen to a `CreateUpdateBook` action to create a book:
```typescript
@Action(CreateUpdateBook)
save({ dispatch }: StateContext<Books.State>, { payload }: CreateUpdateBook) {
return this.booksService
.create(payload)
.pipe(switchMap(() => dispatch(new GetBooks())));
}
```js
import { ... , CreateUpdateBook } from '../actions/books.actions';
import { ... , switchMap } from 'rxjs/operators';
//...
@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
return this.booksService
.create(action.payload)
.pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
When the `SaveBook` action dispatched, the save method is executed. It call `create` method of the `BooksService` defined before. After the service call, `BooksState` dispatches the `GetBooks` action to get books again from the server to refresh the page.
#### Add a Modal to BookListComponent
Open the `book-list.component.html` and add the `abp-modal` to show/hide the book form.
Open the `book-list.component.html` and add the `abp-modal` to show/hide the modal to create a new book.
```html
<abp-modal [(visible)]="isModalOpen">
@ -97,7 +99,7 @@ Open the `book-list.component.html` and add the `abp-modal` to show/hide the boo
`abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits.
Add a button, labeled `New book` to show the modal.
Add a button, labeled `New book` to show the modal:
```html
<div class="row">
@ -107,21 +109,21 @@ Add a button, labeled `New book` to show the modal.
</h5>
</div>
<div class="text-right col col-md-6">
<button id="create-role" class="btn btn-primary" type="button" (click)="onAdd()">
<button id="create-role" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i> <span>New book</span>
</button>
</div>
</div>
```
Open the `book-list.component.ts` and add `isModalOpen` variable and `onAdd` method to show/hide the modal.
Open the `book-list.component.ts` and add `isModalOpen` variable and `createBook` method to show/hide the modal.
```typescript
```js
isModalOpen = false;
//...
onAdd() {
createBook() {
this.isModalOpen = true;
}
```
@ -134,7 +136,7 @@ onAdd() {
Add a `form` variable and inject a `FormBuilder` service to the `book-list.component.ts` as shown below (remember add the import statement).
```typescript
```js
import { FormGroup, FormBuilder } from '@angular/forms';
form: FormGroup;
@ -149,7 +151,7 @@ constructor(
Add the `buildForm` method to create book form.
```typescript
```js
buildForm() {
this.form = this.fb.group({
name: ['', Validators.required],
@ -160,19 +162,19 @@ buildForm() {
}
```
- The `group` method of `FormBuilder` creates a `FormGroup`.
- Added `Validators.required` static method that validation of form element.
- The `group` method of `FormBuilder` (`fb`) creates a `FormGroup`.
- Added `Validators.required` static method that validates the related form element.
Modify the `onAdd` method as shown below:
Modify the `createBook` method as shown below:
```typescript
onAdd() {
```js
createBook() {
this.buildForm();
this.isModalOpen = true;
}
```
### Create the DOM Elements of the Form
#### Create the DOM Elements of the Form
Open `book-list.component.html` and add the form in the body template of the modal.
@ -212,30 +214,34 @@ Open `book-list.component.html` and add the form in the body template of the mod
</ng-template>
```
TODO: Add a short description.
- This template creates a form with Name, Price, Type and Publish date fields.
> We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component.
Open the `book-list.component.ts` and then add the `bookTypes`.
Open the `book-list.component.ts` and then create an array, named `bookTypeArr`:
```typescript
```js
//...
form: FormGroup;
bookTypeArr = Object.keys(Books.BookType).filter(bookType => typeof this.booksType[bookType] === 'number');
bookTypeArr = Object.keys(Books.BookType).filter(
bookType => typeof this.booksType[bookType] === 'number'
);
```
The `bookTypes` variable added to generate array from `BookType` enum. The `bookTypes` equals like this:
The `bookTypeArr` contains the fields of the `BookType` enum. Resulting array is shown below:
```js
['Adventure', 'Biography', 'Dystopia', 'Fantastic' ...]
```
### Add the Datepicker Requirements
This array was used in the previous form template (in the `ngFor` loop).
#### Datepicker Requirements
Import `NgbDatepickerModule` to the `books.module.ts`.
You need to import `NgbDatepickerModule` to the `books.module.ts`:
```typescript
```js
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
@ -247,27 +253,46 @@ import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
export class BooksModule {}
```
Open the `book-list.component.html` and then add `providers` as shown below:
Then open the `book-list.component.ts` and add `providers` as shown below:
```js
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
```typescript
@Component({
// ...
styleUrls: ['./book-list.component.scss'],
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }],
})
export class BookListComponent implements OnInit {
// ...
```
> The `NgbDateAdapter` convert Datepicker value type to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
> The `NgbDateAdapter` converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
![new-book-form](images/bookstore-new-book-form.png)
### Create a New Book
#### Saving the Book
Open the `book-list.component.html` and add an `abp-button` to save the form.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
Cancel
</button>
<button class="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i>
Save
</button>
</ng-template>
```
This adds a save button to the bottom area of the modal:
![bookstore-new-book-form-v2](images/bookstore-new-book-form-v2.png)
Add the `save` method to `BookListComponent`
Then define a `save` method in the `BookListComponent`:
```typescript
```js
save() {
if (this.form.invalid) {
return;
@ -280,72 +305,75 @@ save() {
}
```
TODO: description ??
Then, open the `book-list.component.html` and add the `abp-button` for the run `save` method.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
Cancel
</button>
<abp-button iconClass="fa fa-check" (click)="save()">Save</abp-button>
</ng-template>
```
Now, You can add a new book.
![bookstore-new-book-form-v2](images/bookstore-new-book-form-v2.png)
### Updating An Existing Book
### Add HTTP GET and PUT Methods
#### BooksService
TODO: Description
Open the `books.service.ts` and then add the `getById` and `update` methods.
Open the `book.service.ts` and then add the `getById` and `update` methods.
```typescript
```js
getById(id: string): Observable<Books.Book> {
const request: Rest.Request<null> = {
return this.restService.request<void, Books.Book>({
method: 'GET',
url: `/api/app/book/${id}`,
};
return this.rest.request<null, Books.Book>(request);
url: `/api/app/book/${id}`
});
}
update(body: Books.CreateUpdateBookInput, id: string): Observable<Books.Book> {
const request: Rest.Request<Books.CreateUpdateBookInput> = {
update(updateBookInput: Books.CreateUpdateBookInput, id: string): Observable<Books.Book> {
return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
method: 'PUT',
url: `/api/app/book/${id}`,
body,
};
body: updateBookInput
});
}
```
return this.rest.request<Books.CreateUpdateBookInput, Books.Book>(request);
#### CreateUpdateBook Action
Open the `books.actins.ts` and add `id` parameter to the `CreateUpdateBook` action:
```js
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput, public id?: string) {}
}
```
- Added the `getById` method to get the editing book by performing an HTTP request to the related endpoint.
- Added the `update` method to update a book with the `id` by performing an HTTP request to the related endpoint.
Open `books.state.ts` and modify the `save` method as show below:
### Update a Book
```js
@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
let request;
if (action.id) {
request = this.booksService.update(action.payload, action.id);
} else {
request = this.booksService.create(action.payload);
}
return request.pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
Inject `BooksService` dependency by adding it to the `book-list.component.ts` constructor and add variable named `selectedBook`.
#### BookListComponent
```typescript
Inject `BooksService` dependency by adding it to the `book-list.component.ts` constructor and add a variable named `selectedBook`.
```js
import { BooksService } from '../shared/books.service';
//...
selectedBook = {} as Books.Book;
constructor(
//...
//...
private booksService: BooksService
)
```
- Added `booksService` to get the detail of selected book by `id` before creating the form
- Added `selectedBook` variable to reuse detail of selected book.
Modify the `buildForm` method to reuse the same form while editing a book.
`booksService` is used to get the editing book to prepare the form. Modify the `buildForm` method to reuse the same form while editing a book.
```typescript
```js
buildForm() {
this.form = this.fb.group({
name: [this.selectedBook.name || '', Validators.required],
@ -356,10 +384,10 @@ buildForm() {
}
```
Add the `onEdit` method as shown below:
Add the `editBook` method as shown below:
```typescript
onEdit(id: string) {
```js
editBook(id: string) {
this.booksService.getById(id).subscribe(book => {
this.selectedBook = book;
this.buildForm();
@ -368,28 +396,32 @@ Add the `onEdit` method as shown below:
}
```
- Added `onEdit` method to get selected book detail, build form and then show the modal.
Added `editBook` method to get the editing book, build the form and show the modal.
Add the `selectedBook` definition to `onAdd` method for reuse same form while adding a new book.
Now, add the `selectedBook` definition to `createBook` method to reuse the same form while creating a new book:
```typescript
onAdd() {
this.selectedBook = {} as Books.Item;
```js
createBook() {
this.selectedBook = {} as Books.Book;
//...
}
```
Add the `this.selectedBook.id` to `CreateUpdateBook` action in `save` method.
Modify the `save` method to pass the id of the selected book as shown below:
```typescript
save() {
//...
this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id))
//...
```js
save() {
if (this.form.invalid) {
return;
}
```
Added the `this.selectedBook.id` property to reuse the `CreateUpdateBook` action.
this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id))
.subscribe(() => {
this.isModalOpen = false;
this.form.reset();
});
}
```
#### Add "Actions" Dropdown to the Table
@ -419,7 +451,7 @@ Open the `book-list.component.html` and add modify the `p-table` as shown belo
<i class="fa fa-cog mr-1"></i>Actions
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="onEdit(data.id)">Edit</button>
<button ngbDropdownItem (click)="editBook(data.id)">Edit</button>
</div>
</div>
</td>
@ -430,99 +462,70 @@ Open the `book-list.component.html` and add modify the `p-table` as shown belo
</tr>
</ng-template>
</p-table>
>
```
Actions button added to each row of the table.
- Added a `th` for the "Actions" column.
- Added `button` with `ngbDropdownToggle` to open actions when clicked the button.
> We've used to [NgbDropdown](https://ng-bootstrap.github.io/#/components/dropdown/examples) for the dropdown menu of actions.
The Actions buttons looks like this:
The final UI looks like:
![actions-buttons](images/bookstore-actions-buttons.png)
Update the modal header for reuse the same modal.
Update the modal header to change the title based on the current operation:
```html
<ng-template #abpHeader>
<h3>{{ (selectedBook.id ? 'Edit' : 'New Book') }}</h3>
<h3>{{ selectedBook.id ? 'Edit' : 'New Book' }}</h3>
</ng-template>
```
![actions-buttons](images/bookstore-edit-modal.png)
TODO: header ??
### Deleting an Existing Book
Open the `books.actins.ts` and add `id` parameter.
#### BooksService
```typescript
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput, public id?: string) {}
}
```
Added `id` parameter to reuse the `BooksSave` while updating and creating a book.
Open `books.state.ts` and then modify the `save` method as show below:
```typescript
@Action(CreateUpdateBook)
save({ dispatch }: StateContext<Books.State>, { payload, id }: CreateUpdateBook) {
let request;
if (id) {
request = this.booksService.update(payload, id);
} else {
request = this.booksService.create(payload);
}
return request.pipe(switchMap(() => dispatch(new GetBooks())));
}
```
TODO: description & screenshot ??
### Delete a Book
Open `books.service.ts` and the the `delete` method for delete a book with the `id` by performing an HTTP request to the related endpoint.
Open `books.service.ts` and add a `delete` method to delete a book with the `id` by performing an HTTP request to the related endpoint:
```typescript
delete(id: string): Observable<null> {
const request: Rest.Request<null> = {
```js
delete(id: string): Observable<void> {
return this.restService.request<void, void>({
method: 'DELETE',
url: `/api/app/book/${id}`,
};
return this.rest.request<null, null>(request);
url: `/api/app/book/${id}`
});
}
```
### State Definitions
#### DeleteBook Action
Add an action named `BooksDelete` to `books.actions.ts`
Add an action named `DeleteBook` to `books.actions.ts`:
```typescript
```js
export class DeleteBook {
static readonly type = '[Books] Delete';
constructor(public id: string) {}
}
```
Then, open the `books.state.ts` and add the `delete` method that will listen to a `CreateUpdateBook` action to create a book
Open the `books.state.ts` and add the `delete` method that will listen to the `DeleteBook` action to delete a book:
```typescript
```js
import { ... , DeleteBook } from '../actions/books.actions';
//...
@Action(DeleteBook)
delete({ dispatch }: StateContext<Books.State>, { id }: DeleteBook) {
return this.booksService.delete(id).pipe(switchMap(() => dispatch(new GetBooks())));
delete(ctx: StateContext<Books.State>, action: DeleteBook) {
return this.booksService.delete(action.id).pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
TODO: description??
- Added `DeleteBook` to the import list.
- Uses `bookService` to delete the book.
### Add a Delete Button
#### Add a Delete Button
Open `book-list.component.html` and modify the `ngbDropdownMenu` for add the delete button as shown below:
Open `book-list.component.html` and modify the `ngbDropdownMenu` to add the delete button as shown below:
```html
<div ngbDropdownMenu>
@ -533,11 +536,15 @@ Open `book-list.component.html` and modify the `ngbDropdownMenu` for add the del
</div>
```
### Open Confirmation Popup
The final actions dropdown UI looks like below:
![bookstore-final-actions-dropdown](images/bookstore-final-actions-dropdown.png)
Open `book-list.component.ts` and inject the `ConfirmationService` for show confirmation popup.
#### Delete Confirmation Dialog
```typescript
Open `book-list.component.ts` and inject the `ConfirmationService`.
```js
import { ConfirmationService } from '@abp/ng.theme.shared';
//...
constructor(
@ -546,10 +553,13 @@ constructor(
)
```
Add the following method to `BookListComponent`.
> `ConfirmationService` is a simple service provided by ABP framework that internally uses the PrimeNG.
```typescript
import { ConfirmationService, Toaster } from '@abp/ng.theme.shared';
Add a delete method to the `BookListComponent`:
```js
import { ... , DeleteBook } from '../../store/actions';
import { ... , Toaster } from '@abp/ng.theme.shared';
//...
delete(id: string, name: string) {
this.confirmationService
@ -562,4 +572,10 @@ delete(id: string, name: string) {
}
```
The `delete` method shows confirmation popup and listens to them. When close the popup, the subscribe block runs. If confirmed this popup, it will dispatch the `DeleteBook` action.
The `delete` method shows a confirmation popup and subscribes for the user response. `DeleteBook` action dispatched only if user clicks to the `Yes` button. The confirmation popup looks like below:
![bookstore-confirmation-popup](images/bookstore-confirmation-popup.png)
### Next Part
See the [next part](Part-III.md) of this tutorial.

178
docs/en/Tutorials/Angular/Part-III.md

@ -0,0 +1,178 @@
## ASP.NET Core MVC Tutorial - Part III
### About this Tutorial
This is the third part of the Angular tutorial series. See all parts:
- [Part I: Create the project and a book list page](Part-I.md)
- [Part II: Create, Update and Delete books](Part-II.md)
- **Part III: Integration Tests (this tutorial)**
This part covers the **server side** tests. You can access to the **source code** of the application from the [GitHub repository](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb).
### Test Projects in the Solution
There are multiple test projects in the solution:
![bookstore-test-projects](images/bookstore-test-projects-v3.png)
Each project is used to test the related application project. Test projects use the following libraries for testing:
* [xunit](https://xunit.github.io/) as the main test framework.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) as an assertion library.
* [NSubstitute](http://nsubstitute.github.io/) as a mocking library.
### Adding Test Data
Startup template contains the `BookStoreTestDataSeedContributor` class in the `Acme.BookStore.TestBase` project that creates some data to run tests on.
Change the `BookStoreTestDataSeedContributor` class as show below:
````C#
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreTestDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreTestDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
await _bookRepository.InsertAsync(
new Book
{
Id = _guidGenerator.Create(),
Name = "Test book 1",
Type = BookType.Fantastic,
PublishDate = new DateTime(2015, 05, 24),
Price = 21
}
);
await _bookRepository.InsertAsync(
new Book
{
Id = _guidGenerator.Create(),
Name = "Test book 2",
Type = BookType.Science,
PublishDate = new DateTime(2014, 02, 11),
Price = 15
}
);
}
}
}
````
* Injected `IRepository<Book, Guid>` and used it in the `SeedAsync` to create two book entities as the test data.
* Used `IGuidGenerator` service to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases (see the [Guid generation document](../../Guid-Generation.md) for more).
### Testing the BookAppService
Create a test class named `BookAppService_Tests` in the `Acme.BookStore.Application.Tests` project:
````C#
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit;
namespace Acme.BookStore
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "Test book 1");
}
}
}
````
* `Should_Get_List_Of_Books` test simply uses `BookAppService.GetListAsync` method to get and check the list of users.
Add a new test that creates a valid new book:
````C#
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
````
Add a new test that tries to create an invalid book and fails:
````C#
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
````
* Since the `Name` is empty, ABP throws an `AbpValidationException`.
Open the **Test Explorer Window** (use Test -> Windows -> Test Explorer menu if it is not visible) and **Run All** tests:
![bookstore-appservice-tests](images/bookstore-test-explorer.png)
Congratulations, green icons show that tests have been successfully passed!

BIN
docs/en/Tutorials/Angular/images/bookstore-confirmation-popup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-final-actions-dropdown.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-test-explorer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/Tutorials/Angular/images/bookstore-test-projects-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

38
docs/en/docs-nav.json

@ -7,8 +7,12 @@
"text": "From Startup Templates",
"items": [
{
"text": "ASP.NET Core MVC",
"text": "Application with MVC (Razor Pages) UI",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
},
{
"text": "Application with Angular UI",
"path": "Getting-Started-Angular-Template.md"
}
]
},
@ -34,8 +38,12 @@
"text": "Application Development",
"items": [
{
"text": "With ASP.NET Core MVC",
"text": "With ASP.NET Core MVC UI",
"path": "Tutorials/AspNetCore-Mvc/Part-I.md"
},
{
"text": "With Angular UI",
"path": "Tutorials/Angular/Part-I.md"
}
]
}
@ -237,11 +245,21 @@
"items": [
{
"text": "Entity Framework Core Integration",
"path": "Entity-Framework-Core.md"
"path": "Entity-Framework-Core.md",
"items": [
{
"text": "PostgreSQL Integration",
"path": "Best-Practices/PostgreSQL-Integration.md"
}
]
},
{
"text": "MongoDB Integration",
"path": "MongoDB.md"
},
{
"text": "Dapper Integration",
"path": "Dapper.md"
}
]
},
@ -264,6 +282,20 @@
}
]
},
{
"text": "Startup Templates",
"path": "Startup-Templates/Index.md",
"items": [
{
"text": "Application",
"path": "Startup-Templates/Application.md"
},
{
"text": "Module",
"path": "Startup-Templates/Module.md"
}
]
},
{
"text": "Samples",
"items": [

8
docs/zh-Hans/Application-Services.md

@ -146,7 +146,7 @@ public interface IBookAppService : IApplicationService
`BookDto`是一个简单的[DTO](Data-Transfer-Objects.md)类, 定义如下:
````csharp
[AutoMapFrom(typeof(Book))] //Defines the mapping
[AbpAutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto
{
public Guid Id { get; set; }
@ -159,7 +159,7 @@ public class BookDto
}
````
* `BookDto`定义了`[AutoMapFrom(typeof(Book))]`属性来从创建对象映射Book到BookDto.
* `BookDto`定义了`[AbpAutoMapFrom(typeof(Book))]`属性来从创建对象映射Book到BookDto.
然后你可以实现`GetAsync`方法. 如下所示:
@ -247,7 +247,7 @@ public interface IAsyncCrudAppService<
示例中使用的DTO类是`BookDto`和`CreateUpdateBookDto`:
````csharp
[AutoMapFrom(typeof(Book))]
[AbpAutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -257,7 +257,7 @@ public class BookDto : AuditedEntityDto<Guid>
public float Price { get; set; }
}
[AutoMapTo(typeof(Book))]
[AbpAutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]

19
docs/zh-Hans/CLI.md

@ -39,15 +39,20 @@ abp new Acme.BookStore
#### Options
* `--template` 或 `-t`: 指定模板. 默认的模板是 `mvc`.可用的模板有:
* `mvc` (默认): ASP.NET Core [MVC应用程序模板](Startup-Templates/Mvc.md). 其他选项:
* `--database-provider` 或 `-d`: 指定数据库提供程序. 默认提供程序是 `ef`. 可用的提供程序有:
* `--template` 或者 `-t`: 指定模板. 默认的模板是 `app`,会生成web项目.可用的模板有:
* `app` (default): [应用程序模板](Startup-Templates/Application.md)。 其他选项:
* `--ui` 或者 `-u`: 指定ui框架。默认`mvc`框架。其他选项:
* `mvc`: ASP.NET Core MVC。此模板的其他选项:
* `--tiered`: 创建分层解决方案,Web和Http Api层在物理上是分开的。如果未指定会创建一个分层的解决方案,此解决方案没有那么复杂,适合大多数场景。
* `angular`: Angular. 这个模板还有一些额外的选项:
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `--database-provider` 或者 `-d`: 指定数据库提供程序。默认是 `ef`。其他选项:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--tiered`: 创建分层解决方案,Web和Http Api层在物理上是分开的. 如果未指定会创建一个分层的解决方案, 此解决方案没有那么复杂,适合大多数场景.
* `module`: [模块模板](Startup-Templates/Module.md). 其他选项:
* `--no-ui`: 不包含UI. 仅创建服务模块 (也称为微服务 - 没有UI).
* `--output-folder` 或 `-o`: 指定输出文件夹,默认是当前目录.
* `module`: [Module template](Startup-Templates/Module.md). 其他选项:
* `--no-ui`: 不包含UI。仅创建服务模块(也称为微服务 - 没有UI)。
* `--output-folder` 或者 `-o`: 指定输出文件夹,默认是当前目录。
* `--version` 或者 `-v`: 指定ABP和模板的版本。它可以是 [release tag](https://github.com/abpframework/abp/releases) 或者 [branch name](https://github.com/abpframework/abp/branches). 如果没有指定,则使用最新版本。大多数情况下,您会希望使用最新的版本。
### add-package

61
docs/zh-Hans/Dapper.md

@ -0,0 +1,61 @@
# Dapper 集成
由于Dapper的思想是sql语句优先, 且主要为`IDbConnection`接口提供了一些扩展方法.
Abp并没有为Dapper封装太多功能. Abp Dapper在Abp EntityFrameworkCore的基础上提供了`DapperRepository<TDbContext>`基类, 在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性.
这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合.
## 安装
请先根据[EF Core的集成文档](Entity-Framework-Core.md)安装并配置好EF Core.
`Volo.Abp.Dapper`是Dapper集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层):
```shell
Install-Package Volo.Abp.Dapper
```
然后添加 `AbpDapperModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
````C#
using Volo.Abp.Dapper;
using Volo.Abp.Modularity;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpDapperModule))]
public class MyModule : AbpModule
{
//...
}
}
````
## 实现Dapper仓储
下面的代码实现了`Person`仓储, 它需要EF Core的`DbContext`(MyAppDbContext). 你可以注入`PersonDapperRepository`来调用它的方法.
`DbConnection`和`DbTransaction`来自于`DapperRepository`基类.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency
{
public PersonDapperRepository(IDbContextProvider<MyAppDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<List<string>> GetAllPersonNames()
{
return (await DbConnection.QueryAsync<string>("select Name from People", transaction: DbTransaction))
.ToList();
}
public virtual async Task<int> UpdatePersonNames(string name)
{
return await DbConnection.ExecuteAsync("update People set Name = @NewName", new { NewName = name },
DbTransaction);
}
}
```

2
docs/zh-Hans/Entity-Framework-Core.md

@ -10,7 +10,7 @@
Install-Package Volo.Abp.EntityFrameworkCore
```
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.md)(项目中的Mudole类):
````C#
using Volo.Abp.EntityFrameworkCore;

2
docs/zh-Hans/Startup-Templates/Module.md

@ -25,7 +25,7 @@ abp new Acme.IssueManagement -t module
默认情况模板附带一个MVC UI,你可以使用`--no-ui`选项指定不包含UI层.
````bash
abp new Acme.IssueManagement -t mvc-module --no-ui
abp new Acme.IssueManagement -t module --no-ui
````
## 解决方案结构

4
docs/zh-Hans/docs-nav.json

@ -242,6 +242,10 @@
{
"text": "MongoDB 集成",
"path": "MongoDB.md"
},
{
"text": "Dapper 集成",
"path": "Dapper.md"
}
]
},

14
framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpContextTenantResolveResultAccessor.cs

@ -7,17 +7,11 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
[Dependency(ReplaceServices = true)]
public class HttpContextTenantResolveResultAccessor : ITenantResolveResultAccessor, ITransientDependency
{
public const string HttpContextItemName = "__AbpTenantResolveResult";
public TenantResolveResult Result
{
get
{
if (_httpContextAccessor.HttpContext == null)
{
return null;
}
return _httpContextAccessor.HttpContext.Items[""] as TenantResolveResult;
}
get => _httpContextAccessor.HttpContext?.Items[HttpContextItemName] as TenantResolveResult;
set
{
if (_httpContextAccessor.HttpContext == null)
@ -25,7 +19,7 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
return;
}
_httpContextAccessor.HttpContext.Items[""] = value;
_httpContextAccessor.HttpContext.Items[HttpContextItemName] = value;
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml

@ -27,7 +27,16 @@
{
<li class="nav-item">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@menuItem.DisplayName</a>
<a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@if (menuItem.Icon != null)
{
if (menuItem.Icon.StartsWith("fa"))
{
<i class="@menuItem.Icon"></i>
}
}
@menuItem.DisplayName
</a>
<div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)">
@foreach (var childMenuItem in menuItem.Items)
{

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml

@ -10,6 +10,13 @@
if (Model.Url != null)
{
<a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)>
@if (Model.Icon != null)
{
if (Model.Icon.StartsWith("fa"))
{
<i class="@Model.Icon"></i>
}
}
@Model.DisplayName
</a>
}

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -23,7 +23,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="3.1.5" />
</ItemGroup>
</Project>

4
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs

@ -64,14 +64,14 @@ namespace Volo.Abp.AspNetCore.Auditing
protected virtual string GetBrowserInfo(HttpContext httpContext)
{
return httpContext?.Request?.Headers?["User-Agent"];
return httpContext.Request?.Headers?["User-Agent"];
}
protected virtual string GetClientIpAddress(HttpContext httpContext)
{
try
{
return httpContext?.Connection?.RemoteIpAddress?.ToString();
return httpContext.Connection?.RemoteIpAddress?.ToString();
}
catch (Exception ex)
{

2
framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="8.1.1" />
<PackageReference Include="AutoMapper" Version="9.0.0" />
</ItemGroup>
</Project>

36
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs

@ -1,13 +1,9 @@
using System;
using System.Linq;
using System.Reflection;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Reflection;
namespace Volo.Abp.AutoMapper
{
@ -34,7 +30,6 @@ namespace Volo.Abp.AutoMapper
void ConfigureAll(IAbpAutoMapperConfigurationContext ctx)
{
FindAndAutoMapTypes(ctx);
foreach (var configurator in options.Configurators)
{
configurator(ctx);
@ -59,36 +54,5 @@ namespace Volo.Abp.AutoMapper
scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = mapperConfiguration.CreateMapper();
}
}
private void FindAndAutoMapTypes(IAbpAutoMapperConfigurationContext context)
{
//TODO: AutoMapping (by attributes) can be optionally enabled/disabled.
var typeFinder = context.ServiceProvider.GetRequiredService<ITypeFinder>();
var logger = context.ServiceProvider.GetRequiredService<ILogger<AbpAutoMapperModule>>();
var types = typeFinder.Types.Where(type =>
{
var typeInfo = type.GetTypeInfo();
return typeInfo.IsDefined(typeof(AutoMapAttribute)) ||
typeInfo.IsDefined(typeof(AutoMapFromAttribute)) ||
typeInfo.IsDefined(typeof(AutoMapToAttribute));
}
).ToArray();
if (types.Length <= 0)
{
logger.LogDebug($"No class found with auto mapping attributes.");
}
else
{
logger.LogDebug($"Found {types.Length} classes define auto mapping attributes.");
foreach (var type in types)
{
logger.LogDebug(type.FullName);
context.MapperConfiguration.CreateAutoAttributeMaps(type);
}
}
}
}
}

45
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperOptions.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using Volo.Abp.Collections;
@ -17,8 +18,30 @@ namespace Volo.Abp.AutoMapper
ValidatingProfiles = new TypeList<Profile>();
}
public void AddMaps<TModule>(bool validate = false)
{
var assembly = typeof(TModule).Assembly;
Configurators.Add(context =>
{
context.MapperConfiguration.AddMaps(assembly);
});
if (validate)
{
var profileTypes = assembly
.DefinedTypes
.Where(type => typeof(Profile).IsAssignableFrom(type) && !type.IsAbstract && !type.IsGenericType);
foreach (var profileType in profileTypes)
{
ValidatingProfiles.Add(profileType);
}
}
}
public void AddProfile<TProfile>(bool validate = false)
where TProfile: Profile, new()
where TProfile : Profile, new()
{
Configurators.Add(context =>
{
@ -27,7 +50,25 @@ namespace Volo.Abp.AutoMapper
if (validate)
{
ValidatingProfiles.Add<TProfile>();
ValidateProfile(typeof(TProfile));
}
}
public void ValidateProfile<TProfile>(bool validate = true)
where TProfile : Profile
{
ValidateProfile(typeof(TProfile), validate);
}
public void ValidateProfile(Type profileType, bool validate = true)
{
if (validate)
{
ValidatingProfiles.AddIfNotContains(profileType);
}
else
{
ValidatingProfiles.Remove(profileType);
}
}
}

29
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapAttribute.cs

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapAttribute : AutoMapAttributeBase
{
public AutoMapAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(type, targetType, MemberList.Source);
configuration.CreateMap(targetType, type, MemberList.Destination);
}
}
}
}

17
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapAttributeBase.cs

@ -1,17 +0,0 @@
using System;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public abstract class AutoMapAttributeBase : Attribute
{
public Type[] TargetTypes { get; }
protected AutoMapAttributeBase(params Type[] targetTypes)
{
TargetTypes = targetTypes;
}
public abstract void CreateMap(IMapperConfigurationExpression configuration, Type type);
}
}

36
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapFromAttribute.cs

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapFromAttribute : AutoMapAttributeBase
{
public MemberList MemberList { get; set; } = MemberList.Destination;
public AutoMapFromAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public AutoMapFromAttribute(MemberList memberList, params Type[] targetTypes)
: this(targetTypes)
{
MemberList = memberList;
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(targetType, type, MemberList);
}
}
}
}

36
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapToAttribute.cs

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapToAttribute : AutoMapAttributeBase
{
public MemberList MemberList { get; set; } = MemberList.Source;
public AutoMapToAttribute(params Type[] targetTypes)
: base(targetTypes)
{
}
public AutoMapToAttribute(MemberList memberList, params Type[] targetTypes)
: this(targetTypes)
{
MemberList = memberList;
}
public override void CreateMap(IMapperConfigurationExpression configuration, Type type)
{
if (TargetTypes.IsNullOrEmpty())
{
return;
}
foreach (var targetType in TargetTypes)
{
configuration.CreateMap(type, targetType, MemberList);
}
}
}
}

17
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperConfigurationExtensions.cs

@ -1,17 +0,0 @@
using System;
using System.Reflection;
using AutoMapper;
namespace Volo.Abp.AutoMapper
{
internal static class AutoMapperConfigurationExtensions
{
public static void CreateAutoAttributeMaps(this IMapperConfigurationExpression configuration, Type type)
{
foreach (var autoMapAttribute in type.GetTypeInfo().GetCustomAttributes<AutoMapAttributeBase>())
{
autoMapAttribute.CreateMap(configuration, type);
}
}
}
}

2
framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj

@ -15,7 +15,7 @@
<ItemGroup>
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="4.5.0" />
<PackageReference Include="Autofac" Version="4.9.2" />
<PackageReference Include="Autofac" Version="4.9.3" />
</ItemGroup>
<ItemGroup>

6
framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj

@ -12,13 +12,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpZipLib" Version="1.1.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="HtmlAgilityPack.NetCore" Version="1.5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="NuGet.Versioning" Version="5.1.0" />
<PackageReference Include="NuGet.Versioning" Version="5.2.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
<PackageReference Include="System.Security.Permissions" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.1" />
</ItemGroup>
<ItemGroup>

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using IdentityModel;
@ -17,20 +18,25 @@ namespace Volo.Abp.Cli.Auth
AuthenticationService = authenticationService;
}
public async Task LoginAsync(string userName, string password)
public async Task LoginAsync(string userName, string password, string organizationName = null)
{
var accessToken = await AuthenticationService.GetAccessTokenAsync(
new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio_www abpio_commercial",
"abp-cli",
"1q2w3e*",
OidcConstants.GrantTypes.Password,
userName,
password
)
var configuration = new IdentityClientConfiguration(
CliUrls.AccountAbpIo,
"role email abpio abpio_www abpio_commercial",
"abp-cli",
"1q2w3e*",
OidcConstants.GrantTypes.Password,
userName,
password
);
if (!organizationName.IsNullOrWhiteSpace())
{
configuration["[o]abp-organization-name"] = organizationName;
}
var accessToken = await AuthenticationService.GetAccessTokenAsync(configuration);
File.WriteAllText(CliPaths.AccessToken, accessToken, Encoding.UTF8);
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs

@ -66,7 +66,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp add-module Volo.Blogging Adds the module to the current solution.");
sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore Adds the module to the given solution.");
sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore --skip-db-migrations false Adds the module to the given solution but doesn't create a database migration.");
sb.AppendLine(@" abp add-module Volo.Blogging -s Acme.BookStore -sp src\Acme.BookStore.Web\Acme.BookStore.Web.csproj Adds the module to the given solution and specify migration startup project.");
sb.AppendLine(@" abp add-module Volo.Blogging -s Acme.BookStore -sp ..\Acme.BookStore.Web\Acme.BookStore.Web.csproj Adds the module to the given solution and specify migration startup project.");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs

@ -44,7 +44,11 @@ namespace Volo.Abp.Cli.Commands
);
}
await AuthService.LoginAsync(commandLineArgs.Target, password);
await AuthService.LoginAsync(
commandLineArgs.Target,
password,
commandLineArgs.Options.GetOrNull(Options.Organization.Short, Options.Organization.Long)
);
Logger.LogInformation($"Successfully logged in as '{commandLineArgs.Target}'");
}
@ -70,5 +74,14 @@ namespace Volo.Abp.Cli.Commands
{
return string.Empty;
}
public static class Options
{
public static class Organization
{
public const string Short = "o";
public const string Long = "organization";
}
}
}
}

31
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@ -43,17 +44,21 @@ namespace Volo.Abp.Cli.Commands
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
await UpdateNugetPackages(commandLineArgs);
UpdateNpmPackages();
var updateNpm = commandLineArgs.Options.ContainsKey(Options.Packages.Npm);
var updateNuget = commandLineArgs.Options.ContainsKey(Options.Packages.NuGet);
var options = commandLineArgs.Options
.Select(x => x.Key).ToList();
await _cliAnalyticsCollect.CollectAsync(new CliAnalyticsCollectInputDto
var both = (updateNuget && updateNpm) || (!updateNuget && !updateNpm);
if (updateNpm || both)
{
Tool = _options.ToolName,
Command = commandLineArgs.Command,
Options = _jsonSerializer.Serialize(options)
});
await UpdateNugetPackages(commandLineArgs);
}
if (updateNpm || both)
{
UpdateNpmPackages();
}
}
private void UpdateNpmPackages()
@ -108,6 +113,8 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine("");
sb.AppendLine("Options:");
sb.AppendLine("-p|--include-previews (if supported by the template)");
sb.AppendLine("--npm (Only updates NPM packages)");
sb.AppendLine("--nuget (Only updates Nuget packages)");
sb.AppendLine("");
sb.AppendLine("Some examples:");
sb.AppendLine("");
@ -132,6 +139,12 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "p";
public const string Long = "include-previews";
}
public static class Packages
{
public const string Npm = "npm";
public const string NuGet = "nuget";
}
}
}
}

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Licensing/DeveloperApiKeyResult.cs

@ -8,5 +8,6 @@ namespace Volo.Abp.Cli.Licensing
public string OrganizationName { get; set; }
public string ApiKey { get; set; }
public DateTime LicenseEndTime { get; set; }
public string LicenseCode { get; set; }
}
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Analyticses/CliAnalyticsCollectInputDto.cs

@ -16,6 +16,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Analyticses
public bool? IsTiered { get; set; }
public string UiFramework { get; set; }
public string Options { get; set; }
public string Language { get; set; }

16
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuilder.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Licensing;
using Volo.Abp.Cli.ProjectBuilding.Analyticses;
using Volo.Abp.Cli.ProjectBuilding.Building;
@ -73,10 +74,16 @@ namespace Volo.Abp.Cli.ProjectBuilding
// Exclude unwanted or known options.
var options = args.ExtraProperties
.Where(x => x.Key != CliConsts.Command)
.Where(x => x.Key != "tiered")
.Where(x => x.Key != "database-provider" && x.Key != "d")
.Where(x => x.Key != "output-folder" && x.Key != "o")
.Where(x => !x.Key.Equals(CliConsts.Command, StringComparison.InvariantCultureIgnoreCase))
.Where(x => !x.Key.Equals("tiered", StringComparison.InvariantCultureIgnoreCase))
.Where(x => !x.Key.Equals(NewCommand.Options.DatabaseProvider.Long, StringComparison.InvariantCultureIgnoreCase) &&
!x.Key.Equals(NewCommand.Options.DatabaseProvider.Short, StringComparison.InvariantCultureIgnoreCase))
.Where(x => !x.Key.Equals(NewCommand.Options.OutputFolder.Long, StringComparison.InvariantCultureIgnoreCase) &&
!x.Key.Equals(NewCommand.Options.OutputFolder.Short, StringComparison.InvariantCultureIgnoreCase))
.Where(x => !x.Key.Equals(NewCommand.Options.UiFramework.Long, StringComparison.InvariantCultureIgnoreCase) &&
!x.Key.Equals(NewCommand.Options.UiFramework.Short, StringComparison.InvariantCultureIgnoreCase))
.Where(x => !x.Key.Equals(NewCommand.Options.Version.Long, StringComparison.InvariantCultureIgnoreCase) &&
!x.Key.Equals(NewCommand.Options.Version.Short, StringComparison.InvariantCultureIgnoreCase))
.Select(x => x.Key).ToList();
await CliAnalyticsCollect.CollectAsync(new CliAnalyticsCollectInputDto
@ -85,6 +92,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
Command = args.ExtraProperties.ContainsKey(CliConsts.Command) ? args.ExtraProperties[CliConsts.Command] : "",
DatabaseProvider = args.DatabaseProvider.ToProviderName(),
IsTiered = args.ExtraProperties.ContainsKey("tiered"),
UiFramework = args.UiFramework.ToFrameworkName(),
Options = JsonSerializer.Serialize(options),
ProjectName = args.SolutionName.FullName,
TemplateName = args.TemplateName,

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs

@ -23,6 +23,8 @@ namespace Volo.Abp.Cli.ProjectBuilding
return new AppProTemplate();
case ModuleTemplate.TemplateName:
return new ModuleTemplate();
case ModuleProTemplate.TemplateName:
return new ModuleProTemplate();
default:
throw new Exception("There is no template found with given name: " + name);
}

19
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleProTemplate.cs

@ -0,0 +1,19 @@
using Volo.Abp.Cli.ProjectBuilding.Templates.Module;
namespace Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule
{
public class ModuleProTemplate : ModuleTemplateBase
{
/// <summary>
/// "module".
/// </summary>
public const string TemplateName = "module-pro";
public ModuleProTemplate()
: base(TemplateName)
{
//TODO: uncomment next line after document is ready
//DocumentUrl = "https://docs.abp.io/en/commercial/latest/Startup-Templates/Module";
}
}
}

53
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplate.cs

@ -1,10 +1,8 @@
using System.Collections.Generic;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
using Volo.Abp.Cli.ProjectBuilding.Templates.Module;
namespace Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule
{
public class ModuleTemplate : TemplateInfo
public class ModuleTemplate : ModuleTemplateBase
{
/// <summary>
/// "module".
@ -16,52 +14,5 @@ namespace Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule
{
DocumentUrl = "https://docs.abp.io/en/abp/latest/Startup-Templates/Module";
}
public override IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)
{
var steps = new List<ProjectBuildPipelineStep>();
DeleteUnrelatedProjects(context, steps);
RandomizeSslPorts(context, steps);
CleanupFolderHierarchy(context, steps);
return steps;
}
private void DeleteUnrelatedProjects(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("no-ui"))
{
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web"
));
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web.Host",
projectFolderPath: "/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host"
));
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web.Unified",
projectFolderPath: "/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified"
));
}
}
private void RandomizeSslPorts(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new TemplateRandomSslPortStep(new List<string>
{
"https://localhost:44300",
"https://localhost:44301",
"https://localhost:44302",
"https://localhost:44303"
}));
}
private void CleanupFolderHierarchy(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new MoveFolderStep("/aspnet-core/", "/"));
}
}
}

64
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Module/ModuleTemplateBase.cs

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Module
{
public abstract class ModuleTemplateBase : TemplateInfo
{
protected ModuleTemplateBase([NotNull] string name)
: base(name)
{
}
public override IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)
{
var steps = new List<ProjectBuildPipelineStep>();
DeleteUnrelatedProjects(context, steps);
RandomizeSslPorts(context, steps);
CleanupFolderHierarchy(context, steps);
return steps;
}
private void DeleteUnrelatedProjects(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("no-ui"))
{
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web"
));
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web.Host",
projectFolderPath: "/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host"
));
steps.Add(new RemoveProjectFromSolutionStep(
"MyCompanyName.MyProjectName.Web.Unified",
projectFolderPath: "/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified"
));
}
}
private void RandomizeSslPorts(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new TemplateRandomSslPortStep(new List<string>
{
"https://localhost:44300",
"https://localhost:44301",
"https://localhost:44302",
"https://localhost:44303"
}));
}
private void CleanupFolderHierarchy(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
steps.Add(new MoveFolderStep("/aspnet-core/", "/"));
}
}
}

55
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -34,21 +34,40 @@ namespace Volo.Abp.Cli.ProjectModification
{
var fileList = _packageJsonFileFinder.Find(rootDirectory);
if (fileList.Any())
if (!fileList.Any())
{
_npmGlobalPackagesChecker.Check();
return;
}
_npmGlobalPackagesChecker.Check();
foreach (var file in fileList)
{
UpdatePackagesInFile(file, out var packagesUpdated);
foreach (var file in fileList)
if (packagesUpdated)
{
UpdatePackagesInFile(file);
var fileDirectory = Path.GetDirectoryName(file).EnsureEndsWith(Path.DirectorySeparatorChar);
RunYarnAndGulp(file);
RunYarn(fileDirectory);
if (IsAngularProject(fileDirectory) == false)
{
Thread.Sleep(500);
RunGulp(fileDirectory);
}
}
}
}
protected virtual void UpdatePackagesInFile(string file)
private bool IsAngularProject(string fileDirectory)
{
return File.Exists(Path.Combine(fileDirectory, "angular.json"));
}
protected virtual void UpdatePackagesInFile(string file, out bool packagesUpdated)
{
packagesUpdated = false;
var fileContent = File.ReadAllText(file);
var packageJson = JObject.Parse(fileContent);
var abpPackages = GetAbpPackagesFromPackageJson(packageJson);
@ -60,7 +79,12 @@ namespace Volo.Abp.Cli.ProjectModification
foreach (var abpPackage in abpPackages)
{
UpdatePackage(file, abpPackage);
TryUpdatePackage(file, abpPackage, out var updated);
if (updated)
{
packagesUpdated = true;
}
}
var modifiedFileContent = packageJson.ToString(Formatting.Indented);
@ -68,7 +92,7 @@ namespace Volo.Abp.Cli.ProjectModification
File.WriteAllText(file, modifiedFileContent);
}
protected virtual void UpdatePackage(string file, JProperty package)
protected virtual void TryUpdatePackage(string file, JProperty package, out bool updated)
{
var version = GetLatestVersion(package);
@ -76,8 +100,13 @@ namespace Volo.Abp.Cli.ProjectModification
if (versionWithPrefix == (string)package.Value)
{
updated = false;
return;
}
else
{
updated = true;
}
package.Value.Replace(versionWithPrefix);
@ -92,7 +121,7 @@ namespace Volo.Abp.Cli.ProjectModification
}
var version = CmdHelper.RunCmdAndGetOutput($"npm show {package.Name} version");
_fileVersionStorage[package.Name] = version;
return version;
@ -106,14 +135,6 @@ namespace Volo.Abp.Cli.ProjectModification
return abpPackages;
}
protected virtual void RunYarnAndGulp(string file)
{
var fileDirectory = Path.GetDirectoryName(file).EnsureEndsWith(Path.DirectorySeparatorChar);
RunYarn(fileDirectory);
Thread.Sleep(500);
RunGulp(fileDirectory);
}
protected virtual void RunGulp(string fileDirectory)
{
Logger.LogInformation($"Running Gulp on {fileDirectory}");

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackageJsonFileFinder.cs

@ -27,7 +27,14 @@ namespace Volo.Abp.Cli.ProjectModification
{
var directory = Path.GetDirectoryName(path);
return Directory.GetFiles(directory, "*.csproj", searchOption: SearchOption.TopDirectoryOnly).Any();
if (directory == null)
{
return false;
}
return
Directory.GetFiles(directory, "*.csproj", searchOption: SearchOption.TopDirectoryOnly).Any() ||
File.Exists(Path.Combine(directory, "angular.json"));
}
}
}

2
framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.1" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.0.0" />
<PackageReference Include="Nito.AsyncEx.Context" Version="5.0.0" />
</ItemGroup>

2
framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj

@ -19,7 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Scriban" Version="2.0.1" />
<PackageReference Include="Scriban" Version="2.1.0" />
</ItemGroup>
<ItemGroup>

4
framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />
</ItemGroup>
</Project>

2
framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
</ItemGroup>
<ItemGroup>

4
framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj

@ -19,8 +19,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.6" />
</ItemGroup>
</Project>

16
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -494,24 +494,12 @@ namespace Volo.Abp.EntityFrameworkCore
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsSoftDeleteFilterEnabled || !((ISoftDelete) e).IsDeleted
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> softDeleteFilter = e => !((ISoftDelete)e).IsDeleted || ((ISoftDelete)e).IsDeleted != IsSoftDeleteFilterEnabled;
expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter);
expression = e => !IsSoftDeleteFilterEnabled || !((ISoftDelete) e).IsDeleted;
}
if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsMayHaveTenantFilterEnabled || ((IMayHaveTenant)e).TenantId == CurrentTenantId
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> multiTenantFilter = e => ((IMultiTenant)e).TenantId == CurrentTenantId || (((IMultiTenant)e).TenantId == CurrentTenantId) == IsMultiTenantFilterEnabled;
Expression<Func<TEntity, bool>> multiTenantFilter = e => !IsMultiTenantFilterEnabled || ((IMultiTenant) e).TenantId == CurrentTenantId;
expression = expression == null ? multiTenantFilter : CombineExpressions(expression, multiTenantFilter);
}

2
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs

@ -108,7 +108,7 @@ namespace Volo.Abp.EventBus
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories.ToArray()) //TODO: ToArray should not be needed!
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData, exceptions);
}

2
framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.3" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.6" />
</ItemGroup>
<ItemGroup>

2
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs

@ -81,7 +81,7 @@ namespace Volo.Abp.IdentityModel
get => this.GetOrDefault(nameof(RequireHttps))?.To<bool>() ?? true;
set => this[nameof(RequireHttps)] = value.ToString().ToLowerInvariant();
}
public IdentityClientConfiguration()
{

63
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs

@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
@ -117,26 +118,12 @@ namespace Volo.Abp.IdentityModel
{
case OidcConstants.GrantTypes.ClientCredentials:
return await httpClient.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
Scope = configuration.Scope,
ClientId = configuration.ClientId,
ClientSecret = configuration.ClientSecret
},
await CreateClientCredentialsTokenRequestAsync(discoveryResponse, configuration),
CancellationTokenProvider.Token
);
case OidcConstants.GrantTypes.Password:
return await httpClient.RequestPasswordTokenAsync(
new PasswordTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
Scope = configuration.Scope,
ClientId = configuration.ClientId,
ClientSecret = configuration.ClientSecret,
UserName = configuration.UserName,
Password = configuration.UserPassword
},
await CreatePasswordTokenRequestAsync(discoveryResponse, configuration),
CancellationTokenProvider.Token
);
default:
@ -144,5 +131,49 @@ namespace Volo.Abp.IdentityModel
}
}
}
protected virtual Task<PasswordTokenRequest> CreatePasswordTokenRequestAsync(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration)
{
var request = new PasswordTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
Scope = configuration.Scope,
ClientId = configuration.ClientId,
ClientSecret = configuration.ClientSecret,
UserName = configuration.UserName,
Password = configuration.UserPassword
};
AddParametersToRequestAsync(configuration, request);
return Task.FromResult(request);
}
protected virtual Task<ClientCredentialsTokenRequest> CreateClientCredentialsTokenRequestAsync(
DiscoveryResponse discoveryResponse,
IdentityClientConfiguration configuration)
{
var request = new ClientCredentialsTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
Scope = configuration.Scope,
ClientId = configuration.ClientId,
ClientSecret = configuration.ClientSecret
};
AddParametersToRequestAsync(configuration, request);
return Task.FromResult(request);
}
protected virtual Task AddParametersToRequestAsync(IdentityClientConfiguration configuration, Request request)
{
foreach (var pair in configuration.Where(p => p.Key.StartsWith("[o]", StringComparison.OrdinalIgnoreCase)))
{
request.Parameters[pair.Key] = pair.Value;
}
return Task.CompletedTask;
}
}
}

2
framework/src/Volo.Abp.Security/Volo/Abp/Users/CurrentUser.cs

@ -51,7 +51,7 @@ namespace Volo.Abp.Users
return _principalAccessor.Principal?.Claims.ToArray() ?? EmptyClaimsArray;
}
public bool IsInRole(string roleName)
public virtual bool IsInRole(string roleName)
{
return FindClaims(AbpClaimTypes.Role).Any(c => c.Value == roleName);
}

11
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs

@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
@ -13,10 +14,14 @@ namespace Volo.Abp.Validation
{
public const int MaxRecursiveParameterValidationDepth = 8;
protected IServiceProvider ServiceProvider { get; }
protected AbpValidationOptions Options { get; }
public DataAnnotationObjectValidationContributor(IOptions<AbpValidationOptions> options)
public DataAnnotationObjectValidationContributor(
IOptions<AbpValidationOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
}
@ -90,7 +95,7 @@ namespace Volo.Abp.Validation
if (validatingObject is IValidatableObject validatableObject)
{
errors.AddRange(
validatableObject.Validate(new ValidationContext(validatableObject))
validatableObject.Validate(new ValidationContext(validatableObject, ServiceProvider, null))
);
}
}
@ -103,7 +108,7 @@ namespace Volo.Abp.Validation
return;
}
var validationContext = new ValidationContext(validatingObject)
var validationContext = new ValidationContext(validatingObject, ServiceProvider, null)
{
DisplayName = property.DisplayName,
MemberName = property.Name

4
framework/test/AbpTestBase/AbpTestBase.csproj

@ -15,8 +15,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="NSubstitute" Version="4.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NSubstitute" Version="4.2.1" />
<PackageReference Include="Shouldly" Version="3.0.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />

2
framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Authentication.OAuth\Volo.Abp.AspNetCore.Authentication.OAuth.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.MultiTenancy\Volo.Abp.AspNetCore.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj

@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
<ItemGroup>

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj

@ -20,7 +20,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
<!-- Below ItemGroup and Target tags are added according to https://github.com/aspnet/Hosting/issues/959#issuecomment-286351703 -->

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json

@ -3,7 +3,7 @@
"name": "asp.net",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.shared": "^0.6.6",
"@abp/aspnetcore.mvc.ui.theme.shared": "^0.8.0",
"highlight.js": "^9.13.1"
},
"devDependencies": {}

192
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock

@ -2,28 +2,28 @@
# yarn lockfile v1
"@abp/aspnetcore.mvc.ui.theme.shared@^0.6.6":
version "0.6.6"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-0.6.6.tgz#390625c457390ff399e8230c155f53c2f0bb3bcd"
integrity sha512-+qs1KoOfgbc6uKthZVVrWPyUgXQt6PXS3ugPT8IaqKVCLYRKTEOUQpOugMajnP+GZlw7toFwsN8jjk4T3AIsvQ==
dependencies:
"@abp/aspnetcore.mvc.ui" "^0.6.4"
"@abp/bootstrap" "^0.6.5"
"@abp/datatables.net-bs4" "^0.6.5"
"@abp/font-awesome" "^0.6.5"
"@abp/jquery-form" "^0.6.5"
"@abp/jquery-validation-unobtrusive" "^0.6.5"
"@abp/lodash" "^0.6.5"
"@abp/malihu-custom-scrollbar-plugin" "^0.6.5"
"@abp/select2" "^0.6.5"
"@abp/sweetalert" "^0.6.5"
"@abp/timeago" "^0.6.5"
"@abp/toastr" "^0.6.5"
"@abp/aspnetcore.mvc.ui@^0.6.4":
version "0.6.4"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-0.6.4.tgz#db28bce4eaf57b28e8a4465373809976cea6b950"
integrity sha512-wPDPtrxY6sRksxrmOZE9fYLmgOD7eCTL8gugLGsp6rXZUykYAMmKhyswN3BP/Vml08RM2CvcMiz+AC3H75vldA==
"@abp/aspnetcore.mvc.ui.theme.shared@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-0.8.0.tgz#3892843e1519aa8e9bf56bd08d69a7114de20b56"
integrity sha512-s63rg2nmjbJn5oBrPwt0VBw0V8LQsS4kpup7b7jW8f6Fw1k07w6i4XVG0JZCZPx3HTCZbHc+Nu3fBTUhZfokaA==
dependencies:
"@abp/aspnetcore.mvc.ui" "^0.8.0"
"@abp/bootstrap" "^0.8.0"
"@abp/datatables.net-bs4" "^0.8.0"
"@abp/font-awesome" "^0.8.0"
"@abp/jquery-form" "^0.8.0"
"@abp/jquery-validation-unobtrusive" "^0.8.0"
"@abp/lodash" "^0.8.0"
"@abp/malihu-custom-scrollbar-plugin" "^0.8.0"
"@abp/select2" "^0.8.0"
"@abp/sweetalert" "^0.8.0"
"@abp/timeago" "^0.8.0"
"@abp/toastr" "^0.8.0"
"@abp/aspnetcore.mvc.ui@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-0.8.0.tgz#bb5234696771a900caa91a22033d017ceecd7df5"
integrity sha512-3wokY7KSjoft9O4UnysHZ5756YPTYdKMogxWddzJXCZXK8jJfx0hbAwV0sK+3Q0f4DGOzIzRoxE0UH6Dk2a9pw==
dependencies:
ansi-colors "^3.2.4"
extend-object "^1.0.0"
@ -32,121 +32,121 @@
path "^0.12.7"
rimraf "^2.6.3"
"@abp/bootstrap@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-0.6.5.tgz#5aec7422c07b25410560308e949d0bb17533d2cb"
integrity sha512-vIcQG5YEpY6ojNlHUl/zPZHIYi1dMDklwW+wKB37KH1W8ZoQ5IFMDE6+fGpjdgazApR8BstH/TlleeZmk+7Qeg==
"@abp/bootstrap@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-0.8.0.tgz#ef5b076a892c47ebf00a13420b819a5f627b0beb"
integrity sha512-ZUu+4pbX1ky1Ub41d68VKAa1M/dy+u5RtEAvv2aqECsGu9LUZTxG5hc9adU59ZdnhPjARe2AeG9LA7Pf4KOAVA==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
bootstrap "^4.1.1"
"@abp/core@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-0.6.5.tgz#2fd6975828cd33e1092c98f20f24749b95f9c638"
integrity sha512-b2uGNSFS50AyhYYkXfrHmFcDRymWrJRiE7cY2i23K62CH6eqEtAvld6bHt6ho5NhCrQyy8ua9fUeASX0Ym8MVw==
"@abp/core@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-0.8.0.tgz#e7504cc14acc3a44bee2a414717a43c234600a14"
integrity sha512-hAY1HzZ+NkpmeutVrSajA1jBLoyaNonncO+qa6J463v2UJ/a7+WYGIfllsAIhaUDNqHhFtED2LJHQNEG9gpCfA==
"@abp/datatables.net-bs4@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-0.6.5.tgz#515ec63dea5b23f1ad802bdd8d5e9d337ed9db98"
integrity sha512-1F8YnwI9/BUe+RoYYjoVDy5XAzqWVVOFtyzZy4z+IuJaHk7AwZoTvgV/5Z2l/L8x5ExxRkF0bCS6VHbM6h1TPQ==
"@abp/datatables.net-bs4@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-0.8.0.tgz#acc42026a49948405792fcea2bcece002fe13e38"
integrity sha512-jqWxpbzfaq9VL+xb8qNMYJc0odAwZCbybHbN4kTPN4vIasL4Yhiotsxv6EC7T6YR9FxbAXnHpdmKBUogO3TGrw==
dependencies:
"@abp/datatables.net" "^0.6.5"
"@abp/datatables.net" "^0.8.0"
datatables.net-bs4 "^1.10.16"
"@abp/datatables.net@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-0.6.5.tgz#5c10bbd30243253dd13eb454f19401ac2f6322c0"
integrity sha512-zPuDyDzQ1mzgk3Ofh3TrHwSKa/ti5otnJanJM8W1C9hA9Dvq5xkudXApADLRJVeS3W32vRvwTCdVJlNgqu1PWQ==
"@abp/datatables.net@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-0.8.0.tgz#0a596cbf810a224eeae9189442d0e993eaf4c18a"
integrity sha512-u2o6ZYzJ5kDi7t3ONhbwe14qNSVUj/5egIy645Ab8tDN7wEOt3eS7M3kledCEFmCcdmH2Ls9lxeHK5KMaizzzA==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
datatables.net "^1.10.16"
"@abp/font-awesome@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-0.6.5.tgz#9503fde1d4096493dbe6256ebfde12927786a921"
integrity sha512-SLAxH7MOFfWlTdkYpz8DG5MBAocqKzhBTI9tC3nsLw0H+QuZMT0hsnJG+bSdtGwpDQqO/Oncn6GDwl4zBOM0dA==
"@abp/font-awesome@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-0.8.0.tgz#9325be9f6c7c1258c71b8706a0aea4f8436c67d1"
integrity sha512-R9NGupEjpMFaQ7EOumMwZd5+DgO+UIZ9KgvLU8q+SYTSQ/aSpYUX85BuUpaCWihKpOmucRHDBY4Jy8X/hBtLgQ==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
font-awesome "^4.7.0"
"@abp/jquery-form@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-0.6.5.tgz#dd3aea1116d41b7d9111ba53c759446c50d47e84"
integrity sha512-uQJViV0PhJrMFSqigdodKffWGTHICvvb5Wvgi0QgvtsXHgVyI1gJCKkWry0xVyE/vhqneA/kzYVa2cW6mkTb6w==
"@abp/jquery-form@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-0.8.0.tgz#9408a9fb7b240d21163fe3ccad703701fe22f40c"
integrity sha512-krUQkEV0HbrjsPmKB6qivVGJvB69SfyGE0t4hRrrOPh2NpSPeeQvVHlm9uTuzJyQ57/JPYuGINe2M8/bVbHdNA==
dependencies:
"@abp/jquery" "^0.6.5"
"@abp/jquery" "^0.8.0"
jquery-form "^4.2.2"
"@abp/jquery-validation-unobtrusive@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-0.6.5.tgz#5d00805c85abb261f67d25d3e414046393e125ed"
integrity sha512-/AJYAsEL1PSPCxPXg0zcb7xujVHvOwDm5lbjmT/pEnjO0IuEEfaCrdUnhcm9WOtb5BOM1e+B5doE+oThiDC6LQ==
"@abp/jquery-validation-unobtrusive@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-0.8.0.tgz#369c9e995b6da6ef76519ce1f3ed41dc2f4e2bac"
integrity sha512-qkEXYe6kiI3UECK1vfiR+8zrdrr3bzhbdttP6F+DdstncPEe593B976H/jcXYruAXYAPIgP8sM5CTWoL0HrF2Q==
dependencies:
"@abp/jquery-validation" "^0.6.5"
"@abp/jquery-validation" "^0.8.0"
jquery-validation-unobtrusive "^3.2.9"
"@abp/jquery-validation@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-0.6.5.tgz#a08591f4734a398f3776023d3930b80069acab42"
integrity sha512-afWoD2oWaF3beafWOi4YJLdATKq2I0NvVzL1/dIt9SoUleADtJBqCuvIviVRHNQmKasNhcxy5zQa3PhsiYedKQ==
"@abp/jquery-validation@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-0.8.0.tgz#c9fccfa8afd7b2bc2c455c131fd95338c2954c60"
integrity sha512-9ug0UTTfPdjOKlbUImzTP30SPY+IXz6BBHU4ODpNpIB9bnspW0VymSme3XJnM01pAiwjYEptf+uh7wuXbUvnDQ==
dependencies:
"@abp/jquery" "^0.6.5"
"@abp/jquery" "^0.8.0"
jquery-validation "^1.17.0"
"@abp/jquery@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-0.6.5.tgz#e87087ee1d8af32080fad2f6b395d3e7a0d04f77"
integrity sha512-al8i+W0nmMlHFILl8Koq2XPutP6XEbp+YcJ4F3IOTOGpNyf/FEAdTYj8famjdkr+zCluWcv0IZpU4R0mxnVINw==
"@abp/jquery@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-0.8.0.tgz#3ece7d75f6f19adc1adf07f54e5db5c7c74931c2"
integrity sha512-cAZR/Jl5zWSbnYBKiLYbfItTVIf8teIeGVztA1pI6l40KpvtbGJsokz94su89kSMQj4Ixzo8SfSYlCgaFwYFSg==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
jquery "^3.3.1"
"@abp/lodash@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-0.6.5.tgz#227a1d7a93f8f3a642f439f64f7ac4fe12a5d8c3"
integrity sha512-Jg4tbLR8QVTwcPcgJ6kStjYLxVApNhPploJHBFECnd1uVfHGa0U8Vc+FhVv9/d0vAfpVYN/UGx3LCVT9goH39w==
"@abp/lodash@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-0.8.0.tgz#54bfbe8d69dbd6866b1d923973f20033fc01457d"
integrity sha512-hzkSD36Ig127futjVBTi8HkO6KLPcsCyPiwVyncu1TVNTf37LVOcgtSdp7kaHV6ExwNs+u5FCpUUUftA+MHZig==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
lodash "^4.17.10"
"@abp/malihu-custom-scrollbar-plugin@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-0.6.5.tgz#00383dd433ab0f87b80deebf271ac243e22133d1"
integrity sha512-u2r1rv4WlaaIM8S1aiHONGAO0hLqOgsauC/TLgb7JBD6H54pTcPuiIYn9C8mwJ/EThoByGE3gOxEdY+11bmy+A==
"@abp/malihu-custom-scrollbar-plugin@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-0.8.0.tgz#fdd561e34d2e78dbb18141a5b45f3067e78d13f7"
integrity sha512-YAcVVWkmCv+eAiZl8FMK4inQAYLgqvtT6wp3LHPSwpi9M+npP7rsM1F8Xh8Yxi19ts7/QSluQMdfUqAlC8tzrg==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
malihu-custom-scrollbar-plugin "^3.1.5"
"@abp/select2@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-0.6.5.tgz#0cb45726c7beb60a69a314045b08c938c9ab6595"
integrity sha512-RKvK/jKFTAi5NRmwyjp+a09EAuaL+8AB00tyjtLqNSgy+5xZVwPxCnUmaNaPm1j0UcXz26b0NQbHt5gYedjG7Q==
"@abp/select2@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-0.8.0.tgz#037600ea3ef68ecd0b9d895a8a715b5e52a0ac85"
integrity sha512-Q2HV4/+zk9NNkZP7i0rbeEunE2usXtAHzTiSOFDHJaKCC3b1pAyo9ZZFwlVE69CVb9j0khXeem8T6zQmO9A88Q==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
select2 "^4.0.5"
"@abp/sweetalert@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-0.6.5.tgz#777389e788f46060102c4b1b398d2b072ba6d16b"
integrity sha512-WXjIMrGWoi6Y9FHTO09OW0aGh7YfkPJDTaFj2nS7shgfTVNHLe4HWUIQDRCVhW0wDtqvJYEs+Ue6qfRXGX7Zjw==
"@abp/sweetalert@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-0.8.0.tgz#d81d553d318c2809c8ff37aa92a4eed6492513e4"
integrity sha512-AcUil0SDrbuOSZyrBEOqa9TB9bqwVyfBGtULjRTxlVfYxyfPYxCajsqDL2W0uu9Ju+vRLZRIzY6IiAE7GFm8Ug==
dependencies:
"@abp/core" "^0.6.5"
"@abp/core" "^0.8.0"
sweetalert "^2.1.0"
"@abp/timeago@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-0.6.5.tgz#6c38b33ab7b921307361e3851fe30f3721f8f055"
integrity sha512-kiUK//0kVgXEYdxaXAkPmhR0cNipMvlUmgpG9S+3hHTSaX6QtJMa+GeCNlIbsRy2f6QrHMWvOfDTS+z7onQmqw==
"@abp/timeago@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-0.8.0.tgz#9035f3476e26b7a70560280c7f87aa99c2a1ff29"
integrity sha512-/DJKXHorakg2Y4Tul+LV4Qihx3rsIAnuA236tttGQCuwynBXMCgNQxSDdvfdxYKlx36N+q1+FNa/d7bJ3bLY8g==
dependencies:
"@abp/jquery" "^0.6.5"
"@abp/jquery" "^0.8.0"
timeago "^1.6.3"
"@abp/toastr@^0.6.5":
version "0.6.5"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-0.6.5.tgz#5ab6e8dbb7927f85e594aa5019db67fb3ad31dc1"
integrity sha512-TECl7yNsrOwEOV6O1aJ3HtXdbVnF5WrZ7hkFyM0AOfIy5vO5He57Wm3IxeRDyuYXai2BBxJa9fYD+REQhByNmw==
"@abp/toastr@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-0.8.0.tgz#474e03e8154270fac7998a375d24469a1447e480"
integrity sha512-UUyhYE30KfeTc33kq0QTYuaUseVtHTYzybUDKTpZkXj9Tvb7hJ3MNVfmNcCzX9HYw/oKNNcvYgJHFUuSFX3bwQ==
dependencies:
"@abp/jquery" "^0.6.5"
"@abp/jquery" "^0.8.0"
toastr "^2.1.4"
abbrev@1:

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj

@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
<ItemGroup>

2
framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj

@ -17,7 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
<ItemGroup>

2
framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

5
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs → framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperTestModule.cs

@ -8,7 +8,10 @@ namespace Volo.Abp.AutoMapper
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<AutoMapperTestModule>();
});
}
}
}

4
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_ConfigurationValidation_Tests.cs

@ -30,8 +30,8 @@ namespace Volo.Abp.AutoMapper
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<ValidatedProfile>(true);
options.AddProfile<NonValidatedProfile>();
options.AddMaps<TestModule>(validate: true); //Adds all profiles in the TestModule assembly by validating configurations
options.ValidateProfile<NonValidatedProfile>(validate: false); //Exclude a profile from the configuration validation
});
}
}

75
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_Inheritance_Tests.cs

@ -1,75 +0,0 @@
using AutoMapper;
using Shouldly;
using Xunit;
namespace Volo.Abp.AutoMapper
{
public class AutoMapper_Inheritance_Tests
{
private readonly IMapper _mapper;
public AutoMapper_Inheritance_Tests()
{
var config = new MapperConfiguration(configuration =>
{
configuration.CreateAutoAttributeMaps(typeof(MyTargetClassToMap));
configuration.CreateAutoAttributeMaps(typeof(EntityDto));
configuration.CreateAutoAttributeMaps(typeof(DerivedEntityDto));
});
_mapper = config.CreateMapper();
}
[Fact]
public void Should_Map_Derived_To_Target()
{
var derived = new MyDerivedClass { Value = "fortytwo" };
var target = _mapper.Map<MyTargetClassToMap>(derived);
target.Value.ShouldBe("fortytwo");
}
public class MyBaseClass
{
public string Value { get; set; }
}
public class MyDerivedClass : MyBaseClass
{
}
[AutoMapFrom(typeof(MyBaseClass))]
public class MyTargetClassToMap
{
public string Value { get; set; }
}
//[Fact] //TODO: That's a problem but related to AutoMapper rather than ABP.
public void Should_Map_EntityProxy_To_EntityDto_And_To_DrivedEntityDto()
{
var proxy = new EntityProxy() { Value = "42"};
var target = _mapper.Map<EntityDto>(proxy);
var target2 = _mapper.Map<DerivedEntityDto>(proxy);
target.Value.ShouldBe("42");
target2.Value.ShouldBe("42");
}
private class Entity
{
public string Value { get; set; }
}
private class DerivedEntity : Entity { }
private class EntityProxy : DerivedEntity { }
[AutoMapFrom(typeof(Entity))]
private class EntityDto
{
public string Value { get; set; }
}
[AutoMapFrom(typeof(DerivedEntity))]
private class DerivedEntityDto : EntityDto { }
}
}

157
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapping_Tests.cs

@ -1,157 +0,0 @@
using System;
using System.Collections.Generic;
using AutoMapper;
using Shouldly;
using Xunit;
namespace Volo.Abp.AutoMapper
{
public class AutoMapping_Tests
{
private readonly IMapper _mapper;
public AutoMapping_Tests()
{
var config = new MapperConfiguration(configuration =>
{
configuration.CreateAutoAttributeMaps(typeof(MyClass1));
configuration.CreateAutoAttributeMaps(typeof(MyClass2));
});
_mapper = config.CreateMapper();
}
[Fact]
public void Map_Null_Tests()
{
MyClass1 obj1 = null;
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.ShouldBe(null);
}
[Fact]
public void Map_Null_Existing_Object_Tests()
{
MyClass1 obj1 = null;
var obj2 = new MyClass2 { TestProp = "before map" };
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe("before map");
}
[Fact]
public void MapTo_Tests()
{
var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.TestProp.ShouldBe("Test value");
var obj3 = _mapper.Map<MyClass3>(obj1);
obj3.TestProp.ShouldBe("Test value");
}
[Fact]
public void MapTo_Existing_Object_Tests()
{
var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = new MyClass2();
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe("Test value");
var obj3 = new MyClass3();
_mapper.Map(obj2, obj3);
obj3.TestProp.ShouldBe("Test value");
Assert.ThrowsAny<Exception>(() => //Did not define reverse mapping!
{
_mapper.Map(obj3, obj2);
});
}
[Fact]
public void MapFrom_Tests()
{
var obj2 = new MyClass2 { TestProp = "Test value" };
var obj1 = _mapper.Map<MyClass1>(obj2);
obj1.TestProp.ShouldBe("Test value");
}
[Fact]
public void IgnoreMap_Tests()
{
var obj2 = new MyClass2 {TestProp = "Test value", AnotherValue = 42};
var obj3 = _mapper.Map<MyClass3>(obj2);
obj3.TestProp.ShouldBe("Test value");
obj3.AnotherValue.ShouldBe(0); //Ignored because of IgnoreMap attribute!
}
[Fact]
public void MapTo_Collection_Tests()
{
var list1 = new List<MyClass1>
{
new MyClass1 {TestProp = "Test value 1"},
new MyClass1 {TestProp = "Test value 2"}
};
var list2 = _mapper.Map<List<MyClass2>>(list1);
list2.Count.ShouldBe(2);
list2[0].TestProp.ShouldBe("Test value 1");
list2[1].TestProp.ShouldBe("Test value 2");
}
[Fact]
public void Map_Should_Set_Null_Existing_Object_Tests()
{
MyClass1 obj1 = new MyClass1 { TestProp = null };
var obj2 = new MyClass2 { TestProp = "before map" };
_mapper.Map(obj1, obj2);
obj2.TestProp.ShouldBe(null);
}
[Fact]
public void Should_Map_Nullable_Value_To_Null_If_It_Is_Null_On_Source()
{
var obj1 = new MyClass1();
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.NullableValue.ShouldBeNull();
}
[Fact]
public void Should_Map_Nullable_Value_To__Not_Null_If_It_Is__Not_Null_On_Source()
{
var obj1 = new MyClass1 { NullableValue = 42 };
var obj2 = _mapper.Map<MyClass2>(obj1);
obj2.NullableValue.ShouldBe(42);
}
[AutoMap(typeof(MyClass2), typeof(MyClass3))]
private class MyClass1
{
public string TestProp { get; set; }
public long? NullableValue { get; set; }
}
[AutoMapTo(typeof(MyClass3))]
private class MyClass2
{
public string TestProp { get; set; }
public long? NullableValue { get; set; }
public int AnotherValue { get; set; }
}
private class MyClass3
{
public string TestProp { get; set; }
[IgnoreMap]
public int AnotherValue { get; set; }
}
}
}

1
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyEntityDto.cs

@ -2,7 +2,6 @@
namespace Volo.Abp.AutoMapper.SampleClasses
{
[AutoMap(typeof(MyEntity))]
public class MyEntityDto
{
public Guid Id { get; set; }

12
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/SampleClasses/MyMapProfile.cs

@ -0,0 +1,12 @@
using AutoMapper;
namespace Volo.Abp.AutoMapper.SampleClasses
{
public class MyMapProfile : Profile
{
public MyMapProfile()
{
CreateMap<MyEntity, MyEntityDto>().ReverseMap();
}
}
}

2
framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj

@ -15,7 +15,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.Core.Tests\Volo.Abp.Core.Tests.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj

@ -16,7 +16,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs\Volo.Abp.BackgroundJobs.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj

@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Castle.Core\Volo.Abp.Castle.Core.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj

@ -9,7 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

2
framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
</ItemGroup>
</Project>

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

Loading…
Cancel
Save