@ -1,506 +0,0 @@ |
|||
# Widgety |
|||
|
|||
ABP poskytuje model a infastrukturu k vytváření **znovu použitelných widgetů**. Systém widgetů je rozšíření pro [ASP.NET Core pohledové komponenty](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Widgety jsou zvláště užitečné, když chcete; |
|||
|
|||
* Mít závislosti na **skriptech & stylech** ve vašem widgetu. |
|||
* Vytvářet **řídící panely** za použítí widgetů. |
|||
* Definovat widgety v znovu použitelných **[modulech](../Module-Development-Basics.md)**. |
|||
* Spolupráci widgetů s **[authorizačními](../Authorization.md)** a **[svazovacími](Bundling-Minification.md)** systémy. |
|||
|
|||
## Základní definice widgetu |
|||
|
|||
### Tvorba pohledové komponenty |
|||
|
|||
Jako první krok, vytvořte běžnou ASP.NET Core pohledovou komponentu: |
|||
|
|||
 |
|||
|
|||
**MySimpleWidgetViewComponent.cs**: |
|||
|
|||
````csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Dědění z `AbpViewComponent` není vyžadováno. Můžete dědit ze standardního ASP.NET Core `ViewComponent`. `AbpViewComponent` pouze definuje pár základních a užitečných vlastnosti. |
|||
|
|||
Můžete vložit službu a pomocí metody `Invoke` z ní získat některá data. Možná budete muset provést metodu Invoke jako asynchronní `public async Task<IViewComponentResult> InvokeAsync()`. Podívejte se na dokument [ASP.NET Core ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) pro všechna další použítí. |
|||
|
|||
**Default.cshtml**: |
|||
|
|||
```xml |
|||
<div class="my-simple-widget"> |
|||
<h2>My Simple Widget</h2> |
|||
<p>This is a simple widget!</p> |
|||
</div> |
|||
``` |
|||
|
|||
### Definice widgetu |
|||
|
|||
Přidejte atribut `Widget` k třídě `MySimpleWidgetViewComponent` pro označení této pohledové komponenty jako widgetu: |
|||
|
|||
````csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## Vykreslení widgetu |
|||
|
|||
Vykreslení widgetu je vcelku standardní. Použijte metodu `Component.InvokeAsync` v razor pohledu/stránce jako s kteroukoliv jinou pohledovou komponentou. Příklady: |
|||
|
|||
````xml |
|||
@await Component.InvokeAsync("MySimpleWidget") |
|||
@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent)) |
|||
```` |
|||
|
|||
První přístup používá název widgetu, zatímco druhý používá typ pohledové komponenty. |
|||
|
|||
### Widgety s argumenty |
|||
|
|||
Systém ASP.NET Core pohledových komponent umožňuje přijímat argumenty pro pohledové komponenty. Níže uvedená pohledová komponenta přijímá `startDate` a `endDate` a používá tyto argumenty k získání dat ze služby. |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget |
|||
{ |
|||
[Widget] |
|||
public class CountersWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
private readonly IDashboardAppService _dashboardAppService; |
|||
|
|||
public CountersWidgetViewComponent(IDashboardAppService dashboardAppService) |
|||
{ |
|||
_dashboardAppService = dashboardAppService; |
|||
} |
|||
|
|||
public async Task<IViewComponentResult> InvokeAsync( |
|||
DateTime startDate, DateTime endDate) |
|||
{ |
|||
var result = await _dashboardAppService.GetCountersWidgetAsync( |
|||
new CountersWidgetInputDto |
|||
{ |
|||
StartDate = startDate, |
|||
EndDate = endDate |
|||
} |
|||
); |
|||
|
|||
return View(result); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Nyní musíte předat anonymní objekt k předání argumentů tak jak je ukázáno níže: |
|||
|
|||
````xml |
|||
@await Component.InvokeAsync("CountersWidget", new |
|||
{ |
|||
startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)), |
|||
endDate = DateTime.Now |
|||
}) |
|||
```` |
|||
|
|||
## Název widgetu |
|||
|
|||
Výchozí název pohledových komponent je vypočítán na základě názvu typu pohledové komponenty. Pokud je typ pohledové komponenty `MySimpleWidgetViewComponent` potom název widgetu bude `MySimpleWidget` (odstraní se `ViewComponent` postfix). Takto ASP.NET Core vypočítává název pohledové komponenty. |
|||
|
|||
Chcete-li přizpůsobit název widgetu, stačí použít standardní atribut `ViewComponent` z ASP.NET Core: |
|||
|
|||
```csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget] |
|||
[ViewComponent(Name = "MyCustomNamedWidget")] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View("~/Pages/Components/MySimpleWidget/Default.cshtml"); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
ABP bude respektovat přizpůsobený název při zpracování widgetu. |
|||
|
|||
> Pokud jsou názvy pohledové komponenty a složky, která pohledovou komponentu obsahuje rozdílné, pravděpodobně budete muset ručně uvést cestu pohledu tak jako je to provedeno v tomto příkladu. |
|||
|
|||
### Zobrazovaný název |
|||
|
|||
Můžete také definovat čitelný & lokalizovatelný zobrazovaný název pro widget. Tento zobrazovaný název může být využít na uživatelském rozhraní kdykoliv je to potřeba. Zobrazovaný název je nepovinný a lze ho definovat pomocí vlastností atributu `Widget`: |
|||
|
|||
````csharp |
|||
using DashboardDemo.Localization; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget( |
|||
DisplayName = "MySimpleWidgetDisplayName", // Lokalizační klíč |
|||
DisplayNameResource = typeof(DashboardDemoResource) // Lokalizační zdroj |
|||
)] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Podívejte se na [dokument lokalizace](../Localization.md) pro více informací o lokalizačních zdrojích a klíčích. |
|||
|
|||
## Závislosti na stylech & skriptech |
|||
|
|||
Problémy když má widget soubory skriptů a stylů; |
|||
|
|||
* Každý stránka, která používá widget musí také přidat soubory **skriptů & stylů** tohoto widgetu. |
|||
* Stránka se také musí postarat o **závislé knihovny/soubory** widgetu. |
|||
|
|||
ABP tyto problémy řeší, když správně propojíme zdroje s widgetem. O závislosti widgetu se při jeho používání nestaráme. |
|||
|
|||
### Definování jednoduchých cest souborů |
|||
|
|||
Níže uvedený příklad widgetu přidá stylové a skriptové soubory: |
|||
|
|||
````csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget( |
|||
StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" }, |
|||
ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" } |
|||
)] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
ABP bere v úvahu tyto závislosti a správně je přidává do pohledu/stránky při použití widgetu. Stylové/skriptové soubory mohou být **fyzické nebo virtuální**. Plně integrováno do [virtuálního systému souborů](../Virtual-File-System.md). |
|||
|
|||
### Definování přispěvatelů balíku |
|||
|
|||
Všechny zdroje použité ve widgetech na stránce jsou přidány jako **svazek** (svázány & minifikovány v produkci pokud nenastavíte jinak). Kromě přidání jednoduchého souboru můžete využít plnou funkčnost přispěvatelů balíčků. |
|||
|
|||
Níže uvedený ukázkový kód provádí totéž co výše uvedený kód, ale definuje a používá přispěvatele balíků: |
|||
|
|||
````csharp |
|||
using System.Collections.Generic; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget( |
|||
StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) }, |
|||
ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) } |
|||
)] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
|
|||
public class MySimpleWidgetStyleBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files |
|||
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css"); |
|||
} |
|||
} |
|||
|
|||
public class MySimpleWidgetScriptBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files |
|||
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
Systém přispěvatelů balíků je velmi schopný. Pokud váš widget používá k vykreslení grafu JavaScript knihovnu, můžete ji deklarovat jako závislost, díky tomu se knihovna pokud nebyla dříve přidána automaticky přidá na stránku Tímto způsobem se stránka využívající váš widget nestará o závislosti. |
|||
|
|||
Podívejte se na dokumentaci [svazování & minifikace](Bundling-Minification.md) pro více informací o tomto systému. |
|||
|
|||
## RefreshUrl |
|||
|
|||
Widget může navrhnout `RefreshUrl`, který se používá vždy, když je potřeba widget aktualizovat. Je-li definován, widget se při každé aktualizaci znovu vykreslí na straně serveru (viz refresh `methoda` u `WidgetManager` níže). |
|||
|
|||
````csharp |
|||
[Widget(RefreshUrl = "Widgets/Counters")] |
|||
public class CountersWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
Jakmile pro svůj widget definujete `RefreshUrl`, musíte poskytnout koncový bod pro jeho vykreslení a vrátit ho: |
|||
|
|||
````csharp |
|||
[Route("Widgets")] |
|||
public class CountersWidgetController : AbpController |
|||
{ |
|||
[HttpGet] |
|||
[Route("Counters")] |
|||
public IActionResult Counters(DateTime startDate, DateTime endDate) |
|||
{ |
|||
return ViewComponent("CountersWidget", new {startDate, endDate}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Trasa `Widgets/Counters` předchozímu `RefreshUrl`. |
|||
|
|||
> Widget lze obnovit dvěma způsoby: Prvním způsobem je použití `RefreshUrl`, kdy se znovu vykreslí na serveru a nahradí HTML vrácené tím ze serveru. Druhým způsobem widget získá data (obvykle JSON objekt) ze serveru a obnoví se sám u klienta (viz refresh metoda v sekci Widget JavaScript API). |
|||
|
|||
## JavaScript API |
|||
|
|||
Možná bude potřeba vykreslit a obnovit widget na straně klienta. V takových případech můžete použít ABP `WidgetManager` a definovat API pro vaše widgety. |
|||
|
|||
### WidgetManager |
|||
|
|||
`WidgetManager` se používá k inicializaci a aktualizaci jednoho nebo více widgetů. Vytvořte nový `WidgetManager` jako je ukázáno níže: |
|||
|
|||
````js |
|||
$(function() { |
|||
var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea'); |
|||
}) |
|||
```` |
|||
|
|||
`MyDashboardWidgetsArea` může obsahovat jeden nebo více widgetů. |
|||
|
|||
> Použíti `WidgetManager` uvnitř document.ready (jako nahoře) je dobrá praktika jelikož jeho funkce používají DOM a potřebují, aby byl DOM připraven. |
|||
|
|||
#### WidgetManager.init() |
|||
|
|||
`init` jednoduše inicializuje `WidgetManager` a volá metody `init` v souvisejících widgetech pokud je obsahují (podívejte se na sekci Widget JavaScript API section níže) |
|||
|
|||
```js |
|||
myWidgetManager.init(); |
|||
``` |
|||
|
|||
#### WidgetManager.refresh() |
|||
|
|||
`refresh` metoda obnoví všechny widgety související s tímto `WidgetManager`: |
|||
|
|||
```` |
|||
myWidgetManager.refresh(); |
|||
```` |
|||
|
|||
#### WidgetManager možnosti |
|||
|
|||
WidgetManager má několik dalších možností. |
|||
|
|||
##### Filtrační formulář |
|||
|
|||
Pokud vaše widgety vyžadují parametry/filtry pak budete obvykle mít formulář pro filtrování widgetů. V takových případech můžete vytvořit formulář, který obsahuje prvky formuláře a oblast řídicího panelu s nějakými widgety uvnitř. Příklad: |
|||
|
|||
````xml |
|||
<form method="get" id="MyDashboardFilterForm"> |
|||
...prvky formuláře |
|||
</form> |
|||
|
|||
<div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm"> |
|||
...widgety |
|||
</div> |
|||
```` |
|||
|
|||
`data-widget-filter` atribut propojuje formulář s widgety. Kdykoli je formulář odeslán, všechny widgety jsou automaticky aktualizovány pomocí polí formuláře jako filtru. |
|||
|
|||
Místo atributu `data-widget-filter`, můžete použít parametr `filterForm` v konstruktoru `WidgetManager`. Příklad: |
|||
|
|||
````js |
|||
var myWidgetManager = new abp.WidgetManager({ |
|||
wrapper: '#MyDashboardWidgetsArea', |
|||
filterForm: '#MyDashboardFilterForm' |
|||
}); |
|||
```` |
|||
|
|||
##### Zpětné volání filtru |
|||
|
|||
Možná budete chtít mít lepší kontrolu nad poskytováním filtrů při inicializaci a aktualizaci widgetů. V tomto případě můžete použít volbu `filterCallback`: |
|||
|
|||
````js |
|||
var myWidgetManager = new abp.WidgetManager({ |
|||
wrapper: '#MyDashboardWidgetsArea', |
|||
filterCallback: function() { |
|||
return $('#MyDashboardFilterForm').serializeFormToObject(); |
|||
} |
|||
}); |
|||
```` |
|||
|
|||
Tento příklad ukazuje výchozí implementaci `filterCallback`. Pomocí polí můžete vrátit jakýkoli JavaScript objekt. Příklad: |
|||
|
|||
````js |
|||
filterCallback: function() { |
|||
return { |
|||
'startDate': $('#StartDateInput').val(), |
|||
'endDate': $('#EndDateInput').val() |
|||
}; |
|||
} |
|||
```` |
|||
|
|||
Vrácené filtry jsou předávány všem widgetům na `init` a` refresh`. |
|||
|
|||
### Widget JavaScript API |
|||
|
|||
Widget může definovat rozhraní API jazyka JavaScript, které je v případě potřeby vyvoláno přes `WidgetManager`. Ukázku kódu níže lze použít k definování API pro widget. |
|||
|
|||
````js |
|||
(function () { |
|||
abp.widgets.NewUserStatisticWidget = function ($wrapper) { |
|||
|
|||
var getFilters = function () { |
|||
return { |
|||
... |
|||
}; |
|||
} |
|||
|
|||
var refresh = function (filters) { |
|||
... |
|||
}; |
|||
|
|||
var init = function (filters) { |
|||
... |
|||
}; |
|||
|
|||
return { |
|||
getFilters: getFilters, |
|||
init: init, |
|||
refresh: refresh |
|||
}; |
|||
}; |
|||
})(); |
|||
```` |
|||
|
|||
`NewUserStatisticWidget` je tady název widgetu. Měl by odpovídat názvu widgetu definovanému na straně serveru. Všechny funkce jsou volitelné. |
|||
|
|||
#### getFilters |
|||
|
|||
Pokud má widget vlastní interní filtry, měla by tato funkce vrátit objekt filtru. Příklad: |
|||
|
|||
````js |
|||
var getFilters = function() { |
|||
return { |
|||
frequency: $wrapper.find('.frequency-filter option:selected').val() |
|||
}; |
|||
} |
|||
```` |
|||
|
|||
Tuto metodu používá `WidgetManager` při vytváření filtrů. |
|||
|
|||
#### init |
|||
|
|||
Slouží k inicializaci widgetu kdykoli je potřeba. Má argument filtru, který lze použít při získávání dat ze serveru. Metoda `init` je použita když je volána funkce `WidgetManager.init()`. Použita je i v případě že váš widget vyžaduje úplné obnovení při aktualizaci. Viz `RefreshUrl` v možnostech widgetu. |
|||
|
|||
#### refresh |
|||
|
|||
Slouží k aktualizaci widgetu kdykoli je potřeba. Má argument filtru, který lze použít při získávání dat ze serveru. Metoda `refresh` se používá kdykoliv je volána funkce `WidgetManager.refresh()`. |
|||
|
|||
## Autorizace |
|||
|
|||
Některé widgety budou pravděpodobně muset být dostupné pouze pro ověřené nebo autorizované uživatele. V tomto případě použijte následující vlastnosti atributu `Widget`: |
|||
|
|||
* `RequiresAuthentication` (`bool`): Nastavte na true, aby byl tento widget použitelný pouze pro ověřené uživatele (uživatel je přihlášen do aplikace). |
|||
* `RequiredPolicies` (`List<string>`): Seznam názvů zásad k autorizaci uživatele. Další informace o zásadách naleznete v [dokumentu autorizace](../Authorization.md). |
|||
|
|||
Příklad: |
|||
|
|||
````csharp |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
|||
|
|||
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget |
|||
{ |
|||
[Widget(RequiredPolicies = new[] { "MyPolicyName" })] |
|||
public class MySimpleWidgetViewComponent : AbpViewComponent |
|||
{ |
|||
public IViewComponentResult Invoke() |
|||
{ |
|||
return View(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## WidgetOptions |
|||
|
|||
Jako alternativu k atributu `Widget` můžete ke konfiguraci widgetů použít `AbpWidgetOptions`: |
|||
|
|||
```csharp |
|||
Configure<AbpWidgetOptions>(options => |
|||
{ |
|||
options.Widgets.Add<MySimpleWidgetViewComponent>(); |
|||
}); |
|||
``` |
|||
|
|||
Toto vepište do metody `ConfigureServices` vašeho [modulu](../Module-Development-Basics.md). Veškerá konfigurace udělaná přes atribut `Widget` je dostupná i za pomoci `AbpWidgetOptions`. Příklad konfigurace, která přidává styl pro widget: |
|||
|
|||
````csharp |
|||
Configure<AbpWidgetOptions>(options => |
|||
{ |
|||
options.Widgets |
|||
.Add<MySimpleWidgetViewComponent>() |
|||
.WithStyles("/Pages/Components/MySimpleWidget/Default.css"); |
|||
}); |
|||
```` |
|||
|
|||
> Tip: `AbpWidgetOptions` lze také použít k získání existujícího widgetu a ke změně jeho konfigurace. To je obzvláště užitečné, pokud chcete změnit konfiguraci widgetu uvnitř modulu používaného vaší aplikací. Použíjte `options.Widgets.Find` k získání existujícího `WidgetDefinition`. |
|||
|
|||
## Podívejte se také na |
|||
|
|||
* [Příklad projektu (zdrojový kód)](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo). |
|||
|
|||
@ -1,84 +0,0 @@ |
|||
# Autofac integrace |
|||
|
|||
Autofac je jedním z nejpoužívanějších frameworků pro .Net pro vkládání závislostí (DI). Poskytuje pokročilejší funkce v porovnáním se standardní .Net Core DI knihovnou, jako dynamickou proxy a injekci vlastností. |
|||
|
|||
## Instalace Autofac integrace |
|||
|
|||
> Všechny startovací šablony a vzorky jsou s Autofac již integrovány. Takže většinou nemusíte tento balíček instalovat ručně. |
|||
|
|||
Nainstalujte do vašeho projektu balíček [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) (pro víceprojektovou aplikaci se doporučuje přidat do spustitelného/webového projektu.) |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Autofac |
|||
```` |
|||
|
|||
Poté přídejte k vašemu modulu závislost na `AbpAutofacModule`: |
|||
|
|||
```csharp |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Autofac; |
|||
|
|||
namespace MyCompany.MyProject |
|||
{ |
|||
[DependsOn(typeof(AbpAutofacModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Nakonec nastavte `AbpApplicationCreationOptions` aby nahradil výchozí služby pro vkládání závislostí na Autofac. Záleží na typu aplikace. |
|||
|
|||
### ASP.NET Core aplikace |
|||
|
|||
Volejte `UseAutofac()` v souboru **Startup.cs** jako je ukázáno níže: |
|||
|
|||
````csharp |
|||
public class Startup |
|||
{ |
|||
public IServiceProvider ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<MyWebModule>(options => |
|||
{ |
|||
//Integrace Autofac! |
|||
options.UseAutofac(); |
|||
}); |
|||
|
|||
return services.BuildServiceProviderFromFactory(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### Konzolová aplikace |
|||
|
|||
Volejte metodu `UseAutofac()` v možnostech `AbpApplicationFactory.Create` jako je ukázáno níže: |
|||
|
|||
````csharp |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
using (var application = AbpApplicationFactory.Create<AppModule>(options => |
|||
{ |
|||
options.UseAutofac(); //Autofac integrace |
|||
})) |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
@ -1,167 +0,0 @@ |
|||
# ABP CLI |
|||
|
|||
ABP CLI (Command Line Interface) je nástroj v příkazovém řádku k provádění některých běžných úkonů v řešeních založených na ABP. |
|||
|
|||
## Instalace |
|||
|
|||
ABP CLI je [dotnet global tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools). Nainstalujete jej pomocí okna příkazového řádku: |
|||
|
|||
````bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
Aktualizace stávající instalace: |
|||
|
|||
````bash |
|||
dotnet tool update -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
## Příkazy |
|||
|
|||
### new |
|||
|
|||
Vygeneruje nové řešení založené na ABP [startovací šabloně](Startup-Templates/Index.md). |
|||
|
|||
Základní použití: |
|||
|
|||
````bash |
|||
abp new <název-řešení> [možnosti] |
|||
```` |
|||
|
|||
Příklad: |
|||
|
|||
````bash |
|||
abp new Acme.BookStore |
|||
```` |
|||
|
|||
* `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 `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-auth-server`: Oddělí Auth Server aplikaci od API host aplikace. Pokud není uvedeno, bude na straně serveru jediný koncový bod. |
|||
* `none`: Bez UI. Pro tuto šablonu jsou dostupné dodatečné možnosti: |
|||
* `--separate-auth-server`: Oddělí Auth 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. |
|||
* `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á ABP balíček 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. |
|||
|
|||
Základní použití: |
|||
|
|||
````bash |
|||
abp add-package <název-balíčku> [možnosti] |
|||
```` |
|||
|
|||
Příklad: |
|||
|
|||
```` |
|||
abp add-package Volo.Abp.MongoDB |
|||
```` |
|||
|
|||
* Tento příklad přidá do projektu balíček Volo.Abp.MongoDB. |
|||
|
|||
#### Možnosti |
|||
|
|||
* `--project` nebo `-p`: Určuje cestu k projektu (.csproj). Pokud není zadáno, CLI se pokusí najít soubor .csproj v aktuálním adresáři. |
|||
|
|||
### add-module |
|||
|
|||
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. |
|||
|
|||
Základní použití: |
|||
|
|||
````bash |
|||
abp add-module <název-modulu> [možnosti] |
|||
```` |
|||
|
|||
Příklad: |
|||
|
|||
```bash |
|||
abp add-module Volo.Blogging |
|||
``` |
|||
|
|||
* Tento příklad přidá do projektu modul Volo.Blogging. |
|||
|
|||
#### Možnosti |
|||
|
|||
* `--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ář. |
|||
* `--with-source-code`: Místo balíčků NuGet/NPM přidejte zdrojový kód modulu. |
|||
|
|||
### update |
|||
|
|||
Aktualizace všech balíčků souvisejících s ABP může být únavná, protože existuje mnoho balíčků frameworku a modulů. Tento příkaz automaticky aktualizuje na poslední verze všechny související ABP NuGet a NPM balíčky v řešení nebo projektu. |
|||
|
|||
Použití: |
|||
|
|||
````bash |
|||
abp update [možnosti] |
|||
```` |
|||
|
|||
* Pokud spouštíte v adresáři se souborem .sln, aktualizuje všechny balíčky všech projektů v řešení souvisejících s ABP na nejnovější verze. |
|||
* Pokud spouštíte v adresáři se souborem .csproj, aktualizuje všechny balíčky v projektu na nejnovější verze. |
|||
|
|||
#### 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. |
|||
|
|||
### login |
|||
|
|||
Některé funkce CLI vyžadují přihlášení k platformě abp.io. Chcete-li se přihlásit pomocí svého uživatelského jména, napište |
|||
|
|||
```bash |
|||
abp login <username> |
|||
``` |
|||
|
|||
Všimněte si, že nové přihlášení s již aktivní relací ukončí předchozí relaci a vytvoří novou. |
|||
|
|||
### logout |
|||
|
|||
Odhlásí vás odebráním tokenu relace z počítače. |
|||
|
|||
``` |
|||
abp logout |
|||
``` |
|||
|
|||
### help |
|||
|
|||
Vypíše základní informace k používání CLI. |
|||
|
|||
Použítí: |
|||
|
|||
````bash |
|||
abp help [název-příkazu] |
|||
```` |
|||
|
|||
Příklady: |
|||
|
|||
````bash |
|||
abp help # Zobrazí obecnou nápovědu. |
|||
abp help new # Zobrazí nápovědu k příkazu "new". |
|||
```` |
|||
|
|||
@ -1,64 +0,0 @@ |
|||
## Průvodce pro přispěvatele |
|||
|
|||
ABP je [open source](https://github.com/abpframework) a komunitně řízený projekt. Tento průvodce má za cíl pomoci každému kdo chce do projektu nějak přispět. |
|||
|
|||
### Příspěvek kódu |
|||
|
|||
Vždy můžete zaslat pull request do Github repositáře. |
|||
|
|||
- Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu. |
|||
- Učiňte potřebné změny. |
|||
- Zašlete pull request. |
|||
|
|||
Než budete dělat nějaké změny, diskutujte o nich prosím na [Github problémy](https://github.com/abpframework/abp/issues). Díky tomu nebude žádný jiný vývojář pracovat na stejném problému a Váš PR má lepší šanci na to být přijat. |
|||
|
|||
#### Opravy chyb a vylepšení |
|||
|
|||
Pokud chcete opravit známou chybu nebo pracovat na plánovaném vylepšení podívejte se na [seznam problémů](https://github.com/abpframework/abp/issues) na Githubu. |
|||
|
|||
#### Požadavky na funkce |
|||
|
|||
Pokud máte nápad na funkci pro framework nebo modul [vytvořte problém](https://github.com/abpframework/abp/issues/new) na Githubu nebo se připojte ke stávající diskuzi. V případě přijetí komunitou ho pak můžete implementovat. |
|||
|
|||
### Překlad dokumentů |
|||
|
|||
Pokud chcete přeložit celou [dokumentaci](https://abp.io/documents/) (včetně této stránky) do Vašeho rodného jazyka, následujte tyto kroky: |
|||
|
|||
* Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu. |
|||
* K přidání nového jazyka vytvořte novou složku v [docs](https://github.com/abpframework/abp/tree/master/docs). Název složky musí být "en", "es", "fr", "tr" atd. v závislosti na jazyku (navštivte [všechny jazykové kódy](https://msdn.microsoft.com/en-us/library/hh441729.aspx)). |
|||
* Pro referenci použijte ["en" složku](https://github.com/abpframework/abp/tree/master/docs/en) a její názvy souborů a strom složek. Při překladu této dokumentace zachovejte prosím tyto názvy stejné. |
|||
* Zašlete pull request (PR) po překladu jakéhokoliv dokumentu klidně i po jednom. Nečekejte až budete mít překlad všech dokumentů. |
|||
|
|||
Existuje několik základních dokumentů, které je třeba přeložit než bude jazyk uveřejněn na [stránkách ABP dokumentace](https://docs.abp.io) |
|||
|
|||
* Začínáme dokumenty |
|||
* Tutoriály |
|||
* CLI |
|||
|
|||
Nový jazyk je publikován jakmile jsou minimálně tyto překlady dokončeny. |
|||
|
|||
### Lokalizace zdrojů |
|||
|
|||
ABP framework má flexibilní [lokalizační systém](../Localization.md). Můžete tak vytvořit lokalizované uživatelské prostředí pro svou vlastní aplikaci. |
|||
|
|||
K tomu mají framework a vestavěné moduly již lokalizované texty. Například [lokalizační texty pro Volo.Abp.UI balík](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). |
|||
|
|||
Můžete vytvořit nový soubor ve [stejné složce](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) k přidání překladu. |
|||
|
|||
* Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu. |
|||
* Vytvořte nový soubor pro cílový jazyk pro lokalizační text v (json) souboru (u souboru en.json). |
|||
* Zkopírujte veškerý text ze souboru en.json. |
|||
* Přeložte texty. |
|||
* Zašlete pull request na Githubu. |
|||
|
|||
K překladu lokalizovaných textů můžete také použít příkaz `abp translate` of [ABP CLI](CLI.md). |
|||
|
|||
ABP je modulářní framework, proto je zde mnoho zdrojů lokalizačních textů, jeden pro každý modul. K najití všech .json souborů, vyhledejte po naklonování repozitáře soubory "en.json". Můžete se taky podívat na [tento seznam](Localization-Text-Files.md) souborů lokalizačních textů. |
|||
|
|||
### Příspevky do blogu a návody |
|||
|
|||
Pokud se rozhodnete pro ABP vytvořit nějaké návody nebo příspěvky do blogu, dejte nám vědět (prostřednictvím [Github problémy](https://github.com/abpframework/abp/issues)), ať můžeme přidat odkaz na Váš návod/příspěvek v oficiální dokumentaci a oznámit na našem [Twitter účtu](https://twitter.com/abpframework). |
|||
|
|||
### Zpráva o chybě |
|||
|
|||
Pokud najdete chybu, [vytvořte prosím problém v Github repozitáři](https://github.com/abpframework/abp/issues/new). |
|||
@ -1,61 +0,0 @@ |
|||
# Dapper integrace |
|||
|
|||
Jelikož myšlenka Dapper je taková, že sql příkaz má přednost, tak hlavně poskytuje metody rozšíření pro `IDbConnection` rozhraní. |
|||
|
|||
Abp nezapouzdřuje přílíš mnoho funkcí pro Dapper. Abp Dapper poskytuje základní třídu `DapperRepository<TDbContext>` založenou na Abp EntityFrameworkCore, který poskytuje vlastnosti `IDbConnection` a `IDbTransaction` vyžadované v Dapper. |
|||
|
|||
Tyto dvě vlastnosti fungují dobře s [jednotkou práce](Unit-Of-Work.md). |
|||
|
|||
## Instalace |
|||
|
|||
Nainstalujte a nakonfigurujte EF Core podle [EF Core integrační dokumentace](Entity-Framework-Core.md). |
|||
|
|||
`Volo.Abp.Dapper` je hlavní NuGet balík pro Dapper integraci. Nainstalujte jej proto do vašeho projektu (pro strukturovanou aplikaci do datové/infrastrukturní vrstvy): |
|||
|
|||
```shell |
|||
Install-Package Volo.Abp.Dapper |
|||
``` |
|||
|
|||
Poté přidejte závislost na `AbpDapperModule` modulu (atribut `DependsOn`) do Vašeho [modulu](Module-Development-Basics.md): |
|||
|
|||
````C# |
|||
using Volo.Abp.Dapper; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace MyCompany.MyProject |
|||
{ |
|||
[DependsOn(typeof(AbpDapperModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## Implementace Dapper repozitáře |
|||
|
|||
Následující kód implementuje repozitář `Person`, který vyžaduje `DbContext` z EF Core (MyAppDbContext). Můžete vložit `PersonDapperRepository` k volání jeho metod. |
|||
|
|||
`DbConnection` a `DbTransaction` jsou ze základní třídy `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); |
|||
} |
|||
} |
|||
``` |
|||
@ -1,33 +0,0 @@ |
|||
# Domain Driven Design |
|||
|
|||
## Co je DDD? |
|||
|
|||
ABP framework poskytuje **infrastrukturu**, která zjednodušuje implementaci vývoje založeného na **DDD**. DDD je [definován ve Wikipedii](https://en.wikipedia.org/wiki/Domain-driven_design) takto: |
|||
|
|||
> **Domain-driven design** (**DDD**) je přístup k vývoji softwaru pro komplexní potřeby propojením implementace s vyvíjejícím se modelem. Předpoklad DDD je následující: |
|||
> |
|||
> - Primární zaměření projektu je na jádře domény a doménové logice; |
|||
> - Zakládání komplexních návrhů na modelu domény; |
|||
> - Iniciování tvůrčí spolupráce mezi technickými a doménovými odborníky s cílem iterativně zdokonalit koncepční model, který řeší konkrétní problémy v doméně. |
|||
|
|||
### Vrstvy |
|||
|
|||
ABP dodržuje principy a vzorce DDD pro dosažení vrstveného aplikačního modelu, který se skládá ze čtyř základních vrstev: |
|||
|
|||
- **Prezentační vrstva**: Poskytuje uživateli rozhraní. Používá *Aplikační vrstvu* k dosažení uživatelských interakcí. |
|||
- **Aplikační vrstva**: Prostředník mezi prezentační a doménovou vrstvou. Instrumentuje business objekty k provádění specifických úloh aplikace. Implementuje případy použití jako logiku aplikace. |
|||
- **Doménová vrstva**: Zahrnuje business objekty a jejich business pravidla. Je jádrem aplikace. |
|||
- **Vrstva infrastruktury**: Poskytuje obecné technické možnosti, které podporují vyšší vrstvy většinou pomocí knihoven třetích stran. |
|||
|
|||
## Obsah |
|||
|
|||
* **Doménová vrstva** |
|||
* [Entity & agregované kořeny](Entities.md) |
|||
* Hodnotové objekty |
|||
* [Repozitáře](Repositories.md) |
|||
* Doménové služby |
|||
* Specifikace |
|||
* **Aplikační vrstva** |
|||
* [Aplikační služby](Application-Services.md) |
|||
* [Objekty přenosu dat (DTOs)](Data-Transfer-Objects.md) |
|||
* Jednotka práce |
|||
@ -1,39 +0,0 @@ |
|||
# Přepnutí na EF Core PostgreSQL providera |
|||
|
|||
Tento dokument vysvětluje, jak přepnout na poskytovatele databáze **PostgreSQL** pro **[spouštěcí šablonu aplikace](Startup-Templates/Application.md)**, která je dodávána s předem nakonfigurovaným SQL poskytovatelem. |
|||
|
|||
## Výměna balíku Volo.Abp.EntityFrameworkCore.SqlServer |
|||
|
|||
Projekt `.EntityFrameworkCore` v řešení závisí na NuGet balíku [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer). Odstraňte tento balík a přidejte stejnou verzi balíku [Volo.Abp.EntityFrameworkCore.PostgreSql](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql). |
|||
|
|||
## Nahrazení závislosti modulu |
|||
|
|||
Najděte třídu ***YourProjectName*EntityFrameworkCoreModule** v projektu `.EntityFrameworkCore`, odstraňte `typeof(AbpEntityFrameworkCoreSqlServerModule)` z atributu `DependsOn`, přidejte `typeof(AbpEntityFrameworkCorePostgreSqlModule)` (také nahraďte `using Volo.Abp.EntityFrameworkCore.SqlServer;` za `using Volo.Abp.EntityFrameworkCore.PostgreSql;`). |
|||
|
|||
## UseNpgsql() |
|||
|
|||
Najděte volání `UseSqlServer()` v *YourProjectName*EntityFrameworkCoreModule.cs uvnitř projektu `.EntityFrameworkCore` a nahraďte za `UseNpgsql()`. |
|||
|
|||
Najděte volání `UseSqlServer()` v *YourProjectName*MigrationsDbContextFactory.cs uvnitř projektu `.EntityFrameworkCore.DbMigrations` a nahraďte za `UseNpgsql()`. |
|||
|
|||
> V závislosti na struktuře řešení můžete najít více volání `UseSqlServer()`, které je třeba změnit. |
|||
|
|||
## Změna connection stringů |
|||
|
|||
PostgreSql connection stringy se od těch pro SQL Server liší. Je proto potřeba zkontrolovat všechny soubory `appsettings.json` v řešení a connection stringy v nich nahradit. Podívejte se na [connectionstrings.com](https://www.connectionstrings.com/postgresql/) pro více detailů o možnostech PostgreSql connection stringů. |
|||
|
|||
Typicky je potřeba změnit `appsettings.json` v projektech `.DbMigrator` a `.Web` projects, ale to záleží na vaší struktuře řešení. |
|||
|
|||
## Regenerace migrací |
|||
|
|||
Startovací šablona používá [Entity Framework Core Code First migrace](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core migrace závisí na zvoleném DBMS poskytovateli. Tudíž změna DBMS poskytovatele způsobí selhání migrace. |
|||
* Smažte složku Migrations v projektu `.EntityFrameworkCore.DbMigrations` and znovu sestavte řešení. |
|||
* Spusťte `Add-Migration "Initial"` v Package Manager Console (je nutné zvolit `.DbMigrator` (nebo `.Web`) projekt jako startovací projekt v Solution Explorer a zvolit projekt `.EntityFrameworkCore.DbMigrations` jako výchozí v Package Manager Console). |
|||
|
|||
Tímto vytvoříte migraci databáze se všemi nakonfigurovanými databázovými objekty (tabulkami). |
|||
|
|||
Spusťte projekt `.DbMigrator` k vytvoření databáze a vložení počátečních dat. |
|||
|
|||
## Spuštění aplikace |
|||
|
|||
Vše je připraveno. Stačí už jen spustit aplikaci a užívat si kódování. |
|||
@ -1,126 +0,0 @@ |
|||
## Začínáme s Angular aplikační šablonou |
|||
|
|||
Tento tutoriál vysvětluje, jak vytvořit novou Angular aplikaci pomocí spouštěcí šablony, jak ji nakonfigurovat a spustit. |
|||
|
|||
### Tvorba nového projektu |
|||
|
|||
Tento tutorial používá k vytvoření nového projektu **ABP CLI**. Podívejte se na stránku [začínáme](https://abp.io/get-started) pro více možností. |
|||
|
|||
Pokud jste tak dosud neučinili, nainstalujte ABP CLI pomocí okna příkazového řádku: |
|||
|
|||
````bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
Použíjte příkaz `abp new` v prázdné složce k vytvoření Vašeho projektu: |
|||
|
|||
````bash |
|||
abp new Acme.BookStore -u angular |
|||
```` |
|||
|
|||
> Můžete použít různé úrovně jmenných prostorů; např. BookStore, Acme.BookStore nebo Acme.Retail.BookStore. |
|||
|
|||
`-u angular` volba specifikuje Angular jako UI framework. Výchozí poskytovatel databáze je EF Core. Podívejte se na [CLI dokumentaci](CLI.md) pro všechny dostupné možnosti. |
|||
|
|||
#### Předběžné požadavky |
|||
|
|||
Vytvořené řešení vyžaduje; |
|||
|
|||
* [Visual Studio 2019 (v16.4.0+)](https://visualstudio.microsoft.com/vs/) |
|||
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/) |
|||
* [Node v12+](https://nodejs.org) |
|||
* [Yarn v1.19+](https://classic.yarnpkg.com/) |
|||
|
|||
### Struktura řešení |
|||
|
|||
Otevřete řešení ve **Visual Studio**: |
|||
|
|||
 |
|||
|
|||
Řešení má vrstvenou strukturu (založenou na [domain driven designu](Domain-Driven-Design.md)) a obsahuje projekty testů jednotek a integrace správně nakonfigurované pro práci s **EF Core** & **SQLite in-memory** databází. |
|||
|
|||
> Podívejte se na [dokument šablony aplikace](Startup-Templates/Application.md) k detailnímu pochopení struktury řešení. |
|||
|
|||
### Databázový connection string |
|||
|
|||
Zkontrolujte **connection string** v souboru `appsettings.json` u projektu `.HttpApi.Host`: |
|||
|
|||
````json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Řešení je nakonfigurováno pro použití **Entity Framework Core** s **MS SQL Server**. EF Core podporuje [různé](https://docs.microsoft.com/en-us/ef/core/providers/) poskytovatele databáze, takže pokud chcete můžete použít jiný DBMS. V případě potřeby změňte connection string. |
|||
|
|||
### Tvorba databáze & aplikace migrací databáze |
|||
|
|||
K vytvoření databáze máte dvě možnosti. |
|||
|
|||
#### Použití aplikace DbMigrator |
|||
|
|||
Řešení obsahuje konzolovou aplikaci (v tomto příkladu nazvanou `Acme.BookStore.DbMigrator`), která dokáže vytvořit databázi, aplikovat migrace a vložit počáteční data. Ta je užitečná jak pro vývojové tak pro produkční prostředí. |
|||
|
|||
> `.DbMigrator` má vlastní `appsettings.json`. Pokud jste změnili connection string výše, měli byste změnit i tento. |
|||
|
|||
Klikněte pravým na projekt `.DbMigrator` zvolte **Set as StartUp Project**: |
|||
|
|||
 |
|||
|
|||
Zmáčkněte F5 (nebo Ctrl+F5) ke spuštění aplikace. Výstup by měl být podobný vyobrazení níže: |
|||
|
|||
 |
|||
|
|||
#### Použití příkazu EF Core Update-Database |
|||
|
|||
Ef Core máš příkaz `Update-Database`, který v případě potřeby vytvoří databázi a aplikuje čekající migrace. Klikněte pravým na projekt `.HttpApi.Host` a zvolte **Set as StartUp Project**: |
|||
|
|||
 |
|||
|
|||
Otevřete **Package Manager Console**, zvolte `.EntityFrameworkCore.DbMigrations` jako **Default Project** a proveďte příkaz `Update-Database`: |
|||
|
|||
 |
|||
|
|||
Tímto vytvoříte novou databáze podle nakonfigurovaného connection string. |
|||
|
|||
> Je doporučeno užití nástroje `.DbMigrator`, protože zároveň vloží i počáteční data ke správnému běhu webové aplikace. |
|||
|
|||
### Spuštění aplikace |
|||
|
|||
#### Spuštění API Host (na straně serveru) |
|||
|
|||
Ujistěte se že je projekt `.HttpApi.Host` nastaven jako startovací a spusťte aplikaci což otevře Swagger UI: |
|||
|
|||
 |
|||
|
|||
Tady můžete vidět API aplikace a zároveň je i otestovat. Získejte [více informací](https://swagger.io/tools/swagger-ui/) o Swagger UI. |
|||
|
|||
##### Autorizace pro Swagger UI |
|||
|
|||
Vetšina API aplikace vyžaduje autentizaci & autorizaci. Pokud chcete otestovat autorizované API, manuálně přejděte na stránku `/Account/Login`, vložte `admin` jako uživatelské jméno a `1q2w3E*` jako heslo k příhlášení do aplikace. Poté budete moci provádět autorizované požadavky API. |
|||
|
|||
#### Spuštění Angular aplikace (na straně klienta) |
|||
|
|||
Přejděte do složky `angular`, otevřete terminál příkazového řádku, proveďte příkaz `yarn` (doporučujeme používat správce balíků [yarn](https://yarnpkg.com), npm install bude v mnoha případech také fungovat): |
|||
|
|||
````bash |
|||
yarn |
|||
```` |
|||
|
|||
Jakmile jsou načteny všechny node moduly, proveďte příkaz `yarn start` nebo `npm start`: |
|||
|
|||
````bash |
|||
yarn start |
|||
```` |
|||
|
|||
Otevřete Váš oblíbený prohlížeč a přejděte na adresu `localhost:4200`. Počáteční uživatelské jméno je `admin` a heslo `1q2w3E*`. |
|||
|
|||
Startovací šablona obsahuje moduly **správa identit** a **správa tenantů**. Jakmile se přihlásíte, zprístupní se administrační menu kde můžete spravovat **tenanty**, **role**, **uživatele** a jejich **oprávnění**. |
|||
|
|||
> Doporučujeme [Visual Studio Code](https://code.visualstudio.com/) jako editor pro Angular projekt, ale klidně použijte Váš oblíbený editor. |
|||
|
|||
### Co dále? |
|||
|
|||
* [Tutoriál vývoje aplikace](Tutorials/Angular/Part-I.md) |
|||
@ -1,157 +0,0 @@ |
|||
# Začínáme s ASP.NET Core MVC aplikací |
|||
|
|||
Tento tutoriál vysvětluje jak začít s ABP z ničeho s minimem závislostí. Obvykle chcete začít se **[startovací šablonou](https://abp.io/Templates)**. |
|||
|
|||
## Tvorba nového projektu |
|||
|
|||
1. Vytvořte novou AspNet Core Web aplikaci ve Visual Studio 2019 (16.4.0+): |
|||
|
|||
 |
|||
|
|||
2. Nakonfigurujte váš nový projekt: |
|||
|
|||
 |
|||
|
|||
3. Potvrďte kliknutím na tlačítko vytvořit |
|||
|
|||
 |
|||
|
|||
## Instalace Volo.Abp.AspNetCore.Mvc balíku |
|||
|
|||
Volo.Abp.AspNetCore.Mvc je AspNet Core MVC integrační balík pro ABP. Takže ho nainstalujeme do projektu: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.AspNetCore.Mvc |
|||
```` |
|||
|
|||
## Tvorba prvního ABP modulu |
|||
|
|||
ABP je modulární framework a proto vyžaduje **spouštěcí (kořenový) modul** což je třída dědící z ``AbpModule``: |
|||
|
|||
````C# |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace BasicAspNetCoreApplication |
|||
{ |
|||
[DependsOn(typeof(AbpAspNetCoreMvcModule))] |
|||
public class AppModule : AbpModule |
|||
{ |
|||
public override void OnApplicationInitialization( |
|||
ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
var env = context.GetEnvironment(); |
|||
|
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
else |
|||
{ |
|||
app.UseExceptionHandler("/Error"); |
|||
} |
|||
|
|||
app.UseStaticFiles(); |
|||
app.UseRouting(); |
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
``AppModule`` je dobrý název pro spouštěcí modul aplikace. |
|||
|
|||
ABP balíky definují modulové třídy a modul může mít závislost na jiném. V kódu výše, ``AppModule`` má závislost na ``AbpAspNetCoreMvcModule`` (definován v balíku [Volo.Abp.AspNetCore.Mvc](https://www.nuget.org/packages/Volo.Abp.AspNetCore.Mvc)). Je běžné přidat ``DependsOn`` atribute po instalaci nového ABP NuGet balíku. |
|||
|
|||
Místo třídy Startup, konfigurujeme ASP.NET Core pipeline v této modulové třídě. |
|||
|
|||
## Třída Startup |
|||
|
|||
V dalším kroku upravíme Startup třídu k integraci ABP modulového systému: |
|||
|
|||
````C# |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace BasicAspNetCoreApplication |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AppModule>(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
``services.AddApplication<AppModule>()`` přidává všechny služby definované ve všech modulech počínaje od ``AppModule``. |
|||
|
|||
``app.InitializeApplication()`` v metodě ``Configure`` inicializuje a spustí aplikaci. |
|||
|
|||
## Spusťte aplikaci! |
|||
|
|||
To je vše! Spusťte aplikaci, bude fungovat podle očekávání. |
|||
|
|||
## Použití Autofac jako frameworku pro vkládání závislostí |
|||
|
|||
Ačkoliv je AspNet Core systém pro vkládání závíslostí (DI) dostatečný pro základní požadavky, [Autofac](https://autofac.org/) poskytuje pokročilé funkce jako injekce vlastností nebo záchyt metod, které jsou v ABP užity k provádění pokročilých funkcí frameworku. |
|||
|
|||
Nahrazení AspNet Core DI systému za Autofac a integrace s ABP je snadná. |
|||
|
|||
1. Nainstalujeme [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) balík |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Autofac |
|||
```` |
|||
|
|||
2. Přidáme ``AbpAutofacModule`` závislost |
|||
|
|||
````C# |
|||
[DependsOn(typeof(AbpAspNetCoreMvcModule))] |
|||
[DependsOn(typeof(AbpAutofacModule))] // Přidá závislost na AbpAutofacModule |
|||
public class AppModule : AbpModule |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
3. Upravíme `Program.cs` aby používal Autofac: |
|||
|
|||
````csharp |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace BasicAspNetCoreApplication |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}) |
|||
.UseAutofac(); // Přidejte tento řádek |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## Zdrojový kód |
|||
|
|||
Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication). |
|||
|
|||
@ -1,104 +0,0 @@ |
|||
## Začínáme s ASP.NET Core MVC šablonou |
|||
|
|||
Tento tutoriál vysvětluje, jak vytvořit novou ASP.NET Core MVC webovou aplikaci pomocí úvodní šablony, jak ji nakonfigurovat a spustit. |
|||
|
|||
### Tvorba nového projektu |
|||
|
|||
Tento tutoriál používá k tvorbě nového projektu **ABP CLI**. Podívejte se na stránku [Začínáme](https://abp.io/get-started) pro více možností. |
|||
|
|||
Pokud ještě nemáte ABP CLI nainstalováno, učiňte tak pomocí okna příkazového řádku: |
|||
|
|||
````bash |
|||
dotnet tool install -g Volo.Abp.Cli |
|||
```` |
|||
|
|||
K tvorbě vašeho projektu použijte příkaz `abp new` v prázdné složce: |
|||
|
|||
````bash |
|||
abp new Acme.BookStore |
|||
```` |
|||
|
|||
> Můžete použít různé úrovně jmenných prostorů; např. BookStore, Acme.BookStore nebo Acme.Retail.BookStore. |
|||
|
|||
Příkaz `new` vytvoří **vrstvenou MVC aplikaci** s **Entity Framework Core** jako databázovým poskytovatelem. Jsou zde však i jiné možnosti. Podívejte se na [CLI dokumnentaci](CLI.md) pro všechny další možností. |
|||
|
|||
#### Požadavky |
|||
|
|||
Vytvořené řešení vyžaduje; |
|||
|
|||
* [Visual Studio 2019 (v16.4.0+)](https://visualstudio.microsoft.com/vs/) |
|||
* [.NET Core 3.0+](https://www.microsoft.com/net/download/dotnet-core/) |
|||
* [Node v12+](https://nodejs.org) |
|||
* [Yarn v1.19+](https://classic.yarnpkg.com/) |
|||
|
|||
### Struktura řešení |
|||
|
|||
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 aplikace](Startup-Templates/Application.md) k detailnímu pochopení struktury řešení. |
|||
|
|||
### Connection string databáze |
|||
|
|||
Zkontrolujte **connection string** v souboru `appsettings.json` v projektu `.Web`: |
|||
|
|||
````json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Řešení je nakonfigurováno k používání **Entity Framework Core** s **MS SQL Server**. EF Core podporuje [různé](https://docs.microsoft.com/en-us/ef/core/providers/) databázové poskytovatele, takže můžete použít i jiné DBMS. V případě potřeby změňte connection string. |
|||
|
|||
### Tvorba databáze & aplikace databázových migrací |
|||
|
|||
K vytvoření databáze máte dvě možnosti. |
|||
|
|||
#### Použití DbMigrator aplikace |
|||
|
|||
Řešení obsahuje konzolovou aplikaci (v tomto příkladu nazvanou `Acme.BookStore.DbMigrator`), která může vytvářet databáze, aplikovat migrace a vkládat seed data. Je užitečná jak pro vývojové, tak pro produkční prostředí. |
|||
|
|||
> Projekt `.DbMigrator` má vlastní `appsettings.json`. Takže pokud jste změnili connection string uvedený výše, musíte změnit také tento. |
|||
|
|||
Klikněte pravým na projekt `.DbMigrator` a vyberte **Set as StartUp Project**: |
|||
|
|||
 |
|||
|
|||
Zmáčkněte F5 (nebo Ctrl+F5) ke spuštění aplikace. Výstup bude vypadat následovně: |
|||
|
|||
 |
|||
|
|||
#### Použití EF Core Update-Database příkazu |
|||
|
|||
Ef Core má `Update-Database` příkaz, který v případě potřeby vytvoří databázi a aplikuje čekající migrace. Klikněte pravým na projekt `.Web` a vyberte **Set as StartUp Project**: |
|||
|
|||
 |
|||
|
|||
Otevřete **Package Manager Console**, vyberte projekt `.EntityFrameworkCore.DbMigrations` jako **Default Project** and spusťte příkaz `Update-Database`: |
|||
|
|||
 |
|||
|
|||
Dojde k vytvoření nové databáze na základě nakonfigurovaného connection stringu. |
|||
|
|||
> Použití nástroje `.Migrator` je doporučený způsob, jelikož zároveň vloží seed data nutné k správnému běhu webové aplikace. |
|||
|
|||
### Spuštění aplikace |
|||
|
|||
Ujistěte se že je projekt `.Web` nastaven jako startovací projekt. Spusťte aplikaci což následně otevře **úvodní** stránku ve vašem prohlížeči: |
|||
|
|||
 |
|||
|
|||
Klikněte na tlačítko **Přihlásit**, vložte `admin` jako uživatelské jméno a `1q2w3E*` jako heslo k přihlášení do aplikace. |
|||
|
|||
Startovací šabloná obsahuje **identity management** a **tenant management** moduly. Jakmile se přihlásite, budete mít přístup do nabídky Administrace, kde můžete spravovat **tenanty**, **role**, **uživatele** a jejich **oprávnění**. Správa uživatelů vypadá takto: |
|||
|
|||
 |
|||
|
|||
### Co dále? |
|||
|
|||
* [Tutoriál vývoje aplikace](Tutorials/AspNetCore-Mvc/Part-I.md) |
|||
@ -1,181 +0,0 @@ |
|||
# Začínáme s konzolovou aplikací |
|||
|
|||
Tento tutoriál vysvětluje jak začít s ABP z ničeho s minimem závislostí. Obvykle chcete začít se **[startovací šablonou](https://abp.io/Templates)**. |
|||
|
|||
## Tvorba nového projektu |
|||
|
|||
Vytvořte regulérní .NET Core konzolovou aplikaci z Visual Studio: |
|||
|
|||
 |
|||
|
|||
## Instalace Volo.Abp balíku |
|||
|
|||
Volo.Abp.Core je základní NuGet balík k tvorbě aplikací založených na ABP. Takže ho nainstalujeme do projektu: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Core |
|||
```` |
|||
|
|||
## Tvorba prvního ABP modulu |
|||
|
|||
ABP je modulární framework a proto vyžaduje **spouštěcí (kořenový) modul** což je třída dědící z ``AbpModule``: |
|||
|
|||
````C# |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
public class AppModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
```` |
|||
|
|||
``AppModule`` je dobrý název pro spouštěcí modul aplikace. |
|||
|
|||
## Inicializace aplikace |
|||
|
|||
Dalším krokem je bootstrap aplikace pomocí spouštěcího modulu vytvořeného výše: |
|||
|
|||
````C# |
|||
using System; |
|||
using Volo.Abp; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
using (var application = AbpApplicationFactory.Create<AppModule>()) |
|||
{ |
|||
application.Initialize(); |
|||
|
|||
Console.WriteLine("Press ENTER to stop application..."); |
|||
Console.ReadLine(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
``AbpApplicationFactory`` se používá k vytvoření aplikace a načtení všech modulů, s využitím ``AppModule`` jako spouštěcím modulem. ``Initialize()`` metoda spouští aplikaci. |
|||
|
|||
## Ahoj světe! |
|||
|
|||
Aplikace výše zatím nic nedělá. Pojďme proto vytvořit službu která už něco dělá: |
|||
|
|||
````C# |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
public class HelloWorldService : ITransientDependency |
|||
{ |
|||
public void SayHello() |
|||
{ |
|||
Console.WriteLine("Hello World!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
``ITransientDependency`` je speciální rozhraní ABP, které automaticky registruje službu jako přechodnou (více v [dokumentu vkládání závislostí](Dependency-Injection.md)). |
|||
|
|||
Nyní můžeme vyřešit ``HelloWorldService`` a vypsat naše ahoj. Změníme Program.cs podle vyobrazení níže: |
|||
|
|||
````C# |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
using (var application = AbpApplicationFactory.Create<AppModule>()) |
|||
{ |
|||
application.Initialize(); |
|||
|
|||
// Vyřeší službu a použije ji |
|||
var helloWorldService = |
|||
application.ServiceProvider.GetService<HelloWorldService>(); |
|||
helloWorldService.SayHello(); |
|||
|
|||
Console.WriteLine("Press ENTER to stop application..."); |
|||
Console.ReadLine(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
I když je to dostačující pro tento jednoduchý príklad kódu, je vždy lepší v případě přímého řešení závislostí z ``IServiceProvider`` vytvořit rámce (více v [dokumentu vkládání závislostí](Dependency-Injection.md)). |
|||
|
|||
## Využití Autofac jako frameworku pro vkládání závislostí |
|||
|
|||
Ačkoliv je AspNet Core systém pro vkládání závíslostí (DI) skvělý pro základní požadavky, Autofac poskytuje pokročilé funkce jako injekce vlastností nebo záchyt metod, které jsou v ABP užity k provádění pokročilých funkcí frameworku. |
|||
|
|||
Nahrazení AspNet Core DI systému za Autofac a integrace s ABP je snadná. |
|||
|
|||
1. Nainstalujeme [Volo.Abp.Autofac](https://www.nuget.org/packages/Volo.Abp.Autofac) balík |
|||
|
|||
``` |
|||
Install-Package Volo.Abp.Autofac |
|||
``` |
|||
|
|||
1. Přidáme ``AbpAutofacModule`` závislost |
|||
|
|||
```c# |
|||
[DependsOn(typeof(AbpAutofacModule))] // Přidá závislost na AbpAutofacModule |
|||
public class AppModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
``` |
|||
|
|||
1. Změníme soubor ``Program.cs`` podle vyobrazení níže: |
|||
|
|||
```c# |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
|
|||
namespace AbpConsoleDemo |
|||
{ |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
using (var application = AbpApplicationFactory.Create<AppModule>(options => |
|||
{ |
|||
options.UseAutofac(); // Autofac integrace |
|||
})) |
|||
{ |
|||
application.Initialize(); |
|||
|
|||
// Vyřeší službu a použije ji |
|||
var helloWorldService = |
|||
application.ServiceProvider.GetService<HelloWorldService>(); |
|||
helloWorldService.SayHello(); |
|||
|
|||
Console.WriteLine("Press ENTER to stop application..."); |
|||
Console.ReadLine(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Stačí volat metodu `options.UseAutofac()` v možnostech `AbpApplicationFactory.Create`. |
|||
|
|||
## Zdrojový kód |
|||
|
|||
Získejte zdrojový kód vzorového projektu vytvořeného v tomto tutoriálů [z tohoto odkazu](https://github.com/abpframework/abp-samples/tree/master/BasicConsoleApplication). |
|||
@ -1,25 +0,0 @@ |
|||
# ABP dokumentace |
|||
|
|||
ABP je **open source aplikační framework** se zaměřením na vývoj webových aplikací založených na ASP.NET Core, zároveň ho však lze využít i k vývoji jiných typů aplikací. |
|||
|
|||
K procházení dokumentace využijte navigační nabídky vlevo. |
|||
|
|||
## Začínáme |
|||
|
|||
Nejsnazší cestou jak začít nový projekt s ABP je užití startovací šablony: |
|||
|
|||
* [ASP.NET Core MVC (Razor Pages) UI Počáteční Šablona](Getting-Started-AspNetCore-MVC-Template.md) |
|||
* [Angular UI Počáteční Š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ů: |
|||
|
|||
* [Konzolová Aplikace](Getting-Started-Console-Application.md) |
|||
* [ASP.NET Core Web Aplikace](Getting-Started-AspNetCore-Application.md) |
|||
|
|||
## Zdrojový kód |
|||
|
|||
ABP je hostovaný na GitHub. Zobrazit [zdrojový kód](https://github.com/abpframework/abp). |
|||
|
|||
## Chcete přispět? |
|||
|
|||
ABP je komunitně řízený open source projekt. Podívejte se na [průvodce pro přispěvatele](Contribution/Index.md) pokud chcete být součástí tohoto projektu. |
|||
@ -1,26 +0,0 @@ |
|||
# Noční sestavení |
|||
|
|||
Všechny balíky frameworku a modulů jsou každý večer nasazeny na MyGet. Takže můžete používat nebo testovat nejnovější kód bez čekání na další vydání. |
|||
|
|||
## Konfigurace Visual Studia |
|||
|
|||
> Vyžaduje Visual Studio 2017+ |
|||
|
|||
1. Přejděte do `Tools > Options > NuGet Package Manager > Package Source`. |
|||
2. Klikněte na zelenou ikonku `+`. |
|||
3. Nastavte `ABP Nightly` jako *Name* a `https://www.myget.org/F/abp-nightly/api/v3/index.json` jako *Source* podle vyobrazení níže: |
|||
 |
|||
4. Klikněte na `Update`. |
|||
5. Klikněte na `OK` k uložení změn. |
|||
|
|||
## Instalace balíku |
|||
|
|||
Nyní můžete instalovat náhledové / noční balíky do Vašeho projektu z NuGet prohlížeče nebo Package Manager Console. |
|||
|
|||
 |
|||
|
|||
1. V nuget prohlížeči, vyberte "Include prereleases". |
|||
2. Změňte zdroj balíků na "All". |
|||
3. Vyhledejte balík. Uvidíte prerelease balík formátovaný jako `(VERZE)-preview(DATUM)` (např *v0.16.0-preview20190401* jako v tomto vzorku). |
|||
4. Můžete kliknout na `Install` k přídání balíku do projektu. |
|||
|
|||
@ -1,376 +0,0 @@ |
|||
{ |
|||
"items": [ |
|||
{ |
|||
"text": "Začínáme", |
|||
"items": [ |
|||
{ |
|||
"text": "Ze startovacích šablon", |
|||
"items": [ |
|||
{ |
|||
"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" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Z prázdných projektů", |
|||
"items": [ |
|||
{ |
|||
"text": "S ASP.NET Core Web aplikací", |
|||
"path": "Getting-Started-AspNetCore-Application.md" |
|||
}, |
|||
{ |
|||
"text": "S konzolovou aplikací", |
|||
"path": "Getting-Started-Console-Application.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Tutoriály", |
|||
"items": [ |
|||
{ |
|||
"text": "Vývoj aplikace", |
|||
"items": [ |
|||
{ |
|||
"text": "S ASP.NET Core MVC UI", |
|||
"path": "Tutorials/AspNetCore-Mvc/Part-I.md" |
|||
}, |
|||
{ |
|||
"text": "S Angular UI", |
|||
"path": "Tutorials/Angular/Part-I.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "CLI", |
|||
"path": "CLI.md" |
|||
}, |
|||
{ |
|||
"text": "Základy", |
|||
"items": [ |
|||
{ |
|||
"text": "Konfigurace", |
|||
"path": "Configuration.md" |
|||
}, |
|||
{ |
|||
"text": "Možnosti", |
|||
"path": "Options.md" |
|||
}, |
|||
{ |
|||
"text": "Vkládání závislostí", |
|||
"path": "Dependency-Injection.md", |
|||
"items": [ |
|||
{ |
|||
"text": "AutoFac integrace", |
|||
"path": "Autofac-Integration.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Virtuální systém souborů", |
|||
"path": "Virtual-File-System.md" |
|||
}, |
|||
{ |
|||
"text": "Lokalizace", |
|||
"path": "Localization.md" |
|||
}, |
|||
{ |
|||
"text": "Zpracování výjimek", |
|||
"path": "Exception-Handling.md" |
|||
}, |
|||
{ |
|||
"text": "Validace", |
|||
"path": "Validation.md", |
|||
"items": [ |
|||
{ |
|||
"text": "FluentValidation integrace", |
|||
"path": "FluentValidation.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Autorizace", |
|||
"path": "Authorization.md" |
|||
}, |
|||
{ |
|||
"text": "Ukládání do mezipaměti", |
|||
"path": "Caching.md" |
|||
}, |
|||
{ |
|||
"text": "Audit" |
|||
}, |
|||
{ |
|||
"text": "Nastavení", |
|||
"path": "Settings.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Události", |
|||
"items": [ |
|||
{ |
|||
"text": "Event bus (místní)" |
|||
}, |
|||
{ |
|||
"text": "Distribuovaný event bus", |
|||
"items": [ |
|||
{ |
|||
"text": "RabbitMQ integrace" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Služby", |
|||
"items": [ |
|||
{ |
|||
"text": "Současný uživatel", |
|||
"path": "CurrentUser.md" |
|||
}, |
|||
{ |
|||
"text": "Mapování objekt na objekt", |
|||
"path": "Object-To-Object-Mapping.md" |
|||
}, |
|||
{ |
|||
"text": "Serializace objektu" |
|||
}, |
|||
{ |
|||
"text": "Serializace JSON" |
|||
}, |
|||
{ |
|||
"text": "Emailování" |
|||
}, |
|||
{ |
|||
"text": "GUIDy" |
|||
}, |
|||
{ |
|||
"text": "Vláknování" |
|||
}, |
|||
{ |
|||
"text": "Časování" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Multitenance", |
|||
"path": "Multi-Tenancy.md" |
|||
}, |
|||
{ |
|||
"text": "Vývoj modulů", |
|||
"items": [ |
|||
{ |
|||
"text": "Základy", |
|||
"path": "Module-Development-Basics.md" |
|||
}, |
|||
{ |
|||
"text": "Zásuvné moduly" |
|||
}, |
|||
{ |
|||
"text": "Nejlepší praktiky", |
|||
"path": "Best-Practices/Index.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Domain driven design", |
|||
"path": "Domain-Driven-Design.md", |
|||
"items": [ |
|||
{ |
|||
"text": "Doménová vrstva", |
|||
"items": [ |
|||
{ |
|||
"text": "Entity & agregované kořeny", |
|||
"path": "Entities.md" |
|||
}, |
|||
{ |
|||
"text": "Hodnotové objekty" |
|||
}, |
|||
{ |
|||
"text": "Repozitáře", |
|||
"path": "Repositories.md" |
|||
}, |
|||
{ |
|||
"text": "Doménové služby" |
|||
}, |
|||
{ |
|||
"text": "Specifikace" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Aplikační vrstva", |
|||
"items": [ |
|||
{ |
|||
"text": "Aplikační služby", |
|||
"path": "Application-Services.md" |
|||
}, |
|||
{ |
|||
"text": "Objekty přenosu dat" |
|||
}, |
|||
{ |
|||
"text": "Jednotka práce" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "ASP.NET Core", |
|||
"items": [ |
|||
{ |
|||
"text": "API", |
|||
"items": [ |
|||
{ |
|||
"text": "Automatické API řadiče", |
|||
"path": "AspNetCore/Auto-API-Controllers.md" |
|||
}, |
|||
{ |
|||
"text": "Dynamičtí C# API klienti", |
|||
"path": "AspNetCore/Dynamic-CSharp-API-Clients.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Uživatelské rozhraní", |
|||
"items": [ |
|||
{ |
|||
"text": "Správa klientských balíčků", |
|||
"path": "AspNetCore/Client-Side-Package-Management.md" |
|||
}, |
|||
{ |
|||
"text": "Svazování & minifikace", |
|||
"path": "AspNetCore/Bundling-Minification.md" |
|||
}, |
|||
{ |
|||
"text": "Tag pomocníci", |
|||
"path": "AspNetCore/Tag-Helpers/Index.md" |
|||
}, |
|||
{ |
|||
"text": "Widgety", |
|||
"path": "AspNetCore/Widgets.md" |
|||
}, |
|||
{ |
|||
"text": "Motivy", |
|||
"path": "AspNetCore/Theming.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Přístup k datům", |
|||
"path": "Data-Access.md", |
|||
"items": [ |
|||
{ |
|||
"text": "Connection stringy", |
|||
"path": "Connection-Strings.md" |
|||
}, |
|||
{ |
|||
"text": "Poskytovatelé databází", |
|||
"items": [ |
|||
{ |
|||
"text": "Entity Framework Core", |
|||
"path": "Entity-Framework-Core.md", |
|||
"items": [ |
|||
{ |
|||
"text": "Přepnutí na MySQL", |
|||
"path": "Entity-Framework-Core-MySQL.md" |
|||
}, |
|||
{ |
|||
"text": "Přepnutí na PostgreSQL", |
|||
"path": "Entity-Framework-Core-PostgreSQL.md" |
|||
}, |
|||
{ |
|||
"text": "Přepnutí na SQLite", |
|||
"path": "Entity-Framework-Core-SQLite.md" |
|||
}, |
|||
{ |
|||
"text": "Přepnutí na jiný DBMS", |
|||
"path": "Entity-Framework-Core-Other-DBMS.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "MongoDB", |
|||
"path": "MongoDB.md" |
|||
}, |
|||
{ |
|||
"text": "Dapper", |
|||
"path": "Dapper.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Pozadí", |
|||
"items": [ |
|||
{ |
|||
"text": "Úkony na pozadí", |
|||
"path": "Background-Jobs.md", |
|||
"items": [ |
|||
{ |
|||
"text": "Hangfire integrace", |
|||
"path": "Background-Jobs-Hangfire.md" |
|||
}, |
|||
{ |
|||
"text": "RabbitMQ integrace", |
|||
"path": "Background-Jobs-RabbitMq.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"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": [ |
|||
{ |
|||
"text": "Mikroslužby demo", |
|||
"path": "Samples/Microservice-Demo.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text": "Moduly aplikace", |
|||
"path": "Modules/Index.md" |
|||
}, |
|||
{ |
|||
"text": "Architektura mikroslužby", |
|||
"path": "Microservice-Architecture.md" |
|||
}, |
|||
{ |
|||
"text": "Testování" |
|||
}, |
|||
{ |
|||
"text": "Noční sestavení", |
|||
"path": "Nightly-Builds.md" |
|||
}, |
|||
{ |
|||
"text": "Průvodce pro přispěvatele", |
|||
"path": "Contribution/Index.md" |
|||
} |
|||
] |
|||
} |
|||
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
@ -1,19 +0,0 @@ |
|||
{ |
|||
"Languages":[ |
|||
{ |
|||
"DisplayName" : "English", |
|||
"Code" : "en", |
|||
"IsDefault": true |
|||
}, |
|||
{ |
|||
"DisplayName" : "Português", |
|||
"Code" : "pt-BR", |
|||
"IsDefault": false |
|||
}, |
|||
{ |
|||
"DisplayName" : "简体中文", |
|||
"Code" : "zh-Hans", |
|||
"IsDefault": false |
|||
} |
|||
] |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
{ |
|||
"grammarly.userWords": [ |
|||
"api", |
|||
"apiName", |
|||
"cli", |
|||
"defaultProject", |
|||
"formatter", |
|||
"md", |
|||
"monorepo", |
|||
"ngsw", |
|||
"npx", |
|||
"pwa", |
|||
"rootNamespace" |
|||
] |
|||
} |
|||
@ -1,344 +0,0 @@ |
|||
# API Versioning System |
|||
|
|||
ABP Framework integrates the [ASPNET-API-Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki) feature and adapts to C# and JavaScript Static Client Proxies and [Auto API Controller](../API/Auto-API-Controllers.md). |
|||
|
|||
|
|||
## Enable API Versioning |
|||
|
|||
```cs |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Show neutral/versionless APIs. |
|||
context.Services.AddTransient<IApiControllerFilter, NoControllerFilter>(); |
|||
context.Services.AddAbpApiVersioning(options => |
|||
{ |
|||
options.ReportApiVersions = true; |
|||
options.AssumeDefaultVersionWhenUnspecified = true; |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ChangeControllerModelApiExplorerGroupName = false; |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
## C# and JavaScript Static Client Proxies |
|||
|
|||
This feature does not compatible with [URL Path Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path), we suggest to use [Versioning-via-the-Query-String](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-Query-String). |
|||
|
|||
### Example |
|||
|
|||
**Application Services:** |
|||
```cs |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<BookDto> GetAsync(); |
|||
} |
|||
|
|||
public interface IBookV2AppService : IApplicationService |
|||
{ |
|||
Task<BookDto> GetAsync(); |
|||
|
|||
Task<BookDto> GetAsync(string isbn); |
|||
} |
|||
``` |
|||
|
|||
**HttpApi Controllers:** |
|||
```cs |
|||
[Area(BookStoreRemoteServiceConsts.ModuleName)] |
|||
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)] |
|||
[ApiVersion("1.0", Deprecated = true)] |
|||
[ApiController] |
|||
[ControllerName("Book")] |
|||
[Route("api/BookStore/Book")] |
|||
public class BookController : BookStoreController, IBookAppService |
|||
{ |
|||
private readonly IBookAppService _bookAppService; |
|||
|
|||
public BookController(IBookAppService bookAppService) |
|||
{ |
|||
_bookAppService = bookAppService; |
|||
} |
|||
|
|||
[HttpGet] |
|||
public async Task<BookDto> GetAsync() |
|||
{ |
|||
return await _bookAppService.GetAsync(); |
|||
} |
|||
} |
|||
|
|||
[Area(BookStoreRemoteServiceConsts.ModuleName)] |
|||
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)] |
|||
[ApiVersion("2.0")] |
|||
[ApiController] |
|||
[ControllerName("Book")] |
|||
[Route("api/BookStore/Book")] |
|||
public class BookV2Controller : BookStoreController, IBookV2AppService |
|||
{ |
|||
private readonly IBookV2AppService _bookAppService; |
|||
|
|||
public BookV2Controller(IBookV2AppService bookAppService) |
|||
{ |
|||
_bookAppService = bookAppService; |
|||
} |
|||
|
|||
[HttpGet] |
|||
public async Task<BookDto> GetAsync() |
|||
{ |
|||
return await _bookAppService.GetAsync(); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("{isbn}")] |
|||
public async Task<BookDto> GetAsync(string isbn) |
|||
{ |
|||
return await _bookAppService.GetAsync(isbn); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**Generated CS and JS proxies:** |
|||
|
|||
```cs |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(IBookAppService), typeof(BookClientProxy))] |
|||
public partial class BookClientProxy : ClientProxyBase<IBookAppService>, IBookAppService |
|||
{ |
|||
public virtual async Task<BookDto> GetAsync() |
|||
{ |
|||
return await RequestAsync<BookDto>(nameof(GetAsync)); |
|||
} |
|||
} |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(IBookV2AppService), typeof(BookV2ClientProxy))] |
|||
public partial class BookV2ClientProxy : ClientProxyBase<IBookV2AppService>, IBookV2AppService |
|||
{ |
|||
public virtual async Task<BookDto> GetAsync() |
|||
{ |
|||
return await RequestAsync<BookDto>(nameof(GetAsync)); |
|||
} |
|||
|
|||
public virtual async Task<BookDto> GetAsync(string isbn) |
|||
{ |
|||
return await RequestAsync<BookDto>(nameof(GetAsync), new ClientProxyRequestTypeValue |
|||
{ |
|||
{ typeof(string), isbn } |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
```js |
|||
// controller bookStore.books.book |
|||
|
|||
(function(){ |
|||
|
|||
abp.utils.createNamespace(window, 'bookStore.books.book'); |
|||
|
|||
bookStore.books.book.get = function(api_version, ajaxParams) { |
|||
var api_version = api_version ? api_version : '1.0'; |
|||
return abp.ajax($.extend(true, { |
|||
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
|||
type: 'GET' |
|||
}, ajaxParams)); |
|||
}; |
|||
|
|||
})(); |
|||
|
|||
// controller bookStore.books.bookV2 |
|||
|
|||
(function(){ |
|||
|
|||
abp.utils.createNamespace(window, 'bookStore.books.bookV2'); |
|||
|
|||
bookStore.books.bookV2.get = function(api_version, ajaxParams) { |
|||
var api_version = api_version ? api_version : '2.0'; |
|||
return abp.ajax($.extend(true, { |
|||
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
|||
type: 'GET' |
|||
}, ajaxParams)); |
|||
}; |
|||
|
|||
bookStore.books.bookV2.getAsyncByIsbn = function(isbn, api_version, ajaxParams) { |
|||
var api_version = api_version ? api_version : '2.0'; |
|||
return abp.ajax($.extend(true, { |
|||
url: abp.appPath + 'api/BookStore/Book/' + isbn + '' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '', |
|||
type: 'GET' |
|||
}, ajaxParams)); |
|||
}; |
|||
|
|||
})(); |
|||
``` |
|||
|
|||
|
|||
## Changing version manually |
|||
|
|||
If an application service class supports multiple versions. You can inject `ICurrentApiVersionInfo` to switch versions in C#. |
|||
|
|||
```cs |
|||
var currentApiVersionInfo = _abpApplication.ServiceProvider.GetRequiredService<ICurrentApiVersionInfo>(); |
|||
var bookV4AppService = _abpApplication.ServiceProvider.GetRequiredService<IBookV4AppService>(); |
|||
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0"))) |
|||
{ |
|||
book = await bookV4AppService.GetAsync(); |
|||
logger.LogWarning(book.Title); |
|||
logger.LogWarning(book.ISBN); |
|||
} |
|||
|
|||
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.1"))) |
|||
{ |
|||
book = await bookV4AppService.GetAsync(); |
|||
logger.LogWarning(book.Title); |
|||
logger.LogWarning(book.ISBN); |
|||
} |
|||
``` |
|||
|
|||
We have made a default version in the JS proxy. Of course, you can also manually change the version. |
|||
|
|||
```js |
|||
|
|||
bookStore.books.bookV4.get("4.0") // Manually change the version. |
|||
//Title: Mastering ABP Framework V4.0 |
|||
|
|||
bookStore.books.bookV4.get() // The latest supported version is used by default. |
|||
//Title: Mastering ABP Framework V4.1 |
|||
``` |
|||
|
|||
## Auto API Controller |
|||
|
|||
```cs |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
//2.0 Version |
|||
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts => |
|||
{ |
|||
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v2.TodoAppService).Namespace; |
|||
opts.ApiVersions.Add(new ApiVersion(2, 0)); |
|||
}); |
|||
|
|||
//1.0 Compatibility version |
|||
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts => |
|||
{ |
|||
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v1.TodoAppService).Namespace; |
|||
opts.ApiVersions.Add(new ApiVersion(1, 0)); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>(); |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
preActions.Configure(options); |
|||
}); |
|||
|
|||
// Show neutral/versionless APIs. |
|||
context.Services.AddTransient<IApiControllerFilter, NoControllerFilter>(); |
|||
context.Services.AddAbpApiVersioning(options => |
|||
{ |
|||
options.ReportApiVersions = true; |
|||
options.AssumeDefaultVersionWhenUnspecified = true; |
|||
|
|||
options.ConfigureAbp(preActions.Configure()); |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ChangeControllerModelApiExplorerGroupName = false; |
|||
}); |
|||
} |
|||
``` |
|||
|
|||
## Swagger/VersionedApiExplorer |
|||
|
|||
```cs |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Show neutral/versionless APIs. |
|||
context.Services.AddTransient<IApiControllerFilter, NoControllerFilter>(); |
|||
context.Services.AddAbpApiVersioning(options => |
|||
{ |
|||
options.ReportApiVersions = true; |
|||
options.AssumeDefaultVersionWhenUnspecified = true; |
|||
}).AddApiExplorer(options => { |
|||
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service |
|||
// note: the specified format code will format the version as "'v'major[.minor][-status]" |
|||
options.GroupNameFormat = "'v'VVV"; |
|||
|
|||
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat |
|||
// can also be used to control the format of the API version in route templates |
|||
options.SubstituteApiVersionInUrl = true; |
|||
}); |
|||
|
|||
context.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); |
|||
|
|||
context.Services.AddAbpSwaggerGen( |
|||
options => |
|||
{ |
|||
// add a custom operation filter which sets default values |
|||
options.OperationFilter<SwaggerDefaultValues>(); |
|||
|
|||
options.CustomSchemaIds(type => type.FullName); |
|||
}); |
|||
|
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ChangeControllerModelApiExplorerGroupName = false; |
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
var env = context.GetEnvironment(); |
|||
|
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
else |
|||
{ |
|||
app.UseErrorPage(); |
|||
app.UseHsts(); |
|||
} |
|||
|
|||
app.UseHttpsRedirection(); |
|||
app.UseStaticFiles(); |
|||
app.UseRouting(); |
|||
app.UseAbpRequestLocalization(); |
|||
|
|||
app.UseSwagger(); |
|||
app.UseAbpSwaggerUI( |
|||
options => |
|||
{ |
|||
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>(); |
|||
// build a swagger endpoint for each discovered API version |
|||
foreach (var description in provider.ApiVersionDescriptions) |
|||
{ |
|||
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); |
|||
} |
|||
}); |
|||
|
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
``` |
|||
|
|||
## Custom multi-version API controller |
|||
|
|||
ABP Framework will not affect to your APIs, you can freely implement your APIs according to the Microsoft's documentation. |
|||
|
|||
Further information, see https://github.com/dotnet/aspnet-api-versioning/wiki |
|||
|
|||
|
|||
## Sample source code |
|||
|
|||
Follow the link below to get the sample's complete source-code |
|||
|
|||
https://github.com/abpframework/abp-samples/tree/master/Api-Versioning |
|||
@ -1,70 +0,0 @@ |
|||
# Application Configuration Endpoint |
|||
|
|||
ABP Framework provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, is the list of some fundamental information at this endpoint: |
|||
|
|||
* Granted [policies](../Authorization.md) (permissions) for the current user. |
|||
* [Setting](../Settings.md) values for the current user. |
|||
* Info about the [current user](../CurrentUser.md) (like id and user name). |
|||
* Info about the current [tenant](../Multi-Tenancy.md) (like id and name). |
|||
* [Time zone](../Timing.md) information for the current user and the [clock](../Timing.md) type of the application. |
|||
|
|||
> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint. |
|||
|
|||
## HTTP API |
|||
|
|||
If you navigate to the `/api/abp/application-configuration` URL of an ABP Framework based web application or HTTP Service, you can access the configuration as a JSON object. This endpoint is useful to create the client of your application. |
|||
|
|||
## Script |
|||
|
|||
For ASP.NET Core MVC (Razor Pages) applications, the same configuration values are also available on the JavaScript side. `/Abp/ApplicationConfigurationScript` is the URL of the script that is auto-generated based on the HTTP API above. |
|||
|
|||
See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI. |
|||
|
|||
Other UI types provide services native to the related platform. For example, see the [Angular UI settings documentation](../UI/Angular/Settings.md) to learn how to use the setting values exposes by this endpoint. |
|||
|
|||
## Extending the Endpoint |
|||
|
|||
The **application-configuration** endpoint contains some useful information about the application, such as _localization values_, _current user information_, _granted permissions_, etc. Even most of the time these provided values are sufficient to use in your application to perform common requirements such as getting the logged-in user's ID or its granted permissions. You may still want to extend this endpoint and provide additional information for your application/service. At that point, you can use the `IApplicationConfigurationContributor` endpoint. |
|||
|
|||
### IApplicationConfigurationContributor |
|||
|
|||
`IApplicationConfigurationContributor` is the interface that should be implemented to add additional information to the **application-configuration** endpoint. |
|||
|
|||
**Example: Setting the deployment version** |
|||
|
|||
```csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Acme.BookStore.Web |
|||
{ |
|||
public class MyApplicationConfigurationContributor : IApplicationConfigurationContributor |
|||
{ |
|||
public Task ContributeAsync(ApplicationConfigurationContributorContext context) |
|||
{ |
|||
//for simplicity, it's a static number, you can inject any service to this class and perform your logic... |
|||
var deploymentVersion = "v1.0.0"; |
|||
|
|||
//setting the deploymentVersion |
|||
context.ApplicationConfiguration.SetProperty("deploymentVersion", deploymentVersion); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Add your contributor instance to the `AbpApplicationConfigurationOptions` |
|||
|
|||
```csharp |
|||
Configure<AbpApplicationConfigurationOptions>(options => |
|||
{ |
|||
options.Contributors.AddIfNotContains(new MyApplicationConfigurationContributor()); |
|||
}); |
|||
``` |
|||
|
|||
* `IApplicationConfigurationContributor` defines the `ContributeAsync` method to extend the **application-configuration** endpoint with the specified additional data. |
|||
* You can inject services and perform any logic needed to extend the endpoint as you wish. |
|||
|
|||
> Application configuration contributors are executed as a part of the application configuration initialization process. |
|||
@ -1,33 +0,0 @@ |
|||
# Application Localization Endpoint |
|||
|
|||
ABP Framework provides a pre-built and standard endpoint that returns all the [localization](../Localization.md) resources and texts defined in the server. |
|||
|
|||
> If you have started with ABP's startup solution templates and using one of the official UI options, then all these are set up for you and you don't need to know these details. However, if you are building a UI application from scratch, you may want to know this endpoint. |
|||
|
|||
## HTTP API |
|||
|
|||
`/api/abp/application-localization` is the main URL of the HTTP API that returns the localization data as a JSON string. I accepts the following query string parameters: |
|||
|
|||
* `cultureName` (required): A culture code to get the localization data, like `en` or `en-US`. |
|||
* `onlyDynamics` (optional, default: `false`): Can be set to `true` to only get the dynamically defined localization resources and texts. If your client-side application shares the same localization resources with the server (like ABP's Blazor and MVC UIs), you can set `onlyDynamics` to `true`. |
|||
|
|||
**Example request:** |
|||
|
|||
```` |
|||
/api/abp/application-localization?cultureName=en |
|||
```` |
|||
|
|||
## Script |
|||
|
|||
For [ASP.NET Core MVC (Razor Pages)](../UI/AspNetCore/Overall.md) applications, the same localization data is also available on the JavaScript side. `/Abp/ApplicationLocalizationScript` is the URL of the script that is auto-generated based on the HTTP API above. |
|||
|
|||
**Example request:** |
|||
|
|||
```` |
|||
/Abp/ApplicationLocalizationScript?cultureName=en |
|||
```` |
|||
|
|||
See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI. |
|||
|
|||
Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint. |
|||
|
|||
@ -1,219 +0,0 @@ |
|||
# Auto API Controllers |
|||
|
|||
Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc. |
|||
|
|||
ABP can **automagically** configure your application services as API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible to fully customize it. |
|||
|
|||
## Configuration |
|||
|
|||
Basic configuration is simple. Just configure `AbpAspNetCoreMvcOptions` and use `ConventionalControllers.Create` method as shown below: |
|||
|
|||
````csharp |
|||
[DependsOn(BookStoreApplicationModule)] |
|||
public class BookStoreWebModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options |
|||
.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This example code configures all the application services in the assembly containing the class `BookStoreApplicationModule`. The figure below shows the resulting API on the [Swagger UI](https://swagger.io/tools/swagger-ui/). |
|||
|
|||
 |
|||
|
|||
### Examples |
|||
|
|||
Some example method names and the corresponding routes calculated by convention: |
|||
|
|||
| Service Method Name | HTTP Method | Route | |
|||
| ----------------------------------------------------- | ----------- | -------------------------- | |
|||
| GetAsync(Guid id) | GET | /api/app/book/{id} | |
|||
| GetListAsync() | GET | /api/app/book | |
|||
| CreateAsync(CreateBookDto input) | POST | /api/app/book | |
|||
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} | |
|||
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} | |
|||
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors | |
|||
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor | |
|||
|
|||
### HTTP Method |
|||
|
|||
ABP uses a naming convention while determining the HTTP method for a service method (action): |
|||
|
|||
- **Get**: Used if the method name starts with 'GetList', 'GetAll' or 'Get'. |
|||
- **Put**: Used if the method name starts with 'Put' or 'Update'. |
|||
- **Delete**: Used if the method name starts with 'Delete' or 'Remove'. |
|||
- **Post**: Used if the method name starts with 'Create', 'Add', 'Insert' or 'Post'. |
|||
- **Patch**: Used if the method name starts with 'Patch'. |
|||
- Otherwise, **Post** is used **by default**. |
|||
|
|||
If you need to customize HTTP method for a particular method, then you can use one of the standard ASP.NET Core attributes ([HttpPost], [HttpGet], [HttpPut]... etc.). This requires to add [Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core) nuget package to your project that contains the service. |
|||
|
|||
### Route |
|||
|
|||
Route is calculated based on some conventions: |
|||
|
|||
* It always starts with '**/api**'. |
|||
* Continues with a **route path**. Default value is '**/app**' and can be configured as like below: |
|||
|
|||
````csharp |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.RootPath = "volosoft/book-store"; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
Then the route for getting a book will be '**/api/volosoft/book-store/book/{id}**'. This sample uses two-level root path, but you generally use a single level depth. |
|||
|
|||
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **kebab-case**. If your application service class name is 'ReadingBookAppService' then it becomes only '/reading-book'. |
|||
* If you want to customize naming, then set the `UrlControllerNameNormalizer` option. It's a func delegate which allows you to determine the name per controller/service. |
|||
* If the method has an '**id**' parameter then it adds '**/{id}**' ro the route. |
|||
* Then it adds the action name if necessary. Action name is obtained from the method name on the service and normalized by; |
|||
* Removing '**Async**' postfix. If the method name is 'GetPhonesAsync' then it becomes 'GetPhones'. |
|||
* Removing **HTTP method prefix**. 'GetList', 'GetAll', 'Get', 'Put', 'Update', 'Delete', 'Remove', 'Create', 'Add', 'Insert', 'Post' and 'Patch' prefixes are removed based on the selected HTTP method. So, 'GetPhones' becomes 'Phones' since 'Get' prefix is a duplicate for a GET request. |
|||
* Converting the result to **kebab-case**. |
|||
* If the resulting action name is **empty** then it's not added to the route. If it's not empty, it's added to the route (like '/phones'). For 'GetAllAsync' method name it will be empty, for 'GetPhonesAsync' method name it will be 'phones'. |
|||
* Normalization can be customized by setting the `UrlActionNameNormalizer` option. It's an action delegate that is called for every method. |
|||
* If there is another parameter with 'Id' postfix, then it's also added to the route as the final route segment (like '/phoneId'). |
|||
|
|||
#### Customizing the Route Calculation |
|||
|
|||
`IConventionalRouteBuilder` is used to build the route. It is implemented by the `ConventionalRouteBuilder` by default and works as explained above. You can replace/override this service to customize the route calculation strategy. |
|||
|
|||
#### Version 3.x Style Route Calculation |
|||
|
|||
The route calculation was different before the version 4.0. It was using camelCase conventions, while the ABP Framework version 4.0+ uses kebab-case. If you use the old route calculation strategy, follow one of the approaches; |
|||
|
|||
* Set `UseV3UrlStyle` to `true` in the options of the `options.ConventionalControllers.Create(...)` method. Example: |
|||
|
|||
````csharp |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.UseV3UrlStyle = true; |
|||
}); |
|||
```` |
|||
|
|||
This approach effects only the controllers for the `BookStoreApplicationModule`. |
|||
|
|||
* Set `UseV3UrlStyle` to `true` for the `AbpConventionalControllerOptions` to set it globally. Example: |
|||
|
|||
```csharp |
|||
Configure<AbpConventionalControllerOptions>(options => |
|||
{ |
|||
options.UseV3UrlStyle = true; |
|||
}); |
|||
``` |
|||
|
|||
Setting it globally effects all the modules in a modular application. |
|||
|
|||
## Service Selection |
|||
|
|||
Creating conventional HTTP API controllers are not unique to application services actually. |
|||
|
|||
### IRemoteService Interface |
|||
|
|||
If a class implements the `IRemoteService` interface then it's automatically selected to be a conventional API controller. Since application services inherently implement it, they are considered as natural API controllers. |
|||
|
|||
### RemoteService Attribute |
|||
|
|||
`RemoteService` attribute can be used to mark a class as a remote service or disable for a particular class that inherently implements the `IRemoteService` interface. Example: |
|||
|
|||
````csharp |
|||
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
### TypePredicate Option |
|||
|
|||
You can further filter classes to become an API controller by providing the `TypePredicate` option: |
|||
|
|||
````csharp |
|||
services.Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.TypePredicate = type => { return true; }; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
Instead of returning `true` for every type, you can check it and return `false` if you don't want to expose this type as an API controller. |
|||
|
|||
## API Explorer |
|||
|
|||
API Exploring a service that makes possible to investigate API structure by the clients. Swagger uses it to create a documentation and test UI for an endpoint. |
|||
|
|||
API Explorer is automatically enabled for conventional HTTP API controllers by default. Use `RemoteService` attribute to control it per class or method level. Example: |
|||
|
|||
````csharp |
|||
[RemoteService(IsMetadataEnabled = false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
Disabled `IsMetadataEnabled` which hides this service from API explorer and it will not be discoverable. However, it still can be usable for the clients know the exact API path/route. |
|||
|
|||
## Replace or Remove Controllers. |
|||
|
|||
In addition to [Overriding a Controller](../Customizing-Application-Modules-Overriding-Services.md#example-overriding-a-controller), you can also use a completely independent **Controller** to replace the controller in the framework or module. |
|||
|
|||
They have the same [route](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-7.0), but can have **different** input and output parameters. |
|||
|
|||
### Replace built-in AbpApplicationConfigurationController |
|||
|
|||
The `ReplaceControllersAttribute` indicates the replaced controller type. |
|||
|
|||
````csharp |
|||
[ReplaceControllers(typeof(AbpApplicationConfigurationController))] |
|||
[Area("abp")] |
|||
[RemoteService(Name = "abp")] |
|||
public class ReplaceBuiltInController : AbpController |
|||
{ |
|||
[HttpGet("api/abp/application-configuration")] |
|||
public virtual Task<MyApplicationConfigurationDto> GetAsync(MyApplicationConfigurationRequestOptions options) |
|||
{ |
|||
return Task.FromResult(new MyApplicationConfigurationDto()); |
|||
} |
|||
} |
|||
|
|||
public class MyApplicationConfigurationRequestOptions : ApplicationConfigurationRequestOptions |
|||
{ |
|||
|
|||
} |
|||
|
|||
public class MyApplicationConfigurationDto : ApplicationConfigurationDto |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
### Remove controller |
|||
|
|||
Configure `ControllersToRemove` of `AbpAspNetCoreMvcOptions` to remove the controllers. |
|||
|
|||
````csharp |
|||
services.Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ControllersToRemove.Add(typeof(AbpLanguagesController)); |
|||
}); |
|||
```` |
|||
## See Also |
|||
|
|||
* [Video tutorial](https://abp.io/video-courses/essentials/auto-api-controllers) |
|||
@ -1,209 +0,0 @@ |
|||
# Dynamic C# API Client Proxies |
|||
|
|||
ABP can dynamically create C# API client proxies to call your remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level details to call remote services and get results. |
|||
|
|||
Dynamic C# proxies automatically handle the following stuff for you; |
|||
|
|||
* Maps C# **method calls** to remote server **HTTP calls** by considering the HTTP method, route, query string parameters, request payload and other details. |
|||
* **Authenticates** the HTTP Client by adding access token to the HTTP header. |
|||
* **Serializes** to and deserialize from JSON. |
|||
* Handles HTTP API **versioning**. |
|||
* Add **correlation id**, current **tenant** id and the current **culture** to the request. |
|||
* Properly **handles the error messages** sent by the server and throws proper exceptions. |
|||
|
|||
This system can be used by any type of .NET client to consume your HTTP APIs. |
|||
|
|||
## Static vs Dynamic Client Proxies |
|||
|
|||
ABP provides **two types** of client proxy generation system. This document explains the **dynamic client proxies**, which generates client-side proxies on runtime. You can also see the [Static C# API Client Proxies](Static-CSharp-API-Clients.md) documentation to learn how to generate proxies on development time. |
|||
|
|||
Development-time (static) client proxy generation has a **performance advantage** since it doesn't need to obtain the HTTP API definition on runtime. However, you should **re-generate** the client proxy code whenever you change your API endpoint definition. On the other hand, dynamic client proxies are generated on runtime and provides an **easier development experience**. |
|||
|
|||
## Service Interface |
|||
|
|||
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project, typically in the `Application.Contracts` project if you've created your solution using the startup templates. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<List<BookDto>> GetListAsync(); |
|||
} |
|||
```` |
|||
|
|||
> Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition. |
|||
|
|||
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint. |
|||
|
|||
## Client Proxy Generation |
|||
|
|||
> The startup templates already comes pre-configured for the client proxy generation, in the `HttpApi.Client` project. |
|||
|
|||
If you're not using a startup template, then execute the following command in the folder that contains the .csproj file of your client project: |
|||
|
|||
```` |
|||
abp add-package Volo.Abp.Http.Client |
|||
```` |
|||
|
|||
> If you haven't done it yet, you first need to install the [ABP CLI](../CLI.md). For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Http.Client). |
|||
|
|||
Now, it's ready to create the client proxies. Example: |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), //used to create client proxies |
|||
typeof(BookStoreApplicationContractsModule) //contains the application service interfaces |
|||
)] |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//Create dynamic client proxies |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes. |
|||
|
|||
### Endpoint Configuration |
|||
|
|||
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. The simplest configuration is shown below: |
|||
|
|||
```json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
See the "AbpRemoteServiceOptions" section below for more detailed configuration. |
|||
|
|||
## Usage |
|||
|
|||
It's straightforward to use. Just inject the service interface in the client application code: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IBookAppService _bookService; |
|||
|
|||
public MyService(IBookAppService bookService) |
|||
{ |
|||
_bookService = bookService; |
|||
} |
|||
|
|||
public async Task DoIt() |
|||
{ |
|||
var books = await _bookService.GetListAsync(); |
|||
foreach (var book in books) |
|||
{ |
|||
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This sample injects the `IBookAppService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client. |
|||
|
|||
### IHttpClientProxy Interface |
|||
|
|||
While you can inject `IBookAppService` like above to use the client proxy, you could inject `IHttpClientProxy<IBookAppService>` for a more explicit usage. In this case you will use the `Service` property of the `IHttpClientProxy<T>` interface. |
|||
|
|||
## Configuration |
|||
|
|||
### AbpRemoteServiceOptions |
|||
|
|||
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can configure it in the `ConfigureServices` method of your [module](../Module-Development-Basics.md) to set or override it. Example: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<AbpRemoteServiceOptions>(options => |
|||
{ |
|||
options.RemoteServices.Default = |
|||
new RemoteServiceConfiguration("http://localhost:53929/"); |
|||
}); |
|||
|
|||
//... |
|||
} |
|||
```` |
|||
|
|||
### Multiple Remote Service Endpoints |
|||
|
|||
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file: |
|||
|
|||
````json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
}, |
|||
"BookStore": { |
|||
"BaseUrl": "http://localhost:48392/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientProxies` method can get an additional parameter for the remote service name. Example: |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly, |
|||
remoteServiceConfigurationName: "BookStore" |
|||
); |
|||
```` |
|||
|
|||
`remoteServiceConfigurationName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. |
|||
|
|||
### As Default Services |
|||
|
|||
When you create a service proxy for `IBookAppService`, you can directly inject the `IBookAppService` to use the proxy client (as shown in the usage section). You can pass `asDefaultServices: false` to the `AddHttpClientProxies` method to disable this feature. |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly, |
|||
asDefaultServices: false |
|||
); |
|||
```` |
|||
|
|||
Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy. |
|||
|
|||
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies. See the *IHttpClientProxy Interface* section above. |
|||
|
|||
### Retry/Failure Logic & Polly Integration |
|||
|
|||
If you want to add retry logic for the failing remote HTTP calls for the client proxies, you can configure the `AbpHttpClientBuilderOptions` in the `PreConfigureServices` method of your module class. |
|||
|
|||
**Example: Use the [Polly](https://github.com/App-vNext/Polly) library to re-try 3 times on a failure** |
|||
|
|||
````csharp |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpHttpClientBuilderOptions>(options => |
|||
{ |
|||
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) => |
|||
{ |
|||
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder => |
|||
policyBuilder.WaitAndRetryAsync( |
|||
3, |
|||
i => TimeSpan.FromSeconds(Math.Pow(2, i)) |
|||
) |
|||
); |
|||
}); |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
This example uses the [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly) package. You also need to import the `Polly` namespace (`using Polly;`) to be able to use the `WaitAndRetryAsync` method. |
|||
|
|||
## See Also |
|||
|
|||
* [Static C# Client Proxies](Static-CSharp-API-Clients.md) |
|||
@ -1,275 +0,0 @@ |
|||
# Static C# API Client Proxies |
|||
|
|||
ABP can create C# API client proxy code to call your remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level details to call remote services and get results. |
|||
|
|||
Static C# proxies automatically handle the following stuff for you; |
|||
|
|||
* Maps C# **method calls** to remote server **HTTP calls** by considering the HTTP method, route, query string parameters, request payload and other details. |
|||
* **Authenticates** the HTTP Client by adding access token to the HTTP header. |
|||
* **Serializes** to and deserialize from JSON. |
|||
* Handles HTTP API **versioning**. |
|||
* Add **correlation id**, current **tenant** id and the current **culture** to the request. |
|||
* Properly **handles the error messages** sent by the server and throws proper exceptions. |
|||
|
|||
This system can be used by any type of .NET client to consume your HTTP APIs. |
|||
|
|||
## Static vs Dynamic Client Proxies |
|||
|
|||
ABP provides **two types** of client proxy generation system. This document explains the **static client proxies**, which generates client-side code in your development time. You can also see the [Dynamic C# API Client Proxies](Dynamic-CSharp-API-Clients.md) documentation to learn how to use proxies generated on runtime. |
|||
|
|||
Development-time (static) client proxy generation has a **performance advantage** since it doesn't need to obtain the HTTP API definition on runtime. However, you should **re-generate** the client proxy code whenever you change your API endpoint definition. On the other hand, dynamic client proxies are generated on runtime and provides an **easier development experience**. |
|||
|
|||
## Service Interface |
|||
|
|||
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project, typically in the `Application.Contracts` project if you've created your solution using the startup templates. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<List<BookDto>> GetListAsync(); |
|||
} |
|||
```` |
|||
|
|||
> Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition. |
|||
|
|||
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint. |
|||
|
|||
## With Contracts or Without Contracts |
|||
|
|||
`Without Contracts` depending on target service's `application.contracts` package, so they can reuse the DTOs and other related classes. However, that can be a problem when we want to create fully independently developed and deployed microservices. We want to use the static proxy generation even without depending target service's application.contracts package. |
|||
|
|||
`With Contracts` generate all the `classes/enums/other` types in the client side (including application service interfaces) , This is also the default behavior of the `generate-proxy` command. |
|||
|
|||
## Client Proxy Generation |
|||
|
|||
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Http.Client |
|||
```` |
|||
|
|||
Then add `AbpHttpClientModule` dependency to your module: |
|||
|
|||
````csharp |
|||
[DependsOn(typeof(AbpHttpClientModule))] //add the dependency |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
Now, it's ready to configure the application for the static client proxy generation. |
|||
|
|||
### With Contracts Example |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), //used to create client proxies |
|||
typeof(AbpVirtualFileSystemModule) //virtual file system |
|||
)] |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Prepare for static client proxy generation |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(MyClientAppModule).Assembly |
|||
); |
|||
|
|||
// Include the generated app-generate-proxy.json in the virtual file system |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<MyClientAppModule>(); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### Without Contracts Example |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), //used to create client proxies |
|||
typeof(AbpVirtualFileSystemModule), //virtual file system |
|||
typeof(BookStoreApplicationContractsModule) //contains the application service interfaces |
|||
)] |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Prepare for static client proxy generation |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly |
|||
); |
|||
|
|||
// Include the generated app-generate-proxy.json in the virtual file system |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<MyClientAppModule>(); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddStaticHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, and prepares for static client proxy generation. |
|||
|
|||
> The [application startup template](../Startup-Templates/Application.md) comes pre-configured for the **dynamic** client proxy generation, in the `HttpApi.Client` project. If you want to switch to the **static** client proxies, change `context.Services.AddHttpClientProxies` to `context.Services.AddStaticHttpClientProxies` in the module class of your `HttpApi.Client` project. |
|||
|
|||
### Endpoint Configuration |
|||
|
|||
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. The simplest configuration is shown below: |
|||
|
|||
```json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
See the *AbpRemoteServiceOptions* section below for more detailed configuration. |
|||
|
|||
### Code Generation |
|||
|
|||
Server side must be up and running while generating the client proxy code. So, run your application that serves the HTTP APIs on the `BaseUrl` that is configured like explained in the *Endpoint Configuration* section. |
|||
|
|||
Open a command-line terminal in the root folder of your client project (`.csproj`) and type the following command: |
|||
|
|||
#### With Contracts |
|||
|
|||
````bash |
|||
abp generate-proxy -t csharp -u http://localhost:53929/ |
|||
```` |
|||
|
|||
> If you haven't installed yet, you should install the [ABP CLI](../CLI.md). |
|||
|
|||
This command should generate the following files under the `ClientProxies` folder: |
|||
|
|||
 |
|||
|
|||
* `BookClientProxy.Generated.cs` is the actual generated proxy class in this example. `BookClientProxy` is a `partial` class * where you can write your custom code (ABP won't override it). |
|||
* `IBookAppService.cs` is the app service. |
|||
* `BookDto.cs` is the Dto class which uses by app service. |
|||
* `app-generate-proxy.json` contains information about the remote HTTP endpoint, so ABP can properly perform HTTP requests. This file must be configured as an embedded resource in your project, so that it can be found by the virtual file system. |
|||
|
|||
#### Without Contracts |
|||
|
|||
````bash |
|||
abp generate-proxy -t csharp -u http://localhost:53929/ --without-contracts |
|||
```` |
|||
|
|||
This command should generate the following files under the `ClientProxies` folder: |
|||
|
|||
 |
|||
|
|||
* `BookClientProxy.Generated.cs` is the actual generated proxy class in this example. `BookClientProxy` is a `partial` class where you can write your custom code (ABP won't override it). |
|||
* `app-generate-proxy.json` contains information about the remote HTTP endpoint, so ABP can properly perform HTTP requests. This file must be configured as an embedded resource in your project, so that it can be found by the virtual file system. |
|||
|
|||
> `generate-proxy` command generates proxies for only the APIs you've defined in your application. If you are developing a modular application, you can specify the `-m` (or `--module`) parameter to specify the module you want to generate proxies. See the *generate-proxy* section in the [ABP CLI](../CLI.md) documentation for other options. |
|||
|
|||
## Usage |
|||
|
|||
It's straightforward to use the client proxies. Just inject the service interface in the client application code: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IBookAppService _bookService; |
|||
|
|||
public MyService(IBookAppService bookService) |
|||
{ |
|||
_bookService = bookService; |
|||
} |
|||
|
|||
public async Task DoItAsync() |
|||
{ |
|||
var books = await _bookService.GetListAsync(); |
|||
foreach (var book in books) |
|||
{ |
|||
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This sample injects the `IBookAppService` service interface defined above. The static client proxy implementation makes an HTTP call whenever a service method is called by the client. |
|||
|
|||
## Configuration |
|||
|
|||
### AbpRemoteServiceOptions |
|||
|
|||
`AbpRemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can configure it in the `ConfigureServices` method of your [module](../Module-Development-Basics.md) to set or override it. Example: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<AbpRemoteServiceOptions>(options => |
|||
{ |
|||
options.RemoteServices.Default = |
|||
new RemoteServiceConfiguration("http://localhost:53929/"); |
|||
}); |
|||
|
|||
//... |
|||
} |
|||
```` |
|||
|
|||
### Multiple Remote Service Endpoints |
|||
|
|||
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file: |
|||
|
|||
````json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
}, |
|||
"BookStore": { |
|||
"BaseUrl": "http://localhost:48392/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddStaticHttpClientProxies` method can get an additional parameter for the remote service name. Example: |
|||
|
|||
````csharp |
|||
context.Services.AddStaticHttpClientProxies( |
|||
typeof(BookStoreApplicationContractsModule).Assembly, |
|||
remoteServiceConfigurationName: "BookStore" |
|||
); |
|||
```` |
|||
|
|||
`remoteServiceConfigurationName` parameter matches the service endpoint configured via `AbpRemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. |
|||
|
|||
### Retry/Failure Logic & Polly Integration |
|||
|
|||
If you want to add retry logic for the failing remote HTTP calls for the client proxies, you can configure the `AbpHttpClientBuilderOptions` in the `PreConfigureServices` method of your module class. |
|||
|
|||
**Example: Use the [Polly](https://github.com/App-vNext/Polly) library to re-try 3 times on a failure** |
|||
|
|||
````csharp |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<AbpHttpClientBuilderOptions>(options => |
|||
{ |
|||
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) => |
|||
{ |
|||
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder => |
|||
policyBuilder.WaitAndRetryAsync( |
|||
3, |
|||
i => TimeSpan.FromSeconds(Math.Pow(2, i)) |
|||
) |
|||
); |
|||
}); |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
This example uses the [Microsoft.Extensions.Http.Polly](https://www.nuget.org/packages/Microsoft.Extensions.Http.Polly) package. You also need to import the `Polly` namespace (`using Polly;`) to be able to use the `WaitAndRetryAsync` method. |
|||
|
|||
## See Also |
|||
|
|||
* [Dynamic C# Client Proxies](Dynamic-CSharp-API-Clients.md) |
|||
@ -1,194 +0,0 @@ |
|||
# Swagger Integration |
|||
|
|||
[Swagger (OpenAPI)](https://swagger.io/) is a language-agnostic specification for describing REST APIs. It allows both computers and humans to understand the capabilities of a REST API without direct access to the source code. Its main goals are to: |
|||
|
|||
- Minimize the amount of work needed to connect decoupled services. |
|||
- Reduce the amount of time needed to accurately document a service. |
|||
|
|||
ABP Framework offers a prebuilt module for full Swagger integration with small configurations. |
|||
|
|||
## Installation |
|||
|
|||
> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. |
|||
|
|||
If installation is needed, it is suggested to use the [ABP CLI](../CLI.md) to install this package. |
|||
|
|||
### Using the ABP CLI |
|||
|
|||
Open a command line window in the folder of the `Web` or `HttpApi.Host` project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.Swashbuckle |
|||
``` |
|||
|
|||
> If you haven't done it yet, you first need to install the [ABP CLI](../CLI.md). For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Swashbuckle). |
|||
|
|||
### Manual Installation |
|||
|
|||
If you want to manually install; |
|||
|
|||
1. Add the [Volo.Abp.Swashbuckle](https://www.nuget.org/packages/Volo.Abp.Swashbuckle) NuGet package to your `Web` or `HttpApi.Host` project: |
|||
|
|||
`Install-Package Volo.Abp.Swashbuckle` |
|||
|
|||
2. Add the `AbpSwashbuckleModule` to the dependency list of your module: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
//...other dependencies |
|||
typeof(AbpSwashbuckleModule) // <-- Add module dependency like that |
|||
)] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
## Configuration |
|||
|
|||
First, we need to use `AddAbpSwaggerGen` extension to configure Swagger in `ConfigureServices` method of our module: |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var services = context.Services; |
|||
|
|||
//... other configurations. |
|||
|
|||
services.AddAbpSwaggerGen( |
|||
options => |
|||
{ |
|||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" }); |
|||
options.DocInclusionPredicate((docName, description) => true); |
|||
options.CustomSchemaIds(type => type.FullName); |
|||
} |
|||
); |
|||
} |
|||
``` |
|||
|
|||
Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module: |
|||
|
|||
```csharp |
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
//... other configurations. |
|||
|
|||
app.UseStaticFiles(); |
|||
|
|||
app.UseSwagger(); |
|||
|
|||
app.UseAbpSwaggerUI(options => |
|||
{ |
|||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API"); |
|||
}); |
|||
|
|||
//... other configurations. |
|||
} |
|||
``` |
|||
|
|||
### Hide ABP Endpoints on Swagger UI |
|||
|
|||
If you want to hide ABP's default endpoints, call the `HideAbpEndpoints` method in your Swagger configuration as shown in the following example: |
|||
|
|||
```csharp |
|||
services.AddAbpSwaggerGen( |
|||
options => |
|||
{ |
|||
//... other options |
|||
|
|||
//Hides ABP Related endpoints on Swagger UI |
|||
options.HideAbpEndpoints(); |
|||
} |
|||
) |
|||
``` |
|||
|
|||
## Using Swagger with OAUTH |
|||
|
|||
For non MVC/Tiered applications, we need to configure Swagger with OAUTH to handle authorization. |
|||
|
|||
> ABP Framework uses OpenIddict by default. To get more information about OpenIddict, check this [documentation](../Modules/OpenIddict.md). |
|||
|
|||
To do that, we need to use `AddAbpSwaggerGenWithOAuth` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module: |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var services = contex.Services; |
|||
|
|||
//... other configarations. |
|||
|
|||
services.AddAbpSwaggerGenWithOAuth( |
|||
"https://localhost:44341", // authority issuer |
|||
new Dictionary<string, string> // |
|||
{ // scopes |
|||
{"Test", "Test API"} // |
|||
}, // |
|||
options => |
|||
{ |
|||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" }); |
|||
options.DocInclusionPredicate((docName, description) => true); |
|||
options.CustomSchemaIds(type => type.FullName); |
|||
} |
|||
); |
|||
} |
|||
``` |
|||
|
|||
Then we can use Swagger UI by calling `UseAbpSwaggerUI` method in the `OnApplicationInitialization` method of our module: |
|||
|
|||
```csharp |
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
//... other configurations. |
|||
|
|||
app.UseAbpSwaggerUI(options => |
|||
{ |
|||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API"); |
|||
|
|||
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>(); |
|||
options.OAuthClientId("Test_Swagger"); // clientId |
|||
options.OAuthClientSecret("1q2w3e*"); // clientSecret |
|||
}); |
|||
|
|||
//... other configurations. |
|||
} |
|||
``` |
|||
|
|||
> Do not forget to set `OAuthClientId` and `OAuthClientSecret`. |
|||
|
|||
## Using Swagger with OIDC |
|||
|
|||
You may also want to configure swagger using **OpenIdConnect** instead of OAUTH. This is especially useful when you need to configure different metadata address than the issuer in cases such as when you deploy your application to kubernetes cluster or docker. In these cases, metadata address will be used in sign-in process to reach the valid authentication server discovery endpoint over the internet and use the internal network to validate the obtained token. |
|||
|
|||
To do that, we need to use `AddAbpSwaggerGenWithOidc` extension to configure Swagger with OAuth issuer and scopes in `ConfigureServices` method of our module: |
|||
|
|||
```csharp |
|||
context.Services.AddAbpSwaggerGenWithOidc( |
|||
configuration["AuthServer:Authority"], |
|||
scopes: new[] { "SwaggerDemo" }, |
|||
// "authorization_code" |
|||
flows: new[] { AbpSwaggerOidcFlows.AuthorizationCode }, |
|||
// When deployed on K8s, should be metadata URL of the reachable DNS over internet like https://myauthserver.company.com |
|||
discoveryEndpoint: configuration["AuthServer:Authority"], |
|||
options => |
|||
{ |
|||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerDemo API", Version = "v1" }); |
|||
options.DocInclusionPredicate((docName, description) => true); |
|||
options.CustomSchemaIds(type => type.FullName); |
|||
}); |
|||
``` |
|||
|
|||
The `flows` is a list of default oidc flows that is supported by the oidc-provider (authserver). You can see the default supported flows below: |
|||
|
|||
- `AbpSwaggerOidcFlows.AuthorizationCode`: The `"authorization_code"` flow is the **default and suggested** flow. **Doesn't require a client secret** when even there is a field for it. |
|||
- `AbpSwaggerOidcFlows.Implicit`: The deprecated `"implicit"` flow that was used for javascript applications. |
|||
- `AbpSwaggerOidcFlows.Password`: The legacy `password` flow which is also known as Resource Ownder Password flow. You need to provide a user name, password and client secret for it. |
|||
- `AbpSwaggerOidcFlows.ClientCredentials`: The `"client_credentials"` flow that is used for server to server interactions. |
|||
|
|||
You can define one or many flows which will be shown in the Authorize modal. You can set it **null which will use the default "authorization_code"** flow. |
|||
|
|||
The `discoveryEndpoint` is the reachable openid-provider endpoint for the `.well-known/openid-configuration`. You can set it to **null which will use default AuthServer:Authority** appsettings configuration. If you are deploying your applications to a kubernetes cluster or docker swarm, you should to set the `discoveryEndpoint` as real DNS that should be reachable over the internet. |
|||
|
|||
> If are having problems with seeing the authorization modal, check the browser console logs and make sure you have a correct and reachable `discoveryEndpoint` |
|||
@ -1,3 +0,0 @@ |
|||
## Ambient Context Pattern |
|||
|
|||
TODO |
|||
@ -1,568 +0,0 @@ |
|||
# Application Services |
|||
|
|||
Application services are used to implement the **use cases** of an application. They are used to **expose domain logic to the presentation layer**. |
|||
|
|||
An Application Service is called from the presentation layer (optionally) with a **DTO ([Data Transfer Object](Data-Transfer-Objects.md))** as the parameter. It uses domain objects to **perform some specific business logic** and (optionally) returns a DTO back to the presentation layer. Thus, the presentation layer is completely **isolated** from domain layer. |
|||
|
|||
## Example |
|||
|
|||
### Book Entity |
|||
|
|||
Assume that you have a `Book` entity (actually, an aggregate root) defined as shown below: |
|||
|
|||
````csharp |
|||
public class Book : AggregateRoot<Guid> |
|||
{ |
|||
public const int MaxNameLength = 128; |
|||
|
|||
public virtual string Name { get; protected set; } |
|||
|
|||
public virtual BookType Type { get; set; } |
|||
|
|||
public virtual float? Price { get; set; } |
|||
|
|||
protected Book() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public Book(Guid id, [NotNull] string name, BookType type, float? price = 0) |
|||
{ |
|||
Id = id; |
|||
Name = CheckName(name); |
|||
Type = type; |
|||
Price = price; |
|||
} |
|||
|
|||
public virtual void ChangeName([NotNull] string name) |
|||
{ |
|||
Name = CheckName(name); |
|||
} |
|||
|
|||
private static string CheckName(string name) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(name)) |
|||
{ |
|||
throw new ArgumentException( |
|||
$"name can not be empty or white space!"); |
|||
} |
|||
|
|||
if (name.Length > MaxNameLength) |
|||
{ |
|||
throw new ArgumentException( |
|||
$"name can not be longer than {MaxNameLength} chars!"); |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `Book` entity has a `MaxNameLength` that defines the maximum length of the `Name` property. |
|||
* `Book` constructor and `ChangeName` method to ensure that the `Name` is always a valid value. Notice that `Name`'s setter is not `public`. |
|||
|
|||
> ABP does not force you to design your entities like that. It just can have public get/set for all properties. It's your decision to fully implement DDD practices. |
|||
|
|||
### IBookAppService Interface |
|||
|
|||
In ABP, an application service should implement the `IApplicationService` interface. It's good to create an interface for each application service: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task CreateAsync(CreateBookDto input); |
|||
} |
|||
```` |
|||
|
|||
A Create method will be implemented as the example. `CreateBookDto` is defined like that: |
|||
|
|||
````csharp |
|||
public class CreateBookDto |
|||
{ |
|||
[Required] |
|||
[StringLength(Book.MaxNameLength)] |
|||
public string Name { get; set; } |
|||
|
|||
public BookType Type { get; set; } |
|||
|
|||
public float? Price { get; set; } |
|||
} |
|||
```` |
|||
|
|||
> See [data transfer objects document](Data-Transfer-Objects.md) for more about DTOs. |
|||
|
|||
### BookAppService (Implementation) |
|||
|
|||
````csharp |
|||
public class BookAppService : ApplicationService, IBookAppService |
|||
{ |
|||
private readonly IRepository<Book, Guid> _bookRepository; |
|||
|
|||
public BookAppService(IRepository<Book, Guid> bookRepository) |
|||
{ |
|||
_bookRepository = bookRepository; |
|||
} |
|||
|
|||
public async Task CreateAsync(CreateBookDto input) |
|||
{ |
|||
var book = new Book( |
|||
GuidGenerator.Create(), |
|||
input.Name, |
|||
input.Type, |
|||
input.Price |
|||
); |
|||
|
|||
await _bookRepository.InsertAsync(book); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `BookAppService` inherits from the `ApplicationService` base class. It's not required, but the `ApplicationService` class provides helpful properties for common application service requirements like `GuidGenerator` used in this service. If we didn't inherit from it, we would need to inject the `IGuidGenerator` service manually (see [guid generation](Guid-Generation.md) document). |
|||
* `BookAppService` implements the `IBookAppService` as expected. |
|||
* `BookAppService` [injects](Dependency-Injection.md) `IRepository<Book, Guid>` (see [repositories](Repositories.md)) and uses it inside the `CreateAsync` method to insert a new entity to the database. |
|||
* `CreateAsync` uses the constructor of the `Book` entity to create a new book from the properties of given `input`. |
|||
|
|||
## Data Transfer Objects |
|||
|
|||
Application services get and return DTOs instead of entities. ABP does not force this rule. However, exposing entities to the presentation layer (or to remote clients) has significant problems and is not suggested. |
|||
|
|||
See the [DTO documentation](Data-Transfer-Objects.md) for more. |
|||
|
|||
## Object to Object Mapping |
|||
|
|||
The `CreateAsync` method above manually creates a `Book` entity from given `CreateBookDto` object, because the `Book` entity enforces it (we designed it like that). |
|||
|
|||
However, in many cases, it's very practical to use **auto object mapping** to set properties of an object from a similar object. ABP provides an [object to object mapping](Object-To-Object-Mapping.md) infrastructure to make this even easier. |
|||
|
|||
Object to object mapping provides abstractions and it is implemented by the [AutoMapper](https://automapper.org/) library by default. |
|||
|
|||
Let's create another method to get a book. First, define the method in the `IBookAppService` interface: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task CreateAsync(CreateBookDto input); |
|||
|
|||
Task<BookDto> GetAsync(Guid id); //New method |
|||
} |
|||
```` |
|||
|
|||
`BookDto` is a simple [DTO](Data-Transfer-Objects.md) class defined as below: |
|||
|
|||
````csharp |
|||
public class BookDto |
|||
{ |
|||
public Guid Id { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public BookType Type { get; set; } |
|||
|
|||
public float? Price { get; set; } |
|||
} |
|||
```` |
|||
|
|||
AutoMapper requires to create a mapping [profile class](https://docs.automapper.org/en/stable/Configuration.html#profile-instances). Example: |
|||
|
|||
````csharp |
|||
public class MyProfile : Profile |
|||
{ |
|||
public MyProfile() |
|||
{ |
|||
CreateMap<Book, BookDto>(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
You should then register profiles using the `AbpAutoMapperOptions`: |
|||
|
|||
````csharp |
|||
[DependsOn(typeof(AbpAutoMapperModule))] |
|||
public class MyModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
//Add all mappings defined in the assembly of the MyModule class |
|||
options.AddMaps<MyModule>(); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddMaps` registers all profile classes defined in the assembly of the given class, typically your module class. It also registers for the [attribute mapping](https://docs.automapper.org/en/stable/Attribute-mapping.html). |
|||
|
|||
Then you can implement the `GetAsync` method as shown below: |
|||
|
|||
````csharp |
|||
public async Task<BookDto> GetAsync(Guid id) |
|||
{ |
|||
var book = await _bookRepository.GetAsync(id); |
|||
return ObjectMapper.Map<Book, BookDto>(book); |
|||
} |
|||
```` |
|||
|
|||
See the [object to object mapping document](Object-To-Object-Mapping.md) for more. |
|||
|
|||
## Validation |
|||
|
|||
Inputs of application service methods are automatically validated (like ASP.NET Core controller actions). You can use the standard data annotation attributes or a custom validation method to perform the validation. ABP also ensures that the input is not null. |
|||
|
|||
See the [validation document](Validation.md) for more. |
|||
|
|||
## Authorization |
|||
|
|||
It's possible to use declarative and imperative authorization for application service methods. |
|||
|
|||
See the [authorization document](Authorization.md) for more. |
|||
|
|||
## CRUD Application Services |
|||
|
|||
If you need to create a simple **CRUD application service** which has Create, Update, Delete and Get methods, you can use ABP's **base classes** to easily build your services. You can inherit from the `CrudAppService`. |
|||
|
|||
### Example |
|||
|
|||
Create an `IBookAppService` interface inheriting from the `ICrudAppService` interface. |
|||
|
|||
````csharp |
|||
public interface IBookAppService : |
|||
ICrudAppService< //Defines CRUD methods |
|||
BookDto, //Used to show books |
|||
Guid, //Primary key of the book entity |
|||
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books |
|||
CreateUpdateBookDto, //Used to create a new book |
|||
CreateUpdateBookDto> //Used to update a book |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
`ICrudAppService` has generic arguments to get the primary key type of the entity and the DTO types for the CRUD operations (it does not get the entity type since the entity type is not exposed to the clients use this interface). |
|||
|
|||
> Creating an interface for an application service is good practice, but not required by the ABP Framework. You can skip the interface part. |
|||
|
|||
`ICrudAppService` declares the following methods: |
|||
|
|||
````csharp |
|||
public interface ICrudAppService< |
|||
TEntityDto, |
|||
in TKey, |
|||
in TGetListInput, |
|||
in TCreateInput, |
|||
in TUpdateInput> |
|||
: IApplicationService |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
Task<TEntityDto> GetAsync(TKey id); |
|||
|
|||
Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input); |
|||
|
|||
Task<TEntityDto> CreateAsync(TCreateInput input); |
|||
|
|||
Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input); |
|||
|
|||
Task DeleteAsync(TKey id); |
|||
} |
|||
```` |
|||
|
|||
DTO classes used in this example are `BookDto` and `CreateUpdateBookDto`: |
|||
|
|||
````csharp |
|||
public class BookDto : AuditedEntityDto<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public BookType Type { get; set; } |
|||
|
|||
public float Price { get; set; } |
|||
} |
|||
|
|||
public class CreateUpdateBookDto |
|||
{ |
|||
[Required] |
|||
[StringLength(128)] |
|||
public string Name { get; set; } |
|||
|
|||
[Required] |
|||
public BookType Type { get; set; } = BookType.Undefined; |
|||
|
|||
[Required] |
|||
public float Price { get; set; } |
|||
} |
|||
```` |
|||
|
|||
[Profile](https://docs.automapper.org/en/stable/Configuration.html#profile-instances) class of DTO class. |
|||
|
|||
```csharp |
|||
public class MyProfile : Profile |
|||
{ |
|||
public MyProfile() |
|||
{ |
|||
CreateMap<Book, BookDto>(); |
|||
CreateMap<CreateUpdateBookDto, Book>(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* `CreateUpdateBookDto` is shared by create and update operations, but you could use separated DTO classes as well. |
|||
|
|||
And finally, the `BookAppService` implementation is very simple: |
|||
|
|||
````csharp |
|||
public class BookAppService : |
|||
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, |
|||
CreateUpdateBookDto, CreateUpdateBookDto>, |
|||
IBookAppService |
|||
{ |
|||
public BookAppService(IRepository<Book, Guid> repository) |
|||
: base(repository) |
|||
{ |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`CrudAppService` implements all methods declared in the `ICrudAppService` interface. You can then add your own custom methods or override and customize base methods. |
|||
|
|||
> `CrudAppService` has different versions gets different number of generic arguments. Use the one suitable for you. |
|||
|
|||
### AbstractKeyCrudAppService |
|||
|
|||
`CrudAppService` requires to have an Id property as the primary key of your entity. If you are using composite keys then you can not utilize it. |
|||
|
|||
`AbstractKeyCrudAppService` implements the same `ICrudAppService` interface, but this time without making assumption about your primary key. |
|||
|
|||
#### Example |
|||
|
|||
Assume that you have a `District` entity with `CityId` and `Name` as a composite primary key. Using `AbstractKeyCrudAppService` requires to implement `DeleteByIdAsync` and `GetEntityByIdAsync` methods yourself: |
|||
|
|||
````csharp |
|||
public class DistrictAppService |
|||
: AbstractKeyCrudAppService<District, DistrictDto, DistrictKey> |
|||
{ |
|||
public DistrictAppService(IRepository<District> repository) |
|||
: base(repository) |
|||
{ |
|||
} |
|||
|
|||
protected async override Task DeleteByIdAsync(DistrictKey id) |
|||
{ |
|||
await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name); |
|||
} |
|||
|
|||
protected async override Task<District> GetEntityByIdAsync(DistrictKey id) |
|||
{ |
|||
var queryable = await Repository.GetQueryableAsync(); |
|||
return await AsyncQueryableExecuter.FirstOrDefaultAsync( |
|||
queryable.Where(d => d.CityId == id.CityId && d.Name == id.Name) |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This implementation requires you to create a class that represents your composite key: |
|||
|
|||
````csharp |
|||
public class DistrictKey |
|||
{ |
|||
public Guid CityId { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
} |
|||
```` |
|||
|
|||
### Authorization (for CRUD App Services) |
|||
|
|||
There are two ways of authorizing the base application service methods; |
|||
|
|||
1. You can set the policy properties (xxxPolicyName) in the constructor of your service. Example: |
|||
|
|||
```csharp |
|||
public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid> |
|||
{ |
|||
public MyPeopleAppService(IRepository<Person, Guid> repository) |
|||
: base(repository) |
|||
{ |
|||
GetPolicyName = "..."; |
|||
GetListPolicyName = "..."; |
|||
CreatePolicyName = "..."; |
|||
UpdatePolicyName = "..."; |
|||
DeletePolicyName = "..."; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
`CreatePolicyName` is checked by the `CreateAsync` method and so on... You should specify a policy (permission) name defined in your application. |
|||
|
|||
2. You can override the check methods (CheckXxxPolicyAsync) in your service. Example: |
|||
|
|||
```csharp |
|||
public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid> |
|||
{ |
|||
public MyPeopleAppService(IRepository<Person, Guid> repository) |
|||
: base(repository) |
|||
{ |
|||
} |
|||
|
|||
protected async override Task CheckDeletePolicyAsync() |
|||
{ |
|||
await AuthorizationService.CheckAsync("..."); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
You can perform any logic in the `CheckDeletePolicyAsync` method. It is expected to throw an `AbpAuthorizationException` in any unauthorized case, like `AuthorizationService.CheckAsync` already does. |
|||
|
|||
### Base Properties & Methods |
|||
|
|||
CRUD application service base class provides many useful base methods that **you can override** to customize it based on your requirements. |
|||
|
|||
#### CRUD Methods |
|||
|
|||
These are the essential CRUD methods. You can override any of them to completely customize the operation. Here, the definitions of the methods: |
|||
|
|||
````csharp |
|||
Task<TGetOutputDto> GetAsync(TKey id); |
|||
Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input); |
|||
Task<TGetOutputDto> CreateAsync(TCreateInput input); |
|||
Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input); |
|||
Task DeleteAsync(TKey id); |
|||
```` |
|||
|
|||
#### Querying |
|||
|
|||
These methods are low level methods that can control how to query entities from the database. |
|||
|
|||
* `CreateFilteredQuery` can be overridden to create an `IQueryable<TEntity>` that is filtered by the given input. If your `TGetListInput` class contains any filter, it is proper to override this method and filter the query. It returns the (unfiltered) repository (which is already `IQueryable<TEntity>`) by default. |
|||
* `ApplyPaging` is used to make paging on the query. If your `TGetListInput` already implements `IPagedResultRequest`, you don't need to override this since the ABP Framework automatically understands it and performs the paging. |
|||
* `ApplySorting` is used to sort (order by...) the query. If your `TGetListInput` already implements the `ISortedResultRequest`, ABP Framework automatically sorts the query. If not, it fallbacks to the `ApplyDefaultSorting` which tries to sort by creation time, if your entity implements the standard `IHasCreationTime` interface. |
|||
* `GetEntityByIdAsync` is used to get an entity by id, which calls `Repository.GetAsync(id)` by default. |
|||
* `DeleteByIdAsync` is used to delete an entity by id, which calls `Repository.DeleteAsync(id)` by default. |
|||
|
|||
#### Object to Object Mapping |
|||
|
|||
These methods are used to convert Entities to DTOs and vice verse. They use the [IObjectMapper](Object-To-Object-Mapping.md) by default. |
|||
|
|||
* `MapToGetOutputDtoAsync` is used to map the entity to the DTO returned from the `GetAsync`, `CreateAsync` and `UpdateAsync` methods. Alternatively, you can override the `MapToGetOutputDto` if you don't need to perform any async operation. |
|||
* `MapToGetListOutputDtosAsync` is used to map a list of entities to a list of DTOs returned from the `GetListAsync` method. It uses the `MapToGetListOutputDtoAsync` to map each entity in the list. You can override one of them based on your case. Alternatively, you can override the `MapToGetListOutputDto` if you don't need to perform any async operation. |
|||
* `MapToEntityAsync` method has two overloads; |
|||
* `MapToEntityAsync(TCreateInput)` is used to create an entity from `TCreateInput`. |
|||
* `MapToEntityAsync(TUpdateInput, TEntity)` is used to update an existing entity from `TUpdateInput`. |
|||
|
|||
## Miscellaneous |
|||
|
|||
### Working with Streams |
|||
|
|||
`Stream` object itself is not serializable. So, you may have problems if you directly use `Stream` as the parameter or the return value for your application service. ABP Framework provides a special type, `IRemoteStreamContent` to be used to get or return streams in the application services. |
|||
|
|||
**Example: Application Service Interface that can be used to get and return streams** |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Content; |
|||
|
|||
namespace MyProject.Test |
|||
{ |
|||
public interface ITestAppService : IApplicationService |
|||
{ |
|||
Task Upload(Guid id, IRemoteStreamContent streamContent); |
|||
Task<IRemoteStreamContent> Download(Guid id); |
|||
|
|||
Task CreateFile(CreateFileInput input); |
|||
Task CreateMultipleFile(CreateMultipleFileInput input); |
|||
} |
|||
|
|||
public class CreateFileInput |
|||
{ |
|||
public Guid Id { get; set; } |
|||
|
|||
public IRemoteStreamContent Content { get; set; } |
|||
} |
|||
|
|||
public class CreateMultipleFileInput |
|||
{ |
|||
public Guid Id { get; set; } |
|||
|
|||
public IEnumerable<IRemoteStreamContent> Contents { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
**You need to configure `AbpAspNetCoreMvcOptions` to add DTO class to `FormBodyBindingIgnoredTypes` to use `IRemoteStreamContent` in** **DTO ([Data Transfer Object](Data-Transfer-Objects.md))** |
|||
|
|||
````csharp |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateFileInput)); |
|||
options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateMultipleFileInput)); |
|||
}); |
|||
```` |
|||
|
|||
**Example: Application Service Implementation that can be used to get and return streams** |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Content; |
|||
|
|||
namespace MyProject.Test |
|||
{ |
|||
public class TestAppService : ApplicationService, ITestAppService |
|||
{ |
|||
public Task<IRemoteStreamContent> Download(Guid id) |
|||
{ |
|||
var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.OpenOrCreate); |
|||
return Task.FromResult( |
|||
(IRemoteStreamContent) new RemoteStreamContent(fs) { |
|||
ContentType = "application/octet-stream" |
|||
} |
|||
); |
|||
} |
|||
|
|||
public async Task Upload(Guid id, IRemoteStreamContent streamContent) |
|||
{ |
|||
using (var fs = new FileStream("C:\\Temp\\" + id + ".blob", FileMode.Create)) |
|||
{ |
|||
await streamContent.GetStream().CopyToAsync(fs); |
|||
await fs.FlushAsync(); |
|||
} |
|||
} |
|||
|
|||
public async Task CreateFileAsync(CreateFileInput input) |
|||
{ |
|||
using (var fs = new FileStream("C:\\Temp\\" + input.Id + ".blob", FileMode.Create)) |
|||
{ |
|||
await input.Content.GetStream().CopyToAsync(fs); |
|||
await fs.FlushAsync(); |
|||
} |
|||
} |
|||
|
|||
public async Task CreateMultipleFileAsync(CreateMultipleFileInput input) |
|||
{ |
|||
using (var fs = new FileStream("C:\\Temp\\" + input.Id + ".blob", FileMode.Append)) |
|||
{ |
|||
foreach (var content in input.Contents) |
|||
{ |
|||
await content.GetStream().CopyToAsync(fs); |
|||
} |
|||
await fs.FlushAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`IRemoteStreamContent` is compatible with the [Auto API Controller](API/Auto-API-Controllers.md) and [Dynamic C# HTTP Proxy](API/Dynamic-CSharp-API-Clients.md) systems. |
|||
|
|||
## Lifetime |
|||
|
|||
Lifetime of application services are [transient](Dependency-Injection.md) and they are automatically registered to the dependency injection system. |
|||
|
|||
## See Also |
|||
|
|||
* [Video tutorial Part-1](https://abp.io/video-courses/essentials/application-services-part-1) |
|||
* [Video tutorial Part-2](https://abp.io/video-courses/essentials/application-services-part-2) |
|||
@ -1,320 +0,0 @@ |
|||
## ABP Application Startup |
|||
|
|||
You typically use the [ABP CLI](CLI.md)'s `abp new` command to [get started](Getting-Started.md) with one of the pre-built [startup solution templates](Startup-Templates/Index.md). When you do that, you generally don't need to know the details of how the ABP Framework is integrated with your application or how it is configured and initialized. The startup template also comes with the fundamental ABP packages and [application modules](Modules/Index) are pre-installed and configured for you. |
|||
|
|||
> It is always suggested to [get started with a startup template](Getting-Started.md) and modify it for your requirements. Read this document only if you want to understand the details or if you need to modify how the ABP Framework starts. |
|||
|
|||
While the ABP Framework has a lot of features and integrations, it is built as a lightweight and modular framework. It consists of [hundreds of NuGet and NPM packages](https://abp.io/packages), so you can only use the features you need. If you follow the [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document, you'll see how easy it is to install the ABP Framework into an empty ASP.NET Core project from scratch. You only need to install a single NuGet package and make a few small changes. |
|||
|
|||
This document is for who wants to better understand how the ABP Framework is initialized and configured on startup. |
|||
|
|||
## Installing to a Console Application |
|||
|
|||
A .NET Console application is the minimalist .NET application. So, it is best to show the installing of the ABP Framework to a console application as a minimalist example. |
|||
|
|||
If you [create a new console application with Visual Studio](https://learn.microsoft.com/en-us/dotnet/core/tutorials/with-visual-studio) (for .NET 8.0 or later), you will see the following solution structure (I named the solution as `MyConsoleDemo`): |
|||
|
|||
 |
|||
|
|||
This example uses the [top level statements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top-level-statements), so it consists of only a single line of code. |
|||
|
|||
The first step is to install the [Volo.Abp.Core](https://www.nuget.org/packages/Volo.Abp.Core) NuGet package, which is the most core NuGet package of the ABP framework. You can install it using the ABP CLI. Execute the following command in the folder of the .csproj file that you want to install the package on: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.Core |
|||
```` |
|||
|
|||
> If you haven't done it yet, you first need to install the [ABP CLI](CLI.md). For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Core). |
|||
|
|||
Alternatively, you can use a command-line terminal in the root folder of the project (the folder containing the `MyConsoleDemo.csproj` file, for this example): |
|||
|
|||
````bash |
|||
dotnet add package Volo.Abp.Core |
|||
```` |
|||
|
|||
After adding the NuGet package, we should create a root [module class](Module-Development-Basics.md) for our application. We can create the following class in the project: |
|||
|
|||
````csharp |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace MyConsoleDemo |
|||
{ |
|||
public class MyConsoleDemoModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This is an empty class deriving from the `AbpModule` class. It is the main class that you will control your application's dependencies with, and implement your configuration and startup/shutdown logic. For more information, please check the [Modularity](Module-Development-Basics.md) document. |
|||
|
|||
As the second and the last step, change the `Program.cs` as shown in the following code block: |
|||
|
|||
````csharp |
|||
using MyConsoleDemo; |
|||
using Volo.Abp; |
|||
|
|||
// 1: Create the ABP application container |
|||
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>(); |
|||
|
|||
// 2: Initialize/start the ABP Framework (and all the modules) |
|||
await application.InitializeAsync(); |
|||
|
|||
Console.WriteLine("ABP Framework has been started..."); |
|||
|
|||
// 3: Stop the ABP Framework (and all the modules) |
|||
await application.ShutdownAsync(); |
|||
```` |
|||
|
|||
That's all. Now, ABP Framework is installed, integrated, started and stopped in your application. From now, you can install [ABP packages](https://abp.io/packages) to your application whenever you need them. |
|||
|
|||
## Installing a Framework Package |
|||
|
|||
If you want to send emails from your .NET application, you can use .NET's standard [SmtpClient class](https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient). ABP also provides an `IEmailSender` service that simplifies [sending emails](Emailing.md) and configuring the email settings in a central place. If you want to use it, you should install the [Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing) NuGet package to your project: |
|||
|
|||
````bash |
|||
dotnet add package Volo.Abp.Emailing |
|||
```` |
|||
|
|||
Once you add a new ABP package/module, you also need to specify the module dependency from your module class. So, change the `MyConsoleDemoModule` class as shown below: |
|||
|
|||
````csharp |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace MyConsoleDemo |
|||
{ |
|||
[DependsOn(typeof(AbpEmailingModule))] // Added the module dependency |
|||
public class MyConsoleDemoModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
```` |
|||
|
|||
I've just added a `[DependsOn]` attribute to declare that I want to use the ABP Emailing Module (`AbpEmailingModule`). Now, I can use the `IEmailSender` service in my `Program.cs`: |
|||
|
|||
````csharp |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using MyConsoleDemo; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Emailing; |
|||
|
|||
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>(); |
|||
await application.InitializeAsync(); |
|||
|
|||
// Sending emails using the IEmailSender service |
|||
var emailsender = application.ServiceProvider.GetRequiredService<IEmailSender>(); |
|||
await emailsender.SendAsync( |
|||
to: "info@acme.com", |
|||
subject: "Hello World", |
|||
body: "My message body..." |
|||
); |
|||
|
|||
await application.ShutdownAsync(); |
|||
```` |
|||
|
|||
> If you run that application, you get a runtime error indicating that the email sending settings haven't been done yet. You can check the [Email Sending document](Emailing.md) to learn how to configure it. |
|||
|
|||
That's all. Install an ABP NuGet package, add the module dependency (using the `[DependsOn]` attribute) and use any service inside the NuGet package. |
|||
|
|||
The [ABP CLI](CLI.md) already has a special command to perform the addition of an ABP NuGet and also adding the `[DependsOn]` attribute to your module class for you with a single command: |
|||
|
|||
````bash |
|||
abp add-package Volo.Abp.Emailing |
|||
```` |
|||
|
|||
We suggest you to use the `abp add-package` command instead of manually doing it. |
|||
|
|||
## AbpApplicationFactory |
|||
|
|||
`AbpApplicationFactory` is the main class that creates an ABP application container. It provides a single static `CreateAsync` (and `Create` if you can't use asynchronous programming) method with multiple overloads. Let's investigate these overloads to understand where you can use them. |
|||
|
|||
The first overload gets a generic module class parameter as we've used before in this document: |
|||
|
|||
````csharp |
|||
AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>(); |
|||
```` |
|||
|
|||
The generic class parameter should be the root module class of your application. All the other modules are resolved as dependencies of that module. |
|||
|
|||
The second overload gets the module class as a `Type` parameter, instead of the generic parameter. So, the previous code block could be re-written as shown below: |
|||
|
|||
````csharp |
|||
AbpApplicationFactory.CreateAsync(typeof(MyConsoleDemoModule)); |
|||
```` |
|||
|
|||
Both overloads work exactly the same. So, you can use the second one if you don't know the module class type on development time and you (somehow) calculate it on runtime. |
|||
|
|||
If you use one of the methods above, ABP creates an internal service collection (`IServiceCollection`) and an internal service provider (`IServiceProvider`) to setup the [dependency injection](Dependency-Injection.md) system internally. Notice that we've used the `application.ServiceProvider` property in the *Installing a Framework Package* section to resolve the `IEmailSender` service from the dependency injection system. |
|||
|
|||
The next overload gets an `IServiceCollection` parameter from you to allow you to setup the dependency injection system yourself, or integrate to another framework (like ASP.NET Core) that also sets up the dependency injection system internally. |
|||
|
|||
We can change the `Program.cs` as shown below to externally manage the dependency injection setup: |
|||
|
|||
````csharp |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using MyConsoleDemo; |
|||
using Volo.Abp; |
|||
|
|||
// 1: Manually created the IServiceCollection |
|||
IServiceCollection services = new ServiceCollection(); |
|||
|
|||
// 2: Pass the IServiceCollection externally to the ABP Framework |
|||
using var application = await AbpApplicationFactory |
|||
.CreateAsync<MyConsoleDemoModule>(services); |
|||
|
|||
// 3: Manually built the IServiceProvider object |
|||
IServiceProvider serviceProvider = services.BuildServiceProvider(); |
|||
|
|||
// 4: Pass the IServiceProvider externally to the ABP Framework |
|||
await application.InitializeAsync(serviceProvider); |
|||
|
|||
Console.WriteLine("ABP Framework has been started..."); |
|||
|
|||
await application.ShutdownAsync(); |
|||
```` |
|||
|
|||
In this example, we've used .NET's standard dependency injection container. The `services.BuildServiceProvider()` call creates the standard container. However, ABP provides an alternative extension method, `BuildServiceProviderFromFactory()`, that properly works even if you are using another dependency injection container: |
|||
|
|||
````csharp |
|||
IServiceProvider serviceProvider = services.BuildServiceProviderFromFactory(); |
|||
```` |
|||
|
|||
> You can check the [Autofac Integration](Autofac-Integration.md) document if you want to learn how you can integrate the [Autofac](https://autofac.org/) dependency injection container with the ABP Framework. |
|||
|
|||
Finally, the `CreateAsync` method has a last overload that takes the module class name as a `Type` parameter and a `IServiceCollection` object. So, we could re-write the last `CreateAsync` method usage as in the following code block: |
|||
|
|||
````csharp |
|||
using var application = await AbpApplicationFactory |
|||
.CreateAsync(typeof(MyConsoleDemoModule), services); |
|||
```` |
|||
|
|||
> All of the `CreateAsync` method overloads have `Create` counterparts. If your application type can not utilize asynchronous programming (that means you can't use the `await` keyword), then you can use the `Create` method instead of the `CreateAsync` method. |
|||
|
|||
### AbpApplicationCreationOptions |
|||
|
|||
All of the `CreateAsync` overloads can get an optional `Action<AbpApplicationCreationOptions>` parameter to configure the options that are used on the application creation. See the following example: |
|||
|
|||
````csharp |
|||
using var application = await AbpApplicationFactory |
|||
.CreateAsync<MyConsoleDemoModule>(options => |
|||
{ |
|||
options.ApplicationName = "MyApp"; |
|||
}); |
|||
```` |
|||
|
|||
We've passed a lambda method to configure the `ApplicationName` option. Here's a list of all standard options: |
|||
|
|||
* `ApplicationName`: A human-readable name for the application. It is a unique value for an application. |
|||
* `Configuration`: Can be used to setup the [application configuration](Configuration.md) when it is not provided by the hosting system. It is not needed for ASP.NET Core and other .NET hosted applications. However, if you've used `AbpApplicationFactory` with an internal service provider, you can use this option to configure how the application configuration is built. |
|||
* `Environment`: Environment name for the application. |
|||
* `PlugInSources`: A list of plugin sources. See the [Plug-In Modules documentation](PlugIn-Modules) to learn how to work with plugins. |
|||
* `Services`: The `IServiceCollection` object that can be used to register service dependencies. You generally don't need that, because you configure your services in your [module class](Module-Development-Basics.md). However, it can be used while writing extension methods for the `AbpApplicationCreationOptions` class. |
|||
|
|||
#### The ApplicationName option |
|||
|
|||
As defined above, the `ApplicationName` option is a human-readable name for the application. It is a unique value for an application. |
|||
|
|||
`ApplicationName` is used by the ABP Framework in several places to distinguish the application. For example, the [audit logging](Audit-Logging.md) system saves the `ApplicationName` in each audit log record written by the related application, so you can understand which application has created the audit log entry. So, if your system consists of multiple applications (like a microservice solution) that are saving audit logs to a single point, you should be sure that each application has a different `ApplicationName`. |
|||
|
|||
The `ApplicationName` property's value is set automatically from the **entry assembly's name** (generally, the project name in a .NET solution) by default, which is proper for most cases, since each application typically has a unique entry assembly name. |
|||
|
|||
There are two ways to set the application name to a different value. In this first approach, you can set the `ApplicationName` property in your application's [configuration](Configuration.md). The easiest way is to add an `ApplicationName` field to your `appsettings.json` file: |
|||
|
|||
````json |
|||
{ |
|||
"ApplicationName": "Services.Ordering" |
|||
} |
|||
```` |
|||
|
|||
Alternatively, you can set `AbpApplicationCreationOptions.ApplicationName` while creating the ABP application. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `ApplicationName` option as shown below: |
|||
|
|||
````csharp |
|||
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options => |
|||
{ |
|||
options.ApplicationName = "Services.Ordering"; |
|||
}); |
|||
```` |
|||
|
|||
#### IApplicationInfoAccessor |
|||
|
|||
If you need to access the `ApplicationName` later in your solution, you can inject the `IApplicationInfoAccessor` service and get the value from its `ApplicationName` property. |
|||
|
|||
`IApplicationInfoAccessor` also provides an `InstanceId` value, that is a random GUID value that is generated when your application starts. You can use that value to distinguish application instances from each other. |
|||
|
|||
## IAbpApplication |
|||
|
|||
`AbpApplicationFactory` returns an `IAbpApplication` object from its `CreateAsync` (or `Create`) method. `IAbpApplication` is the main container for an ABP application. It is also registered to the [dependency injection](Dependency-Injection.md) system, so you can inject `IAbpApplication` in your services to use its properties and methods. |
|||
|
|||
Here's a list of `IAbpApplication` properties you may want to know: |
|||
|
|||
* `StartupModuleType`: Gets the root module of the application that was used while creating the application container (on the `AbpApplicationFactory.CreateAsync` method). |
|||
* `Services`: A list of all service registrations (the `IServiceCollection` object). You can not add new services to this collection after application initialization (you can actually add, but it won't have any effect). |
|||
* `ServiceProvider`: A reference to the root service provider used by the application. This can not be used before initializing the application. If you need to resolve non-singleton services from that `IServiceProvider` object, always create a new service scope and dispose it after usage. Otherwise, your application will have memory leak problems. See the *Releasing/Disposing Services* section of the [dependency injection](Dependency-Injection.md) document for more information about service scopes. |
|||
* `Modules`: A read-only list of all the modules loaded into the current application. Alternatively, you can inject the `IModuleContainer` service if you need to access the module list in your application code. |
|||
|
|||
The `IAbpApplication` interface extends the `IApplicationInfoAccessor` interface, so you can get the `ApplicationName` and `InstanceId` values from it. However, if you only need to access these properties, inject and use the `IApplicationInfoAccessor` service instead. |
|||
|
|||
`IAbpApplication` is disposable. Always dispose of it before exiting your application. |
|||
|
|||
## IAbpHostEnvironment |
|||
|
|||
Sometimes, while creating an application, we need to get the current hosting environment and take actions according to that. In such cases, we can use some services such as [IWebHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.iwebhostenvironment?view=aspnetcore-8.0) or [IWebAssemblyHostEnvironment](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.webassembly.hosting.iwebassemblyhostenvironment) provided by .NET, in the final application. |
|||
|
|||
However, we can not use these services in a class library, which is used by the final application. ABP Framework provides the `IAbpHostEnvironment` service, which allows you to get the current environment name whenever you want. `IAbpHostEnvironment` is used by the ABP Framework in several places to perform specific actions by the environment. For example, ABP Framework reduces the cache duration on the **Development** environment for some services. |
|||
|
|||
`IAbpHostEnvironment` obtains the current environment name by the following order: |
|||
|
|||
1. Gets and sets the environment name if it's specified in the `AbpApplicationCreationOptions`. |
|||
2. Tries to obtain the environment name from the `IWebHostEnvironment` or `IWebAssemblyHostEnvironment` services for ASP.NET Core & Blazor WASM applications if the environment name isn't specified in the `AbpApplicationCreationOptions`. |
|||
3. Sets the environment name as **Production**, if the environment name is not specified or can not be obtained from the services. |
|||
|
|||
You can configure the `AbpApplicationCreationOptions` [options class](Options.md) while creating the ABP application and set an environment name to its `Environment` property. You can find the `AddApplication` or `AddApplicationAsync` call in your solution (typically in the `Program.cs` file), and set the `Environment` option as shown below: |
|||
|
|||
```csharp |
|||
await builder.AddApplicationAsync<OrderingServiceHttpApiHostModule>(options => |
|||
{ |
|||
options.Environment = Environments.Staging; //or directly set as "Staging" |
|||
}); |
|||
``` |
|||
|
|||
Then, whenever you need to get the current environment name or check the environment, you can use the `IAbpHostEnvironment` interface: |
|||
|
|||
```csharp |
|||
public class MyDemoService |
|||
{ |
|||
private readonly IAbpHostEnvironment _abpHostEnvironment; |
|||
|
|||
public MyDemoService(IAbpHostEnvironment abpHostEnvironment) |
|||
{ |
|||
_abpHostEnvironment = abpHostEnvironment; |
|||
} |
|||
|
|||
public void MyMethod() |
|||
{ |
|||
var environmentName = _abpHostEnvironment.EnvironmentName; |
|||
|
|||
if (_abpHostEnvironment.IsDevelopment()) { /* ... */ } |
|||
|
|||
if (_abpHostEnvironment.IsStaging()) { /* ... */ } |
|||
|
|||
if (_abpHostEnvironment.IsProduction()) { /* ... */ } |
|||
|
|||
if (_abpHostEnvironment.IsEnvironment("custom-environment")) { /* ... */ } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## .NET Generic Host & ASP.NET Core Integrations |
|||
|
|||
`AbpApplicationFactory` can create a standalone ABP application container without any external dependency. However, in most cases, you will want to integrate it with [.NET's generic host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) or ASP.NET Core. For such usages, ABP provides built-in extension methods to easily create an ABP application container that is well-integrated to these systems. |
|||
|
|||
The [Getting Started with an Empty ASP.NET Core MVC / Razor Pages Application](Getting-Started-AspNetCore-Application.md) document clearly explains how you can create an ABP application container in an ASP.NET Core application. |
|||
|
|||
You can also [create a console application](Startup-Templates/Console) to see how it is integrated with .NET Generic Host. |
|||
|
|||
> Most of the times, you will directly create ABP applications using the ABP CLI's `new` command. So, you don't need to care about these integration details. |
|||
|
|||
## See Also |
|||
|
|||
* [Dependency injection](Dependency-Injection.md) |
|||
* [Modularity](Module-Development-Basics.md) |
|||
@ -1,183 +0,0 @@ |
|||
# VoloDocs |
|||
|
|||
## What is VoloDocs? |
|||
|
|||
VoloDocs is a cross-platform web application that allows you to easily create beautiful documentation and build developer communities. It simplifies software documentation with the help of GitHub integration. You use the power of GitHub for versioning, hosting of your docs. You let your users to edit a document. |
|||
|
|||
## Main Features |
|||
|
|||
- Serves documents from your GitHub repository. |
|||
- Supports Markdown / HTML document formatting. |
|||
- Supports versioning (integrated to GitHub releases). |
|||
- Supports multiple projects. |
|||
- Allows users to edit a document on GitHub. |
|||
- Cross-platform; deployable to Windows / Linux / macOS. |
|||
|
|||
## GitHub Repository |
|||
|
|||
It's free & open-source. You can browse VoloDocs source-code and contribute on GitHub: |
|||
|
|||
https://github.com/abpframework/abp/tree/master/modules/docs |
|||
|
|||
## Download |
|||
|
|||
You can download the VoloDocs release from the following links: |
|||
|
|||
https://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit** |
|||
|
|||
https://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit** |
|||
|
|||
https://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS** |
|||
|
|||
https://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux** |
|||
|
|||
Notice that, all installations are self-contained deployments. It means all the required third-party dependencies along with the version of .NET Core is included. So you don't need to install any .NET Core SDK / Runtime. |
|||
|
|||
## Folder Structure |
|||
|
|||
When you extract the `VoloDocs.*.zip` file, you will see a `Web` folder and a `Migrator` folder. The `Web` folder contains the website files and `Migrator` contains the application to build your database. Before publishing your website, you need to create a new database or update your existing database to the latest. If this is the first time you install VoloDocs, `Migrator` will create a new database for you, otherwise it updates to the latest version. The only setting you need to configure, is the `ConnectionString` which is located in the `appsettings.json` file. See the next section to learn how to configure your VoloDocs application. |
|||
|
|||
## Steps by Step Deployment |
|||
|
|||
- ### Database Migration |
|||
|
|||
To update your existing database or create your initial database, go to `Migrator` folder in your VoloDocs directory. |
|||
|
|||
Open `appsettings.json` in your text editor and set your database connection string. If you don't know how to write the connection string for your database system, you can check out https://www.connectionstrings.com/. |
|||
|
|||
After you set your connection string, run `Migrate.bat` for Windows platform and `VoloDocs.Migrator` for other operating systems. That's it now configure your website. |
|||
|
|||
- ### Configuring Website |
|||
|
|||
Go to `Web` folder in your VoloDocs directory. Open `appsettings.json` in your text editor. Set your connection string (same as in the `Migrator`'s `appsettings.json`). That's it! Now you can publish your website. |
|||
|
|||
If you want to run |
|||
|
|||
- ### Deploying Website |
|||
|
|||
In the previous step, you created or updated your database. Ensure that your database exists on the specified connection string. |
|||
|
|||
- #### Deploying to IIS |
|||
|
|||
- Move `Web` folder to your `wwwroot ` folder. |
|||
- Rename `Web` folder to `VoloDocs` (Now you have `C:\inetpub\wwwroot\VoloDocs`). |
|||
- The `VoloDocs` application pool is being created automatically. Open **Application Pools** and double click `VoloDocs` application pool and set |
|||
- **.NET CLR version**: `No Managed Code` |
|||
- **Managed pipeline mode**: `Integrated` |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
- If you get the below error, it means don't have the hosting bundle installed on the server. See [this document](https://docs.microsoft.com/aspnet/core/host-and-deploy/iis/#install-the-net-core-hosting-bundle) to learn how to install it or [download Hosting Bundle](https://www.microsoft.com/net/permalink/dotnetcore-current-windows-runtime-bundle-installer) and run on your server. |
|||
|
|||
``` |
|||
Handler "aspNetCore" has a bad module "AspNetCoreModuleV2" in its module list using IIS |
|||
``` |
|||
|
|||
- Further information about hosting VoloDocs check out [Microsoft's official document for hosting ASP.NET Core application on IIS](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis). |
|||
|
|||
- #### Deploying to Azure |
|||
|
|||
Microsoft has a good document on how to deploy your ASP.NET Core web app to Azure App Service. We recommend you to read this document https://docs.microsoft.com/en-us/azure/app-service/app-service-web-get-started-dotnet. |
|||
|
|||
- #### Running the Application From Command Line |
|||
|
|||
Alternatively you can run the application from command line, navigate to `VoloDocs\Web` folder and run `VoloDocs.Web.exe` for Windows or `VoloDocs.Web` for MacOS / Linux. |
|||
|
|||
- ### First Run |
|||
|
|||
To start the website, navigate to your address (as configured in the previous section). |
|||
|
|||
When you first open the website, you need to create a project. |
|||
|
|||
#### Creating a Project |
|||
|
|||
Go to the following address to create project |
|||
|
|||
- `http://<yourwebsite>/Account/Login?returnUrl=/Docs/Admin/Projects` |
|||
|
|||
##### Default credentials |
|||
|
|||
To login the admin side, use the following credentials: |
|||
|
|||
* **Username**: `admin` |
|||
|
|||
* **Password**: `1q2w3E*` |
|||
|
|||
##### An example project definition |
|||
|
|||
Here's a sample project information that uses GitHub source. |
|||
|
|||
We will configure the VoloDocs to show ABP Framework's documentation that's stored in GitHub. |
|||
|
|||
Here's the link to ABP Framework GitHub docs folder: |
|||
|
|||
https://github.com/abpframework/abp/tree/master/docs/en |
|||
|
|||
|
|||
|
|||
* **Name**: `ABP Framework` |
|||
|
|||
* **Short name**: `abp` |
|||
|
|||
* **Format**: `markdown` |
|||
|
|||
* **Default document name**: `Index` |
|||
|
|||
* **Navigation document name**: `docs-nav.json` ([see the sample navigation](https://github.com/abpframework/abp/blob/master/docs/en/docs-nav.json)) |
|||
|
|||
* **Parameters Document Name**: `docs-params.json` ([see the sample parameters](https://github.com/abpframework/abp/blob/dev/docs/en/docs-params.json)) |
|||
|
|||
* **Minimum version**: *leave empty* *(hides the previous versions)* |
|||
|
|||
* **Main web site URL**: `/` |
|||
|
|||
* **Latest version branch name**: leave empty () |
|||
|
|||
* **GitHub root URL**: `https://github.com/abpframework/abp/tree/{version}/docs/` |
|||
|
|||
* **GitHub access token**: [see how to retrieve GitHub access token](#retrieving-github-access-token) |
|||
|
|||
* **GitHub user agent**: [see how to learn your GitHub username](#learn-your-github-username) |
|||
|
|||
* **GitHub version provider source**: `Releases` (other option is `Branches`) |
|||
|
|||
* **Version branch prefix**: leave empty () |
|||
|
|||
 |
|||
|
|||
##### Retrieving GitHub Access Token |
|||
|
|||
To create a personal access token in GitHub, you need to visit the **Settings** of the user account and under **Developer settings** you will find **Personal access tokens**. Select **Generate new token**, enter in a name as the Token description and enable the repo checkbox. Alternatively, to enter generate new token, browse to https://github.com/settings/tokens/new. |
|||
|
|||
###### Generate Token for Public Repositories |
|||
|
|||
To access public repositories, check `public_repo` under the `repo` section. This will enable VoloDocs to access your public GitHub repositories. Click `Generate Token` button on the bottom of the page. |
|||
|
|||
 |
|||
|
|||
###### Generate Token for Private Repositories |
|||
|
|||
To access public repositories, check all items under the `repo` section. This will enable VoloDocs to access your private GitHub repositories. Click `Generate Token` button on the bottom of the page. |
|||
|
|||
 |
|||
|
|||
###### Learn Your GitHub Username |
|||
|
|||
To learn your GitHub username, click on your profile picture on the top-right corner of the GitHub page. You will see your username right after the text "Signed in as ..." |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
After you save the project, go to root website address and you will see your documentation. |
|||
|
|||
`http://<yourwebsite>/documents` |
|||
|
|||
### Any Issues? |
|||
|
|||
If you encounter any problem or issues about installation, usage or report a bug, follow the link: |
|||
|
|||
https://github.com/abpframework/abp/issues/new |
|||
|
|||
@ -1,731 +0,0 @@ |
|||
# Migrating from ASP.NET Boilerplate to the ABP Framework |
|||
|
|||
ABP Framework is **the successor** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. This guide aims to help you to **migrate your existing solutions** (you developed with the ASP.NET Boilerplate framework) to the ABP Framework. |
|||
|
|||
## Introduction |
|||
|
|||
**ASP.NET Boilerplate** is being **actively developed** [since 2013](https://github.com/aspnetboilerplate/aspnetboilerplate/graphs/contributors). It is loved, used and contributed by the community. It started as a side project of [a developer](http://halilibrahimkalkan.com/), but now it is officially maintained and improved by the company [Volosoft](https://volosoft.com/) in addition to the great community support. |
|||
|
|||
ABP Framework has the same goal of the ASP.NET Boilerplate framework: **Don't Repeat Yourself**! It provides infrastructure, tools and startup templates to make a developer's life easier while developing enterprise software solutions. |
|||
|
|||
See [the introduction blog post](https://blog.abp.io/abp/Abp-vNext-Announcement) if you wonder why we needed to re-write the ASP.NET Boilerplate framework. |
|||
|
|||
### Should I Migrate? |
|||
|
|||
No, you don't have to! |
|||
|
|||
* ASP.NET Boilerplate is still in active development and maintenance. |
|||
* It also works on the latest ASP.NET Core and related libraries and tools. It is up to date. |
|||
|
|||
However, if you want to take the advantage of the new ABP Framework [features](https://abp.io/features) and the new architecture opportunities (like support for NoSQL databases, microservice compatibility, advanced modularity), you can use this document as a guide. |
|||
|
|||
### What About the ASP.NET Zero? |
|||
|
|||
[ASP.NET Zero](https://aspnetzero.com/) is a commercial product developed by the core ASP.NET Boilerplate team, on top of the ASP.NET Boilerplate framework. It provides pre-built application [features](https://aspnetzero.com/Features), code generation tooling and a nice looking modern UI. It is trusted and used by thousands of companies from all around the World. |
|||
|
|||
We have created the [ABP Commercial](https://commercial.abp.io/) as an alternative to the ASP.NET Zero. ABP Commercial is more modular and upgradeable compared to the ASP.NET Zero. It currently has less features compared to ASP.NET Zero, but the gap will be closed by the time (it also has some features don't exist in the ASP.NET Zero). |
|||
|
|||
We think ASP.NET Zero is still a good choice while starting a new application. It is production ready and mature solution delivered as a full source code. It is being actively developed and we are constantly adding new features. |
|||
|
|||
We don't suggest to migrate your ASP.NET Zero based solution to the ABP Commercial if; |
|||
|
|||
* Your ASP.NET Zero solution is mature and it is in maintenance rather than a rapid development. |
|||
* You don't have enough development time to perform the migration. |
|||
* A monolithic solution fits in your business. |
|||
* You've customized existing ASP.NET Zero features too much based on your requirements. |
|||
|
|||
We also suggest you to compare the features of two products based on your needs. |
|||
|
|||
If you have an ASP.NET Zero based solution and want to migrate to the ABP Commercial, this guide will also help you. |
|||
|
|||
### ASP.NET MVC 5.x Projects |
|||
|
|||
The ABP Framework doesn't support ASP.NET MVC 5.x, it only works with ASP.NET Core. So, if you migrate your ASP.NET MVC 5.x based projects, you will also deal with the .NET Core migration. |
|||
|
|||
## The Migration Progress |
|||
|
|||
We've designed the ABP Framework by **getting the best parts** of the ASP.NET Boilerplate framework, so it will be familiar to you if you've developed ASP.NET Boilerplate based applications. |
|||
|
|||
In the ASP.NET Boilerplate, we have not worked much on the UI side, but used some free themes (we've used [metronic theme](https://keenthemes.com/metronic/) for ASP.NET Zero on the other side). In the ABP Framework, we worked a lot on the UI side (especially for the MVC / Razor Pages UI, because Angular already has a good modular system of its own). So, the **most challenging part** of the migration will be the **User Interface** of your solution. |
|||
|
|||
ABP Framework is (and ASP.NET Boilerplate was) designed based on the [Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) patterns & principles and the startup templates are layered based on the DDD layers. So, this guide respects to that layering model and explains the migration layer by layer. |
|||
|
|||
## Creating the Solution |
|||
|
|||
First step of the migration is to create a new solution. We suggest you to create a fresh new project using [the startup templates](https://abp.io/get-started) (see [this document](https://docs.abp.io/en/commercial/latest/getting-started) for the ABP Commercial). |
|||
|
|||
After creating the project and running the application, you can copy your code from your existing solution to the new solution step by step, layer by layer. |
|||
|
|||
### About Pre-Built Modules |
|||
|
|||
The startup projects for the ABP Framework use the [pre-built modules](https://docs.abp.io/en/abp/latest/Modules/Index) (not all of them, but the essentials) and themes as NuGet/NPM packages. So, you don't see the source code of the modules/themes in your solution. This has an advantage that you can easily update these packages when a new version is released. However, you can not easily customize them as their source code in your hands. |
|||
|
|||
We suggest to continue to use these modules as package references, in this way you can get new features easily (see [abp update command](https://docs.abp.io/en/abp/latest/CLI#update)). In this case, you have a few options to customize or extend the functionality of the used modules; |
|||
|
|||
* You can create your own entity and share the same database table with an entity in a used module. An example of this is the `AppUser` entity comes in the startup template. |
|||
* You can [replace](https://docs.abp.io/en/abp/latest/Dependency-Injection#replace-a-service) a domain service, application service, controller, page model or other types of services with your own implementation. We suggest you to inherit from the existing implementation and override the method you need. |
|||
* You can replace a `.cshtml` view, page, view component, partial view... with your own one using the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). |
|||
* You can override javascript, css, image or any other type of static files using the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). |
|||
|
|||
More extend/customization options will be developed and documented by the time. However, if you need to fully change the module implementation, it is best to add the [source code](https://github.com/abpframework/abp/tree/dev/modules) of the related module into your own solution and remove the package dependencies. |
|||
|
|||
The source code of the modules and the themes are [MIT](https://opensource.org/licenses/MIT) licensed, you can fully own and customize it without any limitation (for the ABP Commercial, you can download the source code of a [module](https://commercial.abp.io/modules)/[theme](https://commercial.abp.io/themes) if you have a [license](https://commercial.abp.io/pricing) type that includes the source code). |
|||
|
|||
## The Domain Layer |
|||
|
|||
Most of your domain layer code will remain same, while you need to perform some minor changes in your domain objects. |
|||
|
|||
### Aggregate Roots & Entities |
|||
|
|||
The ABP Framework and the ASP.NET Boilerplate both have the `IEntity` and `IEntity<T>` interfaces and `Entity` and `Entity<T>` base classes to define entities but they have some differences. |
|||
|
|||
If you have an entity in the ASP.NET Boilerplate application like that: |
|||
|
|||
````csharp |
|||
public class Person : Entity //Default PK is int for the ASP.NET Boilerplate |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
Then your primary key (the `Id` property in the base class) is `int` which is the **default primary key** (PK) type for the ASP.NET Boilerplate. If you want to set another type of PK, you need to explicitly declare it: |
|||
|
|||
````csharp |
|||
public class Person : Entity<Guid> //Set explicit PK in the ASP.NET Boilerplate |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
ABP Framework behaves differently and expects to **always explicitly set** the PK type: |
|||
|
|||
````csharp |
|||
public class Person : Entity<Guid> //Set explicit PK in the ASP.NET Boilerplate |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
`Id` property (and the corresponding PK in the database) will be `Guid` in this case. |
|||
|
|||
#### Composite Primary Keys |
|||
|
|||
ABP Framework also has a non-generic `Entity` base class, but this time it has no `Id` property. Its purpose is to allow you to create entities with composite PKs. See [the documentation](https://docs.abp.io/en/abp/latest/Entities#entities-with-composite-keys) to learn more about the composite PKs. |
|||
|
|||
#### Aggregate Root |
|||
|
|||
It is best practice now to use the `AggregateRoot` base class instead of `Entity` for aggregate root entities. See [the documentation](https://docs.abp.io/en/abp/latest/Entities#aggregateroot-class) to learn more about the aggregate roots. |
|||
|
|||
In opposite to the ASP.NET Boilerplate, the ABP Framework creates default repositories (`IRepository<T>`) **only for the aggregate roots**. It doesn't create for other types derived from the `Entity`. |
|||
|
|||
If you still want to create default repositories for all entity types, find the *YourProjectName*EntityFrameworkCoreModule class in your solution and change `options.AddDefaultRepositories()` to `options.AddDefaultRepositories(includeAllEntities: true)` (it may be already like that for the application startup template). |
|||
|
|||
#### Migrating the Existing Entities |
|||
|
|||
We suggest & use the GUID as the PK type for all the ABP Framework modules. However, you can continue to use your existing PK types to migrate your database tables easier. |
|||
|
|||
The challenging part will be the primary keys of the ASP.NET Boilerplate related entities, like Users, Roles, Tenants, Settings... etc. Our suggestion is to copy data from existing database to the new database tables using a tool or in a manual way (be careful about the foreign key values). |
|||
|
|||
#### Documentation |
|||
|
|||
See the documentation for details on the entities: |
|||
|
|||
* [ASP.NET Boilerplate - Entity documentation](https://aspnetboilerplate.com/Pages/Documents/Entities) |
|||
* [ABP Framework - Entity documentation](https://docs.abp.io/en/abp/latest/Entities) |
|||
|
|||
### Repositories |
|||
|
|||
> ABP Framework creates default repositories (`IRepository<T>`) **only for the aggregate roots**. It doesn't create for other types derived from the `Entity`. See the "Aggregate Root" section above for more information. |
|||
|
|||
The ABP Framework and the ASP.NET Boilerplate both have the default generic repository system, but has some differences. |
|||
|
|||
#### Injecting the Repositories |
|||
|
|||
In the ASP.NET Boilerplate, there are two default repository interfaces you can directly inject and use: |
|||
|
|||
* `IRepository<TEntity>` (e.g. `IRepository<Person>`) is used for entities with `int` primary key (PK) which is the default PK type. |
|||
* `IRepository<TEntity, TKey>` (e.g. `IRepository<Person, Guid>`) is used for entities with other types of PKs. |
|||
|
|||
ABP Framework doesn't have a default PK type, so you need to **explicitly declare the PK type** of your entity, like `IRepository<Person, int>` or `IRepository<Person, Guid>`. |
|||
|
|||
ABP Framework also has the `IRepository<TEntity>` (without PK), but it is mostly used when your entity has a composite PK (because this repository has no methods work with the `Id` property). See [the documentation](https://docs.abp.io/en/abp/latest/Entities#entities-with-composite-keys) to learn more about the **composite PKs**. |
|||
|
|||
#### Restricted Repositories |
|||
|
|||
ABP Framework additionally provides a few repository interfaces: |
|||
|
|||
* `IBasicRepository<TEntity, TKey>` has the same methods with the `IRepository` except it doesn't have `IQueryable` support. It can be useful if you don't want to expose complex querying code to the application layer. In this case, you typically want to create custom repositories to encapsulate the querying logic. It is also useful for database providers those don't support `IQueryable`. |
|||
* `IReadOnlyRepository<TEntity,TKey>` has the methods get data from the database, but doesn't contain any method change the database. |
|||
* `IReadOnlyBasicRepository<TEntity, TKey>` is similar to the read only repository but also doesn't support `IQueryable`. |
|||
|
|||
All the interfaces also have versions without `TKey` (like ``IReadOnlyRepository<TEntity>`) those can be used for composite PKs just like explained above. |
|||
|
|||
#### GetAll() vs IQueryable |
|||
|
|||
ASP.NET Boilerplate's repository has a `GetAll()` method that is used to obtain an `IQueryable` object to execute LINQ on it. An example application service calls the `GetAll()` method: |
|||
|
|||
````csharp |
|||
public class PersonAppService : ApplicationService, IPersonAppService |
|||
{ |
|||
private readonly IRepository<Person, Guid> _personRepository; |
|||
|
|||
public PersonAppService(IRepository<Person, Guid> personRepository) |
|||
{ |
|||
_personRepository = personRepository; |
|||
} |
|||
|
|||
public async Task DoIt() |
|||
{ |
|||
var people = await _personRepository |
|||
.GetAll() //GetAll() returns IQueryable |
|||
.Where(p => p.BirthYear > 2000) //Use LINQ extension methods |
|||
.ToListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
ABP Framework's repository have `GetQueryableAsync` instead: |
|||
|
|||
````csharp |
|||
public class PersonAppService : ApplicationService, IPersonAppService |
|||
{ |
|||
private readonly IRepository<Person, Guid> _personRepository; |
|||
|
|||
public PersonAppService(IRepository<Person, Guid> personRepository) |
|||
{ |
|||
_personRepository = personRepository; |
|||
} |
|||
|
|||
public async Task DoIt() |
|||
{ |
|||
var queryable = await _personRepository.GetQueryableAsync(); |
|||
var people = await queryable |
|||
.Where(p => p.BirthYear > 2000) //Use LINQ extension methods |
|||
.ToListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
> Note that in order to use the async LINQ extension methods (like `ToListAsync` here), you may need to depend on the database provider (like EF Core) since these methods are defined in the database provider package, they are not standard LINQ methods. See the [repository document](Repositories.md) for alternative approaches for async query execution. |
|||
|
|||
#### FirstOrDefault(predicate), Single()... Methods |
|||
|
|||
ABP Framework repository has not such methods get predicate (expression) since the repository itself is `IQueryable` and all these methods are already standard LINQ extension methods those can be directly used. |
|||
|
|||
However, it provides the following methods those can be used to query a single entity by its Id: |
|||
|
|||
* `FindAsync(id)` returns the entity or null if not found. |
|||
* `GetAsync(id)` method returns the entity or throws an `EntityNotFoundException` (which causes HTTP 404 status code) if not found. |
|||
|
|||
#### Sync vs Async |
|||
|
|||
ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions. |
|||
|
|||
In general, ABP Framework forces you to completely use async everywhere, because mixing async & sync methods is not a recommended approach. |
|||
|
|||
#### Documentation |
|||
|
|||
See the documentation for details on the repositories: |
|||
|
|||
* [ASP.NET Boilerplate - Repository documentation](https://aspnetboilerplate.com/Pages/Documents/Repositories) |
|||
* [ABP Framework - Repository documentation](https://docs.abp.io/en/abp/latest/Repositories) |
|||
|
|||
### Domain Services |
|||
|
|||
Your domain service logic mostly remains same on the migration. ABP Framework also defines the base `DomainService` class and the `IDomainService` interface just works like the ASP.NET Boilerplate. |
|||
|
|||
## The Application Layer |
|||
|
|||
Your application service logic remains similar on the migration. ABP Framework also defines the base `ApplicationService` class and the `IApplicationService` interface just works like the ASP.NET Boilerplate, but there are some differences in details. |
|||
|
|||
### Declarative Authorization |
|||
|
|||
ASP.NET Boilerplate has `AbpAuthorize` and `AbpMvcAuthorize` attributes for declarative authorization. Example usage: |
|||
|
|||
````csharp |
|||
[AbpAuthorize("MyUserDeletionPermissionName")] |
|||
public async Task DeleteUserAsync(...) |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
ABP Framework doesn't has such a custom attribute. It uses the standard `Authorize` attribute in all layers. |
|||
|
|||
````csharp |
|||
[Authorize("MyUserDeletionPermissionName")] |
|||
public async Task DeleteUserAsync(...) |
|||
{ |
|||
... |
|||
} |
|||
```` |
|||
|
|||
This is possible with the better integration to the Microsoft Authorization Extensions libraries. See the Authorization section below for more information about the authorization system. |
|||
|
|||
### CrudAppService and AsyncCrudAppService Classes |
|||
|
|||
ASP.NET Boilerplate has `CrudAppService` (with sync service methods) and `AsyncCrudAppService` (with async service methods) classes. |
|||
|
|||
ABP Framework only has the `CrudAppService` which actually has only the async methods (instead of sync methods). |
|||
|
|||
ABP Framework's `CrudAppService` method signatures are slightly different than the old one. For example, old update method signature was ` Task<TEntityDto> UpdateAsync(TUpdateInput input) ` while the new one is ` Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input) `. The main difference is that it gets the Id of the updating entity as a separate parameter instead of including in the input DTO. |
|||
|
|||
### Data Transfer Objects (DTOs) |
|||
|
|||
There are similar base DTO classes (like `EntityDto`) in the ABP Framework too. So, you can find the corresponding DTO base class if you need. |
|||
|
|||
#### Validation |
|||
|
|||
You can continue to use the data annotation attributes to validate your DTOs just like in the ASP.NET Boilerplate. |
|||
|
|||
ABP Framework doesn't include the ` ICustomValidate ` that does exists in the ASP.NET Boilerplate. Instead, you should implement the standard `IValidatableObject` interface for your custom validation logic. |
|||
|
|||
## The Infrastructure Layer |
|||
|
|||
### Namespaces |
|||
|
|||
ASP.NET Boilerplate uses the `Abp.*` namespaces while the ABP Framework uses the `Volo.Abp.*` namespaces for the framework and pre-built fundamental modules. |
|||
|
|||
In addition, there are also some pre-built application modules (like docs and blog modules) those are using the `Volo.*` namespaces (like `Volo.Blogging.*` and `Volo.Docs.*`). We consider these modules as standalone open source products developed by Volosoft rather than add-ons or generic modules completing the ABP Framework and used in the applications. We've developed them as a module to make them re-usable as a part of a bigger solution. |
|||
|
|||
### Module System |
|||
|
|||
Both of the ASP.NET Boilerplate and the ABP Framework have the `AbpModule` while they are a bit different. |
|||
|
|||
ASP.NET Boilerplate's `AbpModule` class has `PreInitialize`, `Initialize` and `PostInitialize` methods you can override and configure the framework and the depended modules. You can also register and resolve dependencies in these methods. |
|||
|
|||
ABP Framework's `AbpModule` class has the `ConfigureServices` and `OnApplicationInitialization` methods (and their Pre and Post versions). It is similar to ASP.NET Core's Startup class. You configure other services and register dependencies in the `ConfigureServices`. However, you can now resolve dependencies in that point. You can resolve dependencies and configure the ASP.NET Core pipeline in the `OnApplicationInitialization` method while you can not register dependencies here. So, the new module classes separate dependency registration phase from dependency resolution phase since it follows the ASP.NET Core's approach. |
|||
|
|||
### Dependency Injection |
|||
|
|||
#### The DI Framework |
|||
|
|||
ASP.NET Boilerplate is using the [Castle Windsor](http://www.castleproject.org/projects/windsor/) as the dependency injection framework. This is a fundamental dependency of the ASP.NET Boilerplate framework. We've got a lot of feedback to make the ASP.NET Boilerplate DI framework agnostic, but it was not so easy because of the design. |
|||
|
|||
ABP Framework is dependency injection framework independent since it uses Microsoft's [Dependency Injection Extensions](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) library as an abstraction. None of the ABP Framework or module packages depends on any specific library. |
|||
|
|||
However, ABP Framework doesn't use the Microsoft's base DI library because it has some missing features ABP Framework needs to: Property Injection and Interception. All the startup templates and the samples are using the [Autofac](https://autofac.org/) as the DI library and it is the only [officially integrated](Autofac-Integration.md) library to the ABP Framework. We suggest you to use the Autofac with the ABP Framework if you have not a good reason. If you have a good reason, please create an [issue](https://github.com/abpframework/abp/issues/new) on GitHub to request it or just implement it and send a pull request :) |
|||
|
|||
#### Registering the Dependencies |
|||
|
|||
Registering the dependencies are similar and mostly handled by the framework conventionally (like repositories, application services, controllers... etc). Implement the same `ITransientDependency`, `ISingletonDependency` and `IScopedDependency` interfaces for the services not registered by conventions. |
|||
|
|||
When you need to manually register dependencies, use the `context.Services` in the `ConfigureServices` method of your module. Example: |
|||
|
|||
````csharp |
|||
public class BlogModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//Register an instance as singleton |
|||
context.Services.AddSingleton<TaxCalculator>(new TaxCalculator(taxRatio: 0.18)); |
|||
|
|||
//Register a factory method that resolves from IServiceProvider |
|||
context.Services.AddScoped<ITaxCalculator>( |
|||
sp => sp.GetRequiredService<TaxCalculator>() |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
See the ABP Framework [dependency injection document](https://docs.abp.io/en/abp/latest/Dependency-Injection) for details. |
|||
|
|||
### Configuration vs Options System |
|||
|
|||
ASP.NET Boilerplate has its own configuration system to configure the framework and the modules. For example, you could disable the audit logging in the `Initialize` method of your [module](https://aspnetboilerplate.com/Pages/Documents/Module-System): |
|||
|
|||
````csharp |
|||
public override void Initialize() |
|||
{ |
|||
Configuration.Auditing.IsEnabled = false; |
|||
} |
|||
```` |
|||
|
|||
ABP Framework uses [the options pattern](Options.md) to configure the framework and the modules. You typically configure the options in the `ConfigureServices` method of your [module](Module-Development-Basics.md): |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.IsEnabled = false; |
|||
}); |
|||
} |
|||
```` |
|||
|
|||
Instead of a central configuration object, there are separated option classes for every module and feature those are defined in the related documents. |
|||
|
|||
### IAbpSession vs ICurrentUser and ICurrentTenant |
|||
|
|||
ASP.NET Boilerplate's `IAbpSession` service is used to obtain the current user and tenant information, like ` UserId ` and `TenantId`. |
|||
|
|||
ABP Framework doesn't have the same service. Instead, use `ICurrentUser` and `ICurrentTenant` services. These services are defined as base properties in some common classes (like `ApplicationService` and `AbpController`), so you generally don't need to manually inject them. They also have much properties compared to the `IAbpSession`. |
|||
|
|||
### Authorization |
|||
|
|||
ABP Framework extends the [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing the authorization system to be usable in the [application services](Application-Services.md) too. |
|||
|
|||
#### AbpAuthorize vs Authorize |
|||
|
|||
Use the standard `[Authorize]` and `[AllowAnonymous]` attributes instead of ASP.NET Boilerplate's custom `[AbpAuthorize]` and `[AbpAllowAnonymous]` attributes. |
|||
|
|||
#### IPermissionChecker vs IAuthorizationService |
|||
|
|||
Use the standard `IAuthorizationService` to check permissions instead of the ASP.NET Boilerplate's `IPermissionChecker` service. While `IPermissionChecker` also exists in the ABP Framework, it is used to explicitly use the permissions. Using `IAuthorizationService` is the recommended way since it covers other type of policy checks too. |
|||
|
|||
#### AuthorizationProvider vs PermissionDefinitionProvider |
|||
|
|||
You inherit from the `AuthorizationProvider` in the ASP.NET Boilerplate to define your permissions. ABP Framework replaces it by the `PermissionDefinitionProvider` base class. So, define your permissions by inheriting from the `PermissionDefinitionProvider` class. |
|||
|
|||
### Unit of Work |
|||
|
|||
Unit of work system has been designed to work seamlessly. For most of the cases, you don't need to change anything. |
|||
|
|||
`UnitOfWork` attribute of the ABP Framework doesn't have the `ScopeOption` (type of `TransactionScopeOption`) property. Instead, use `IUnitOfWorkManager.Begin()` method with `requiresNew = true` to create an independent inner transaction in a transaction scope. |
|||
|
|||
#### Data Filters |
|||
|
|||
ASP.NET Boilerplate implements the data filtering system as a part of the unit of work. ABP Framework has a separate `IDataFilter` service. |
|||
|
|||
See the [data filtering document](Data-Filtering.md) to learn how to enable/disable a filter. |
|||
|
|||
See [the UOW documentation](Unit-Of-Work.md) for more about the UOW system. |
|||
|
|||
### Multi-Tenancy |
|||
|
|||
#### IMustHaveTenant & IMayHaveTenant vs IMultiTenant |
|||
|
|||
ASP.NET Boilerplate defines `IMustHaveTenant` and `IMayHaveTenant` interfaces to implement them for your entities. In this way, your entities are automatically filtered according to the current tenant. Because of the design, there was a problem: You had to create a "Default" tenant in the database with "1" as the Id if you want to create a non multi-tenant application (this "Default" tenant was used as the single tenant). |
|||
|
|||
ABP Framework has a single interface for multi-tenant entities: `IMultiTenant` which defines a nullable `TenantId` property of type `Guid`. If your application is not multi-tenant, then your entities will have null TenantId (instead of a default one). |
|||
|
|||
On the migration, you need to change the TenantId field type and replace these interfaces with the `IMultiTenant` |
|||
|
|||
#### Switch Between Tenants |
|||
|
|||
In some cases you might need to switch to a tenant for a code scope and work with the tenant's data in this scope. |
|||
|
|||
In ASP.NET Boilerplate, it is done using the `IUnitOfWorkManager` service: |
|||
|
|||
````csharp |
|||
public async Task<List<Product>> GetProducts(int tenantId) |
|||
{ |
|||
using (_unitOfWorkManager.Current.SetTenantId(tenantId)) |
|||
{ |
|||
return await _productRepository.GetAllListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
In the ABP Framework it is done with the `ICurrentTenant` service: |
|||
|
|||
````csharp |
|||
public async Task<List<Product>> GetProducts(Guid tenantId) |
|||
{ |
|||
using (_currentTenant.Change(tenantId)) |
|||
{ |
|||
return await _productRepository.GetListAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Pass `null` to the `Change` method to switch to the host side. |
|||
|
|||
### Caching |
|||
|
|||
ASP.NET Boilerplate has its [own distributed caching abstraction](https://aspnetboilerplate.com/Pages/Documents/Caching) which has in-memory and Redis implementations. You typically inject the `ICacheManager` service and use its `GetCache(...)` method to obtain a cache, then get and set objects in the cache. |
|||
|
|||
ABP Framework uses and extends ASP.NET Core's [distributed caching abstraction](Caching.md). It defines the `IDistributedCache<T>` services to inject a cache and get/set objects. |
|||
|
|||
### Logging |
|||
|
|||
ASP.NET Boilerplate uses Castle Windsor's [logging facility](https://github.com/castleproject/Windsor/blob/master/docs/logging-facility.md) as an abstraction and supports multiple logging providers including Log4Net (the default one comes with the startup projects) and Serilog. You typically property-inject the logger: |
|||
|
|||
````csharp |
|||
using Castle.Core.Logging; //1: Import Logging namespace |
|||
|
|||
public class TaskAppService : ITaskAppService |
|||
{ |
|||
//2: Getting a logger using property injection |
|||
public ILogger Logger { get; set; } |
|||
|
|||
public TaskAppService() |
|||
{ |
|||
//3: Do not write logs if no Logger supplied. |
|||
Logger = NullLogger.Instance; |
|||
} |
|||
|
|||
public void CreateTask(CreateTaskInput input) |
|||
{ |
|||
//4: Write logs |
|||
Logger.Info("Creating a new task with description: " + input.Description); |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
ABP Framework depends on Microsoft's [logging extensions](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging) library which is also an abstraction and there are many providers implement it. Startup templates are using the Serilog as the pre-configured logging libary while it is easy to change in your project. The usage pattern is similar: |
|||
|
|||
````csharp |
|||
//1: Import the Logging namespaces |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
|
|||
public class TaskAppService : ITaskAppService |
|||
{ |
|||
//2: Getting a logger using property injection |
|||
public ILogger<TaskAppService> Logger { get; set; } |
|||
|
|||
public TaskAppService() |
|||
{ |
|||
//3: Do not write logs if no Logger supplied. |
|||
Logger = NullLogger<TaskAppService>.Instance; |
|||
} |
|||
|
|||
public void CreateTask(CreateTaskInput input) |
|||
{ |
|||
//4: Write logs |
|||
Logger.Info("Creating a new task with description: " + input.Description); |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
You inject the `ILogger<T>` instead of the `ILogger`. |
|||
|
|||
### Object to Object Mapping |
|||
|
|||
#### IObjectMapper Service |
|||
|
|||
ASP.NET Boilerplate defines an `IObjectMapper` service ([see](https://aspnetboilerplate.com/Pages/Documents/Object-To-Object-Mapping)) and has an integration to the [AutoMapper](https://automapper.org/) library. |
|||
|
|||
Example usage: Create a `User` object with the given `CreateUserInput` object: |
|||
|
|||
````csharp |
|||
public void CreateUser(CreateUserInput input) |
|||
{ |
|||
var user = ObjectMapper.Map<User>(input); |
|||
... |
|||
} |
|||
```` |
|||
|
|||
Example: Update an existing `User` properties with the given `UpdateUserInput` object: |
|||
|
|||
````csharp |
|||
public async Task UpdateUserAsync(Guid id, UpdateUserInput input) |
|||
{ |
|||
var user = await _userRepository.GetAsync(id); |
|||
ObjectMapper.Map(input, user); |
|||
} |
|||
```` |
|||
|
|||
ABP Framework has the same `IObjectMapper` service ([see](Object-To-Object-Mapping.md)) and the AutoMapper integration with a slightly different mapping methods. |
|||
|
|||
Example usage: Create a `User` object with the given `CreateUserInput` object: |
|||
|
|||
````csharp |
|||
public void CreateUser(CreateUserInput input) |
|||
{ |
|||
var user = ObjectMapper.Map<CreateUserInput, User>(input); |
|||
} |
|||
```` |
|||
|
|||
This time you need to explicitly declare the source type and target type (while ASP.NET Boilerplate was requiring only the target type). |
|||
|
|||
Example: Update an existing `User` properties with the given `UpdateUserInput` object: |
|||
|
|||
````csharp |
|||
public async Task UpdateUserAsync(Guid id, UpdateUserInput input) |
|||
{ |
|||
var user = await _userRepository.GetAsync(id); |
|||
ObjectMapper.Map<UpdateUserInput, User>(input, user); |
|||
} |
|||
```` |
|||
|
|||
Again, ABP Framework expects to explicitly set the source and target types. |
|||
|
|||
#### AutoMapper Integration |
|||
|
|||
##### Auto Mapping Attributes |
|||
|
|||
ASP.NET Boilerplate has `AutoMapTo`, `AutoMapFrom` and `AutoMap` attributes to automatically create mappings for the declared types. Example: |
|||
|
|||
````csharp |
|||
[AutoMapTo(typeof(User))] |
|||
public class CreateUserInput |
|||
{ |
|||
public string Name { get; set; } |
|||
public string Surname { get; set; } |
|||
... |
|||
} |
|||
```` |
|||
|
|||
ABP Framework has no such attributes, because AutoMapper as a [similar attribute](https://automapper.readthedocs.io/en/latest/Attribute-mapping.html) now. You need to switch to AutoMapper's attribute. |
|||
|
|||
##### Mapping Definitions |
|||
|
|||
ABP Framework follows AutoMapper principles closely. You can define classes derived from the `Profile` class to define your mappings. |
|||
|
|||
##### Configuration Validation |
|||
|
|||
Configuration validation is a best practice for the AutoMapper to maintain your mapping configuration in a safe way. |
|||
|
|||
See [the documentation](Object-To-Object-Mapping.md) for more information related to the object mapping. |
|||
|
|||
### Setting Management |
|||
|
|||
#### Defining the Settings |
|||
|
|||
In an ASP.NET Boilerplate based application, you create a class deriving from the `SettingProvider` class, implement the `GetSettingDefinitions` method and add your class to the `Configuration.Settings.Providers` list. |
|||
|
|||
In the ABP Framework, you need to derive your class from the `SettingDefinitionProvider` and implement the `Define` method. You don't need to register your class since the ABP Framework automatically discovers it. |
|||
|
|||
#### Getting the Setting Values |
|||
|
|||
ASP.NET Boilerplate provides the `ISettingManager` to read the setting values in the server side and `abp.setting.get(...)` method in the JavaScript side. |
|||
|
|||
ABP Framework has the `ISettingProvider` service to read the setting values in the server side and `abp.setting.get(...)` method in the JavaScript side. |
|||
|
|||
#### Setting the Setting Values |
|||
|
|||
For ASP.NET Boilerplate, you use the same `ISettingManager` service to change the setting values. |
|||
|
|||
ABP Framework separates it and provides the setting management module (pre-added to the startup projects) which has the ` ISettingManager ` to change the setting values. This separation was introduced to support tiered deployment scenarios (where `ISettingProvider` can also work in the client application while `ISettingManager ` can also work in the server (API) side). |
|||
|
|||
### Clock |
|||
|
|||
ASP.NET Boilerplate has a static `Clock` service ([see](https://aspnetboilerplate.com/Pages/Documents/Timing)) which is used to abstract the `DateTime` kind, so you can easily switch between Local and UTC times. You don't inject it, but just use the `Clock.Now` static method to obtain the current time. |
|||
|
|||
ABP Framework has the `IClock` service ([see](Timing.md)) which has a similar goal, but now you need to inject it whenever you need it. |
|||
|
|||
### Event Bus |
|||
|
|||
ASP.NET Boilerplate has an in-process event bus system. You typically inject the `IEventBus` (or use the static instance `EventBus.Default`) to trigger an event. It automatically triggers events for entity changes (like `EntityCreatingEventData` and `EntityUpdatedEventData`). You create a class by implementing the `IEventHandler<T>` interface. |
|||
|
|||
ABP Framework separates the event bus into two services: `ILocalEventBus` and `IDistributedEventBus`. |
|||
|
|||
The local event bus is similar to the event bus of the ASP.NET Boilerplate while the distributed event bus is new feature introduced in the ABP Framework. |
|||
|
|||
So, to migrate your code; |
|||
|
|||
* Use the `ILocalEventBus` instead of the `IEventBus`. |
|||
* Implement the `ILocalEventHandler` instead of the `IEventHandler`. |
|||
|
|||
> Note that ABP Framework has also an `IEventBus` interface, but it does exists to be a common interface for the local and distributed event bus. It is not injected and directly used. |
|||
|
|||
### Feature Management |
|||
|
|||
Feature system is used in multi-tenant applications to define features of your application check if given feature is available for the current tenant. |
|||
|
|||
#### Defining Features |
|||
|
|||
In the ASP.NET Boilerplate ([see](https://aspnetboilerplate.com/Pages/Documents/Feature-Management)), you create a class inheriting from the `FeatureProvider`, override the `SetFeatures` method and add your class to the `Configuration.Features.Providers` list. |
|||
|
|||
In the ABP Framework ([see](Features.md)), you derive your class from the `FeatureDefinitionProvider` and override the `Define` method. No need to add your class to the configuration, it is automatically discovered by the framework. |
|||
|
|||
#### Checking Features |
|||
|
|||
You can continue to use the `RequiresFeature` attribute and `IFeatureChecker` service to check if a feature is enabled for the current tenant. |
|||
|
|||
#### Changing the Feature Values |
|||
|
|||
In the ABP Framework you use the `IFeatureManager` to change a feature value for a tenant. |
|||
|
|||
### Audit Logging |
|||
|
|||
The ASP.NET Boilerplate ([see](https://aspnetboilerplate.com/Pages/Documents/Audit-Logging)) and the ABP Framework ([see](Audit-Logging.md)) has similar audit logging systems. ABP Framework requires to add `UseAuditing()` middleware to the ASP.NET Core pipeline, which is already added in the startup templates. So, most of the times it will be work out of the box. |
|||
|
|||
### Localization |
|||
|
|||
ASP.NET Boilerplate supports XML and JSON files to define the localization key-values for the UI ([see](https://aspnetboilerplate.com/Pages/Documents/Localization)). ABP Framework only supports the JSON formatter localization files ([see](Localization.md)). So, you need to convert your XML file to JSON. |
|||
|
|||
The ASP.NET Boilerplate has its own the `ILocalizationManager` service to be injected and used for the localization in the server side. |
|||
|
|||
The ABP Framework uses [Microsoft localization extension](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) library, so it is completely integrated to ASP.NET Core. You use the `IStringLocalizer<T>` service to get a localized text. Example: |
|||
|
|||
````csharp |
|||
public class MyService |
|||
{ |
|||
private readonly IStringLocalizer<TestResource> _localizer; |
|||
|
|||
public MyService(IStringLocalizer<TestResource> localizer) |
|||
{ |
|||
_localizer = localizer; |
|||
} |
|||
|
|||
public void Foo() |
|||
{ |
|||
var str = _localizer["HelloWorld"]; //Get a localized text |
|||
} |
|||
} |
|||
```` |
|||
|
|||
So, you need to replace `ILocalizationManager` usage by the `IStringLocalizer`. |
|||
|
|||
It also provides API used in the client side: |
|||
|
|||
````js |
|||
var testResource = abp.localization.getResource('Test'); |
|||
var str = testResource('HelloWorld'); |
|||
```` |
|||
|
|||
It was like `abp.localization.localize(...)` in the ASP.NET Boilerplate. |
|||
|
|||
### Navigation vs Menu |
|||
|
|||
In ASP.NET Boilerplate you create a class deriving from the `NavigationProvider` to define your menu elements. Menu items has `requiredPermissionName` attributes to restrict access to a menu element. Menu items were static and your class is executed only one time. |
|||
|
|||
In the ABP Framework you need to create a class implements the `IMenuContributor` interface. Your class is executed whenever the menu needs to be rendered. So, you can conditionally add menu items. |
|||
|
|||
As an example, this is the menu contributor of the tenant management module: |
|||
|
|||
````csharp |
|||
public class AbpTenantManagementWebMainMenuContributor : IMenuContributor |
|||
{ |
|||
public async Task ConfigureMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
//Add items only to the main menu |
|||
if (context.Menu.Name != StandardMenus.Main) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//Get the standard administration menu item |
|||
var administrationMenu = context.Menu.GetAdministration(); |
|||
|
|||
//Resolve some needed services from the DI container |
|||
var l = context.GetLocalizer<AbpTenantManagementResource>(); |
|||
|
|||
var tenantManagementMenuItem = new ApplicationMenuItem( |
|||
TenantManagementMenuNames.GroupName, |
|||
l["Menu:TenantManagement"], |
|||
icon: "fa fa-users"); |
|||
|
|||
administrationMenu.AddItem(tenantManagementMenuItem); |
|||
|
|||
//Conditionally add the "Tenants" menu item based on the permission |
|||
if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) |
|||
{ |
|||
tenantManagementMenuItem.AddItem( |
|||
new ApplicationMenuItem( |
|||
TenantManagementMenuNames.Tenants, |
|||
l["Tenants"], |
|||
url: "/TenantManagement/Tenants")); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
So, you need to check permission using the `IAuthorizationService` if you want to show a menu item only when the user has the related permission. |
|||
|
|||
> Navigation/Menu system is only for ASP.NET Core MVC / Razor Pages applications. Angular applications has a different system implemented in the startup templates. |
|||
|
|||
## Missing Features |
|||
|
|||
The following features are not present for the ABP Framework. Here, a list of some major missing features (and the related issue for that feature waiting on the ABP Framework GitHub repository): |
|||
|
|||
* [Multi-Lingual Entities](https://aspnetboilerplate.com/Pages/Documents/Multi-Lingual-Entities) ([#1754](https://github.com/abpframework/abp/issues/1754)) |
|||
* [Real time notification system](https://aspnetboilerplate.com/Pages/Documents/Notification-System) ([#633](https://github.com/abpframework/abp/issues/633)) |
|||
* [NHibernate Integration](https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration) ([#339](https://github.com/abpframework/abp/issues/339)) - We don't intent to work on this, but any community contribution welcome. |
|||
|
|||
Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated. |
|||
@ -1,3 +0,0 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to Auto API Controllers document](../API/Auto-API-Controllers.md) |
|||
@ -1,4 +0,0 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to ASP.NET Core MVC Bundling & Minification document](../UI/AspNetCore/Bundling-Minification.md) |
|||
@ -1,4 +0,0 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to ASP.NET Core MVC Client Side Package Management document](../UI/AspNetCore/Client-Side-Package-Management.md) |
|||
@ -1,3 +0,0 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to Dynamic C# API Clients document](../API/Dynamic-CSharp-API-Clients.md) |
|||
@ -1,3 +0,0 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to Dynamic Forms document](../../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) |
|||
@ -1,3 +0,0 @@ |
|||
This document has moved. |
|||
|
|||
[Click to navigate to ABP Tag Helpers document](../../UI/AspNetCore/Tag-Helpers/Index.md) |
|||
@ -1,4 +0,0 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to Theming document](../UI/AspNetCore/Theming.md) |
|||
@ -1,4 +0,0 @@ |
|||
|
|||
This document has moved. |
|||
|
|||
[Click to navigate to Widgets document](../UI/AspNetCore/Widgets.md) |
|||
@ -1,3 +0,0 @@ |
|||
## Dynamic Proxying / Interceptors |
|||
|
|||
TODO |
|||
@ -1,391 +0,0 @@ |
|||
# Audit Logging |
|||
|
|||
[Wikipedia](https://en.wikipedia.org/wiki/Audit_trail): "*An audit trail (also called **audit log**) is a security-relevant chronological record, set of records, and/or destination and source of records that provide documentary evidence of the sequence of activities that have affected at any time a specific operation, procedure, or event*". |
|||
|
|||
ABP Framework provides an **extensible audit logging system** that automates the audit logging by **convention** and provides **configuration** points to control the level of the audit logs. |
|||
|
|||
An **audit log object** (see the Audit Log Object section below) is typically created & saved per web request. It includes; |
|||
|
|||
* **Request & response details** (like URL, Http method, Browser info, HTTP status code... etc.). |
|||
* **Performed actions** (controller actions and application service method calls with their parameters). |
|||
* **Entity changes** occurred in the web request. |
|||
* **Exception** information (if there was an error while executing the request). |
|||
* **Request duration** (to measure the performance of the application). |
|||
|
|||
> [Startup templates](Startup-Templates/Index.md) are configured for the audit logging system which is suitable for most of the applications. Use this document for a detailed control over the audit log system. |
|||
|
|||
### Database Provider Support |
|||
|
|||
* Fully supported by the [Entity Framework Core](Entity-Framework-Core.md) provider. |
|||
* Entity change logging is not supported by the [MongoDB](MongoDB.md) provider. Other features work as expected. |
|||
|
|||
## UseAuditing() |
|||
|
|||
`UseAuditing()` middleware should be added to the ASP.NET Core request pipeline in order to create and save the audit logs. If you've created your applications using [the startup templates](Startup-Templates/Index.md), it is already added. |
|||
|
|||
## AbpAuditingOptions |
|||
|
|||
`AbpAuditingOptions` is the main [options object](Options.md) to configure the audit log system. You can configure it in the `ConfigureServices` method of your [module](Module-Development-Basics.md): |
|||
|
|||
````csharp |
|||
Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.IsEnabled = false; //Disables the auditing system |
|||
}); |
|||
```` |
|||
|
|||
Here, a list of the options you can configure: |
|||
|
|||
* `IsEnabled` (default: `true`): A root switch to enable or disable the auditing system. Other options is not used if this value is `false`. |
|||
* `HideErrors` (default: `true`): Audit log system hides and write regular [logs](Logging.md) if any error occurs while saving the audit log objects. If saving the audit logs is critical for your system, set this to `false` to throw exception in case of hiding the errors. |
|||
* `IsEnabledForAnonymousUsers` (default: `true`): If you want to write audit logs only for the authenticated users, set this to `false`. If you save audit logs for anonymous users, you will see `null` for `UserId` values for these users. |
|||
* `AlwaysLogOnException` (default: `true`): If you set to true, it always saves the audit log on an exception/error case without checking other options (except `IsEnabled`, which completely disables the audit logging). |
|||
* `IsEnabledForIntegrationService` (default: `false`): Audit Logging is disabled for [integration services](Integration-Services.md) by default. Set this property as `true` to enable it. |
|||
* `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests. |
|||
* `DisableLogActionInfo` (default: `false`):If you set to true, Will no longer log `AuditLogActionInfo`. |
|||
* `ApplicationName`: If multiple applications are saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. If you don't set, it will set from the `IApplicationInfoAccessor.ApplicationName` value, which is the entry assembly name by default. |
|||
* `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters. |
|||
* `EntityHistorySelectors`: A list of selectors those are used to determine if an entity type is selected for saving the entity change. See the section below for details. |
|||
* `SaveEntityHistoryWhenNavigationChanges` (default: `true`): If you set to true, it will save entity changes to audit log when any navigation property changes. |
|||
* `Contributors`: A list of `AuditLogContributor` implementations. A contributor is a way of extending the audit log system. See the "Audit Log Contributors" section below. |
|||
* `AlwaysLogSelectors`: A list of selectors to save the audit logs for the matched criteria. |
|||
|
|||
### Entity History Selectors |
|||
|
|||
Saving all changes of all your entities would require a lot of database space. For this reason, **audit log system doesn't save any change for the entities unless you explicitly configure it**. |
|||
|
|||
To save all changes of all entities, simply use the `AddAllEntities()` extension method. |
|||
|
|||
````csharp |
|||
Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.EntityHistorySelectors.AddAllEntities(); |
|||
}); |
|||
```` |
|||
|
|||
`options.EntityHistorySelectors` actually a list of type predicate. You can write a lambda expression to define your filter. |
|||
|
|||
The example selector below does the same of the `AddAllEntities()` extension method defined above: |
|||
|
|||
````csharp |
|||
Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.EntityHistorySelectors.Add( |
|||
new NamedTypeSelector( |
|||
"MySelectorName", |
|||
type => |
|||
{ |
|||
if (typeof(IEntity).IsAssignableFrom(type)) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
) |
|||
); |
|||
}); |
|||
```` |
|||
|
|||
The condition `typeof(IEntity).IsAssignableFrom(type)` will be `true` for any class implements the `IEntity` interface (this is technically all the entities in your application). You can conditionally check and return `true` or `false` based on your preference. |
|||
|
|||
`options.EntityHistorySelectors` is a flexible and dynamic way of selecting the entities for audit logging. Another way is to use the `Audited` and `DisableAuditing` attributes per entity. |
|||
|
|||
## AbpAspNetCoreAuditingOptions |
|||
|
|||
`AbpAspNetCoreAuditingOptions` is the [options object](Options.md) to configure audit logging in the ASP.NET Core layer. You can configure it in the `ConfigureServices` method of your [module](Module-Development-Basics.md): |
|||
|
|||
````csharp |
|||
Configure<AbpAspNetCoreAuditingOptions>(options => |
|||
{ |
|||
options.IgnoredUrls.Add("/products"); |
|||
}); |
|||
```` |
|||
|
|||
`IgnoredUrls` is the only option. It is a list of ignored URLs prefixes. In the preceding example, all URLs starting with `/products` will be ignored for audit logging. |
|||
|
|||
## Enabling/Disabling Audit Logging for Services |
|||
|
|||
### Enable/Disable for Controllers & Actions |
|||
|
|||
All the controller actions are logged by default (see `IsEnabledForGetRequests` above for GET requests). |
|||
|
|||
You can use the `[DisableAuditing]` to disable it for a specific controller type: |
|||
|
|||
````csharp |
|||
[DisableAuditing] |
|||
public class HomeController : AbpController |
|||
{ |
|||
//... |
|||
} |
|||
```` |
|||
|
|||
Use `[DisableAuditing]` for any action to control it in the action level: |
|||
|
|||
````csharp |
|||
public class HomeController : AbpController |
|||
{ |
|||
[DisableAuditing] |
|||
public async Task<ActionResult> Home() |
|||
{ |
|||
//... |
|||
} |
|||
|
|||
public async Task<ActionResult> OtherActionLogged() |
|||
{ |
|||
//... |
|||
} |
|||
} |
|||
```` |
|||
|
|||
### Enable/Disable for Application Services & Methods |
|||
|
|||
[Application service](Application-Services.md) method calls also included into the audit log by default. You can use the `[DisableAuditing]` in service or method level. |
|||
|
|||
#### Enable/Disable for Other Services |
|||
|
|||
Action audit logging can be enabled for any type of class (registered to and resolved from the [dependency injection](Dependency-Injection.md)) while it is only enabled for the controllers and the application services by default. |
|||
|
|||
Use `[Audited]` and `[DisableAuditing]` for any class or method that need to be audit logged. In addition, your class can (directly or inherently) implement the `IAuditingEnabled` interface to enable the audit logging for that class by default. |
|||
|
|||
### Enable/Disable for Entities & Properties |
|||
|
|||
An entity is ignored on entity change audit logging in the following cases; |
|||
|
|||
* If you add an entity type to the `AbpAuditingOptions.IgnoredTypes` (as explained before), it is completely ignored in the audit logging system. |
|||
* If the object is not an [entity](Entities.md) (not implements `IEntity` directly or inherently - All entities implement this interface by default). |
|||
* If entity type is not public. |
|||
|
|||
Otherwise, you can use `Audited` to enable entity change audit logging for an entity: |
|||
|
|||
````csharp |
|||
[Audited] |
|||
public class MyEntity : Entity<Guid> |
|||
{ |
|||
//... |
|||
} |
|||
```` |
|||
|
|||
Or disable it for an entity: |
|||
|
|||
````csharp |
|||
[DisableAuditing] |
|||
public class MyEntity : Entity<Guid> |
|||
{ |
|||
//... |
|||
} |
|||
```` |
|||
|
|||
Disabling audit logging can be necessary only if the entity is being selected by the `AbpAuditingOptions.EntityHistorySelectors` that explained before. |
|||
|
|||
You can disable auditing only some properties of your entities for a detailed control over the audit logging: |
|||
|
|||
````csharp |
|||
[Audited] |
|||
public class MyUser : Entity<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Email { get; set; } |
|||
|
|||
[DisableAuditing] //Ignore the Passoword on audit logging |
|||
public string Password { get; set; } |
|||
} |
|||
```` |
|||
|
|||
Audit log system will save changes for the `MyUser` entity while it ignores the `Password` property which can be dangerous to save for security purposes. |
|||
|
|||
In some cases, you may want to save a few properties but ignore all others. Writing `[DisableAuditing]` for all the other properties would be tedious. In such cases, use `[Audited]` only for the desired properties and mark the entity with the `[DisableAuditing]` attribute: |
|||
|
|||
````csharp |
|||
[DisableAuditing] |
|||
public class MyUser : Entity<Guid> |
|||
{ |
|||
[Audited] //Only log the Name change |
|||
public string Name { get; set; } |
|||
|
|||
public string Email { get; set; } |
|||
|
|||
public string Password { get; set; } |
|||
} |
|||
```` |
|||
|
|||
## IAuditingStore |
|||
|
|||
`IAuditingStore` is an interface that is used to save the audit log objects (explained below) by the ABP Framework. If you need to save the audit log objects to a custom data store, you can implement the `IAuditingStore` in your own application and replace using the [dependency injection system](Dependency-Injection.md). |
|||
|
|||
`SimpleLogAuditingStore` is used if no audit store was registered. It simply writes the audit object to the standard [logging system](Logging.md). |
|||
|
|||
[The Audit Logging Module](Modules/Audit-Logging.md) has been configured in [the startup templates](Startup-Templates/Index.md) saves audit log objects to a database (it supports multiple database providers). So, most of the times you don't care about how `IAuditingStore` was implemented and used. |
|||
|
|||
## Audit Log Object |
|||
|
|||
An **audit log object** is created for each **web request** by default. An audit log object can be represented by the following relation diagram: |
|||
|
|||
 |
|||
|
|||
* **AuditLogInfo**: The root object with the following properties: |
|||
* `ApplicationName`: When you save audit logs of different applications to the same database, this property is used to distinguish the logs of the applications. |
|||
* `UserId`: Id of the current user, if the user has logged in. |
|||
* `UserName`: User name of the current user, if the user has logged in (this value is here to not depend on the identity module/system for lookup). |
|||
* `TenantId`: Id of the current tenant, for a multi-tenant application. |
|||
* `TenantName`: Name of the current tenant, for a multi-tenant application. |
|||
* `ExecutionTime`: The time when this audit log object has been created. |
|||
* `ExecutionDuration`: Total execution duration of the request, in milliseconds. This can be used to observe the performance of the application. |
|||
* `ClientId`: Id of the current client, if the client has been authenticated. A client is generally a 3rd-party application using the system over an HTTP API. |
|||
* `ClientName`: Name of the current client, if available. |
|||
* `ClientIpAddress`: IP address of the client/user device. |
|||
* `CorrelationId`: Current [Correlation Id](CorrelationId.md). Correlation Id is used to relate the audit logs written by different applications (or microservices) in a single logical operation. |
|||
* `BrowserInfo`: Browser name/version info of the current user, if available. |
|||
* `HttpMethod`: HTTP method of the current request (GET, POST, PUT, DELETE... etc.). |
|||
* `HttpStatusCode`: HTTP response status code for this request. |
|||
* `Url`: URL of the request. |
|||
* **AuditLogActionInfo**: An audit log action is typically a controller action or an [application service](Application-Services.md) method call during the web request. One audit log may contain multiple actions. An action object has the following properties: |
|||
* `ServiceName`: Name of the executed controller/service. |
|||
* `MethodName`: Name of the executed method of the controller/service. |
|||
* `Parameters`: A JSON formatted text representing the parameters passed to the method. |
|||
* `ExecutionTime`: The time when this method was executed. |
|||
* `ExecutionDuration`: Duration of the method execution, in milliseconds. This can be used to observe the performance of the method. |
|||
* **EntityChangeInfo**: Represents a change of an entity in this web request. An audit log may contain zero or more entity changes. An entity change has the following properties: |
|||
* `ChangeTime`: The time when the entity was changed. |
|||
* `ChangeType`: An enum with the following fields: `Created` (0), `Updated` (1) and `Deleted` (2). |
|||
* `EntityId`: Id of the entity that was changed. |
|||
* `EntityTenantId`: Id of the tenant this entity belongs to. |
|||
* `EntityTypeFullName`: Type (class) name of the entity with full namespace (like *Acme.BookStore.Book* for the Book entity). |
|||
* **EntityPropertyChangeInfo**: Represents a change of a property of an entity. An entity change info (explained above) may contain one or more property change with the following properties: |
|||
* `NewValue`: New value of the property. It is `null` if the entity was deleted. |
|||
* `OriginalValue`: Old/original value before the change. It is `null` if the entity was newly created. |
|||
* `PropertyName`: The name of the property on the entity class. |
|||
* `PropertyTypeFullName`: Type (class) name of the property with full namespace. |
|||
* **Exception**: An audit log object may contain zero or more exception. In this way, you can get a report of the failed requests. |
|||
* **Comment**: An arbitrary string value to add custom messages to the audit log entry. An audit log object may contain zero or more comments. |
|||
|
|||
In addition to the standard properties explained above, `AuditLogInfo`, `AuditLogActionInfo` and `EntityChangeInfo` objects implement the `IHasExtraProperties` interface, so you can add custom properties to these objects. |
|||
|
|||
## Audit Log Contributors |
|||
|
|||
You can extend the auditing system by creating a class that is derived from the `AuditLogContributor` class which defines the `PreContribute` and the `PostContribute` methods. |
|||
|
|||
The only pre-built contributor is the `AspNetCoreAuditLogContributor` class which sets the related properties for an HTTP request. |
|||
|
|||
A contributor can set properties and collections of the `AuditLogInfo` class to add more information. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
public class MyAuditLogContributor : AuditLogContributor |
|||
{ |
|||
public override void PreContribute(AuditLogContributionContext context) |
|||
{ |
|||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>(); |
|||
context.AuditInfo.SetProperty( |
|||
"MyCustomClaimValue", |
|||
currentUser.FindClaimValue("MyCustomClaim") |
|||
); |
|||
} |
|||
|
|||
public override void PostContribute(AuditLogContributionContext context) |
|||
{ |
|||
context.AuditInfo.Comments.Add("Some comment..."); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* `context.ServiceProvider` can be used to resolve services from the [dependency injection](Dependency-Injection.md). |
|||
* `context.AuditInfo` can be used to access to the current audit log object to manipulate it. |
|||
|
|||
After creating such a contributor, you must add it to the `AbpAuditingOptions.Contributors` list: |
|||
|
|||
````csharp |
|||
Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.Contributors.Add(new MyAuditLogContributor()); |
|||
}); |
|||
```` |
|||
|
|||
## IAuditLogScope & IAuditingManager |
|||
|
|||
This section explains the `IAuditLogScope` & `IAuditingManager` services for advanced use cases. |
|||
|
|||
An **audit log scope** is an [ambient scope](Ambient-Context-Pattern.md) that **builds** and **saves** an audit log object (explained before). By default, an audit log scope is created for a web request by the Audit Log Middleware (see `UseAuditing()` section above). |
|||
|
|||
### Access to the Current Audit Log Scope |
|||
|
|||
Audit log contributors, was explained above, is a global way of manipulating the audit log object. It is good if you can get a value from a service. |
|||
|
|||
If you need to manipulate the audit log object in an arbitrary point of your application, you can access to the current audit log scope and get the current audit log object (independent of how the scope is managed). Example: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IAuditingManager _auditingManager; |
|||
|
|||
public MyService(IAuditingManager auditingManager) |
|||
{ |
|||
_auditingManager = auditingManager; |
|||
} |
|||
|
|||
public async Task DoItAsync() |
|||
{ |
|||
var currentAuditLogScope = _auditingManager.Current; |
|||
if (currentAuditLogScope != null) |
|||
{ |
|||
currentAuditLogScope.Log.Comments.Add( |
|||
"Executed the MyService.DoItAsync method :)" |
|||
); |
|||
|
|||
currentAuditLogScope.Log.SetProperty("MyCustomProperty", 42); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Always check if `_auditingManager.Current` is null or not, because it is controlled in an outer scope and you can't know if an audit log scope was created before calling your method. |
|||
|
|||
### Manually Create an Audit Log Scope |
|||
|
|||
You rarely need to create a manual audit log scope, but if you need, you can create an audit log scope using the `IAuditingManager` as like in the following example: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IAuditingManager _auditingManager; |
|||
|
|||
public MyService(IAuditingManager auditingManager) |
|||
{ |
|||
_auditingManager = auditingManager; |
|||
} |
|||
|
|||
public async Task DoItAsync() |
|||
{ |
|||
using (var auditingScope = _auditingManager.BeginScope()) |
|||
{ |
|||
try |
|||
{ |
|||
//Call other services... |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
//Add exceptions |
|||
_auditingManager.Current.Log.Exceptions.Add(ex); |
|||
throw; |
|||
} |
|||
finally |
|||
{ |
|||
//Always save the log |
|||
await auditingScope.SaveAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
You can call other services, they may call others, they may change entities and so on. All these interactions are saved as a single audit log object in the finally block. |
|||
|
|||
## The Audit Logging Module |
|||
|
|||
The Audit Logging Module basically implements the `IAuditingStore` to save the audit log objects to a database. It supports multiple database providers. This module is added to the startup templates by default. |
|||
|
|||
See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it. |
|||
@ -1,3 +0,0 @@ |
|||
# Social/External Logins |
|||
|
|||
> This document has been moved. See the [Account Module](../Modules/Account.md) documentation. |
|||
@ -1,476 +0,0 @@ |
|||
# Authorization |
|||
|
|||
Authorization is used to check if a user is allowed to perform some specific operations in the application. |
|||
|
|||
ABP extends [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing authorization system to be usable in the **[application services](Application-Services.md)** too. |
|||
|
|||
So, all the ASP.NET Core authorization features and the documentation are valid in an ABP based application. This document focuses on the features that are added on top of ASP.NET Core authorization features. |
|||
|
|||
## Authorize Attribute |
|||
|
|||
ASP.NET Core defines the [**Authorize**](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple) attribute that can be used for an action, a controller or a page. ABP allows you to use the same attribute for an [application service](Application-Services.md) too. |
|||
|
|||
Example: |
|||
|
|||
```csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Acme.BookStore |
|||
{ |
|||
[Authorize] |
|||
public class AuthorAppService : ApplicationService, IAuthorAppService |
|||
{ |
|||
public Task<List<AuthorDto>> GetListAsync() |
|||
{ |
|||
... |
|||
} |
|||
|
|||
[AllowAnonymous] |
|||
public Task<AuthorDto> GetAsync(Guid id) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
[Authorize("BookStore_Author_Create")] |
|||
public Task CreateAsync(CreateAuthorDto input) |
|||
{ |
|||
... |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
- `Authorize` attribute forces the user to login into the application in order to use the `AuthorAppService` methods. So, `GetListAsync` method is only available to the authenticated users. |
|||
- `AllowAnonymous` suppresses the authentication. So, `GetAsync` method is available to everyone including unauthorized users. |
|||
- `[Authorize("BookStore_Author_Create")]` defines a policy (see [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies)) that is checked to authorize the current user. |
|||
|
|||
"BookStore_Author_Create" is an arbitrary policy name. If you declare an attribute like that, ASP.NET Core authorization system expects a policy to be defined before. |
|||
|
|||
You can, of course, implement your policies as stated in the ASP.NET Core documentation. But for simple true/false conditions like a policy was granted to a user or not, ABP defines the permission system which will be explained in the next section. |
|||
|
|||
## Permission System |
|||
|
|||
A permission is a simple policy that is granted or prohibited for a particular user, role or client. |
|||
|
|||
### Defining Permissions |
|||
|
|||
To define permissions, create a class inheriting from the `PermissionDefinitionProvider` as shown below: |
|||
|
|||
```csharp |
|||
using Volo.Abp.Authorization.Permissions; |
|||
|
|||
namespace Acme.BookStore.Permissions |
|||
{ |
|||
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var myGroup = context.AddGroup("BookStore"); |
|||
|
|||
myGroup.AddPermission("BookStore_Author_Create"); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> ABP automatically discovers this class. No additional configuration required! |
|||
|
|||
> You typically define this class inside the `Application.Contracts` project of your [application](Startup-Templates/Application.md). The startup template already comes with an empty class named *YourProjectNamePermissionDefinitionProvider* that you can start with. |
|||
|
|||
In the `Define` method, you first need to add a **permission group** or get an existing group then add **permissions** to this group. |
|||
|
|||
When you define a permission, it becomes usable in the ASP.NET Core authorization system as a **policy** name. It also becomes visible in the UI. See permissions dialog for a role: |
|||
|
|||
 |
|||
|
|||
- The "BookStore" group is shown as a new tab on the left side. |
|||
- "BookStore_Author_Create" on the right side is the permission name. You can grant or prohibit it for the role. |
|||
|
|||
When you save the dialog, it is saved to the database and used in the authorization system. |
|||
|
|||
> The screen above is available when you have installed the identity module, which is basically used for user and role management. Startup templates come with the identity module pre-installed. |
|||
|
|||
#### Localizing the Permission Name |
|||
|
|||
"BookStore_Author_Create" is not a good permission name for the UI. Fortunately, `AddPermission` and `AddGroup` methods can take `LocalizableString` as second parameters: |
|||
|
|||
```csharp |
|||
var myGroup = context.AddGroup( |
|||
"BookStore", |
|||
LocalizableString.Create<BookStoreResource>("BookStore") |
|||
); |
|||
|
|||
myGroup.AddPermission( |
|||
"BookStore_Author_Create", |
|||
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create") |
|||
); |
|||
``` |
|||
|
|||
Then you can define texts for "BookStore" and "Permission:BookStore_Author_Create" keys in the localization file: |
|||
|
|||
```json |
|||
"BookStore": "Book Store", |
|||
"Permission:BookStore_Author_Create": "Creating a new author" |
|||
``` |
|||
|
|||
> For more information, see the [localization document](Localization.md) on the localization system. |
|||
|
|||
The localized UI will be as seen below: |
|||
|
|||
 |
|||
|
|||
#### Multi-Tenancy |
|||
|
|||
ABP supports [multi-tenancy](Multi-Tenancy.md) as a first class citizen. You can define multi-tenancy side option while defining a new permission. It gets one of the three values defined below: |
|||
|
|||
- **Host**: The permission is available only for the host side. |
|||
- **Tenant**: The permission is available only for the tenant side. |
|||
- **Both** (default): The permission is available both for tenant and host sides. |
|||
|
|||
> If your application is not multi-tenant, you can ignore this option. |
|||
|
|||
To set the multi-tenancy side option, pass to the third parameter of the `AddPermission` method: |
|||
|
|||
```csharp |
|||
myGroup.AddPermission( |
|||
"BookStore_Author_Create", |
|||
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create"), |
|||
multiTenancySide: MultiTenancySides.Tenant //set multi-tenancy side! |
|||
); |
|||
``` |
|||
|
|||
#### Enable/Disable Permissions |
|||
|
|||
A permission is enabled by default. It is possible to disable a permission. A disabled permission will be prohibited for everyone. You can still check for the permission, but it will always return prohibited. |
|||
|
|||
Example definition: |
|||
|
|||
````csharp |
|||
myGroup.AddPermission("Author_Management", isEnabled: false); |
|||
```` |
|||
|
|||
You normally don't need to define a disabled permission (unless you temporary want disable a feature of your application). However, you may want to disable a permission defined in a depended module. In this way you can disable the related application functionality. See the "*Changing Permission Definitions of a Depended Module*" section below for an example usage. |
|||
|
|||
> Note: Checking an undefined permission will throw an exception while a disabled permission check simply returns prohibited (false). |
|||
|
|||
#### Child Permissions |
|||
|
|||
A permission may have child permissions. It is especially useful when you want to create a hierarchical permission tree where a permission may have additional sub permissions which are available only if the parent permission has been granted. |
|||
|
|||
Example definition: |
|||
|
|||
```csharp |
|||
var authorManagement = myGroup.AddPermission("Author_Management"); |
|||
authorManagement.AddChild("Author_Management_Create_Books"); |
|||
authorManagement.AddChild("Author_Management_Edit_Books"); |
|||
authorManagement.AddChild("Author_Management_Delete_Books"); |
|||
``` |
|||
|
|||
The result on the UI is shown below (you probably want to localize permissions for your application): |
|||
|
|||
 |
|||
|
|||
For the example code, it is assumed that a role/user with "Author_Management" permission granted may have additional permissions. Then a typical application service that checks permissions can be defined as shown below: |
|||
|
|||
```csharp |
|||
[Authorize("Author_Management")] |
|||
public class AuthorAppService : ApplicationService, IAuthorAppService |
|||
{ |
|||
public Task<List<AuthorDto>> GetListAsync() |
|||
{ |
|||
... |
|||
} |
|||
|
|||
public Task<AuthorDto> GetAsync(Guid id) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
[Authorize("Author_Management_Create_Books")] |
|||
public Task CreateAsync(CreateAuthorDto input) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
[Authorize("Author_Management_Edit_Books")] |
|||
public Task UpdateAsync(CreateAuthorDto input) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
[Authorize("Author_Management_Delete_Books")] |
|||
public Task DeleteAsync(CreateAuthorDto input) |
|||
{ |
|||
... |
|||
} |
|||
} |
|||
``` |
|||
|
|||
- `GetListAsync` and `GetAsync` will be available to users if they have `Author_Management` permission is granted. |
|||
- Other methods require additional permissions. |
|||
|
|||
### Overriding a Permission by a Custom Policy |
|||
|
|||
If you define and register a policy to the ASP.NET Core authorization system with the same name of a permission, your policy will override the existing permission. This is a powerful way to extend the authorization for a pre-built module that you are using in your application. |
|||
|
|||
See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy. |
|||
|
|||
### Changing Permission Definitions of a Depended Module |
|||
|
|||
A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions. |
|||
|
|||
Example: |
|||
|
|||
````csharp |
|||
context |
|||
.GetPermissionOrNull(IdentityPermissions.Roles.Delete) |
|||
.IsEnabled = false; |
|||
```` |
|||
|
|||
When you write this code inside your permission definition provider, it finds the "role deletion" permission of the [Identity Module](Modules/Identity.md) and disabled the permission, so no one can delete a role on the application. |
|||
|
|||
> Tip: It is better to check the value returned by the `GetPermissionOrNull` method since it may return null if the given permission was not defined. |
|||
|
|||
### Permission Depending on a Condition |
|||
|
|||
You may want to disable a permission based on a condition. Disabled permissions are not visible on the UI and always returns `prohibited` when you check them. There are two built-in conditional dependencies for a permission definition; |
|||
|
|||
* A permission can be automatically disabled if a [Feature](Features.md) was disabled. |
|||
* A permission can be automatically disabled if a [Global Feature](Global-Features.md) was disabled. |
|||
|
|||
In addition, you can create your custom extensions. |
|||
|
|||
#### Depending on a Features |
|||
|
|||
Use the `RequireFeatures` extension method on your permission definition to make the permission available only if a given feature is enabled: |
|||
|
|||
````csharp |
|||
myGroup.AddPermission("Book_Creation") |
|||
.RequireFeatures("BookManagement"); |
|||
```` |
|||
|
|||
#### Depending on a Global Feature |
|||
|
|||
Use the `RequireGlobalFeatures` extension method on your permission definition to make the permission available only if a given feature is enabled: |
|||
|
|||
````csharp |
|||
myGroup.AddPermission("Book_Creation") |
|||
.RequireGlobalFeatures("BookManagement"); |
|||
```` |
|||
|
|||
#### Creating a Custom Permission Dependency |
|||
|
|||
`PermissionDefinition` supports state check, Please refer to [Simple State Checker's documentation](SimpleStateChecker.md) |
|||
|
|||
## IAuthorizationService |
|||
|
|||
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization. |
|||
|
|||
Example: |
|||
|
|||
```csharp |
|||
public async Task CreateAsync(CreateAuthorDto input) |
|||
{ |
|||
var result = await AuthorizationService |
|||
.AuthorizeAsync("Author_Management_Create_Books"); |
|||
if (result.Succeeded == false) |
|||
{ |
|||
//throw exception |
|||
throw new AbpAuthorizationException("..."); |
|||
} |
|||
|
|||
//continue to the normal flow... |
|||
} |
|||
``` |
|||
|
|||
> `AuthorizationService` is available as a property when you derive from ABP's `ApplicationService` base class. Since it is widely used in application services, `ApplicationService` pre-injects it for you. Otherwise, you can directly [inject](Dependency-Injection.md) it into your class. |
|||
|
|||
Since this is a typical code block, ABP provides extension methods to simplify it. |
|||
|
|||
Example: |
|||
|
|||
```csharp |
|||
public async Task CreateAsync(CreateAuthorDto input) |
|||
{ |
|||
await AuthorizationService.CheckAsync("Author_Management_Create_Books"); |
|||
|
|||
//continue to the normal flow... |
|||
} |
|||
``` |
|||
|
|||
`CheckAsync` extension method throws `AbpAuthorizationException` if the current user/client is not granted for the given permission. There is also `IsGrantedAsync` extension method that returns `true` or `false`. |
|||
|
|||
`IAuthorizationService` has some overloads for the `AuthorizeAsync` method. These are explained in the [ASP.NET Core authorization documentation](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction). |
|||
|
|||
> Tip: Prefer to use the `Authorize` attribute wherever possible, since it is declarative & simple. Use `IAuthorizationService` if you need to conditionally check a permission and run a business code based on the permission check. |
|||
|
|||
## Check a Permission in JavaScript |
|||
|
|||
See the following documents to learn how to re-use the authorization system on the client side: |
|||
|
|||
* [ASP.NET Core MVC / Razor Pages UI: Authorization](UI/AspNetCore/JavaScript-API/Auth.md) |
|||
* [Angular UI Authorization](UI/Angular/Permission-Management.md) |
|||
* [Blazor UI Authorization](UI/Blazor/Authorization.md) |
|||
|
|||
## Permission Management |
|||
|
|||
Permission management is normally done by an admin user using the permission management modal: |
|||
|
|||
 |
|||
|
|||
If you need to manage permissions by code, inject the `IPermissionManager` and use as shown below: |
|||
|
|||
```csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IPermissionManager _permissionManager; |
|||
|
|||
public MyService(IPermissionManager permissionManager) |
|||
{ |
|||
_permissionManager = permissionManager; |
|||
} |
|||
|
|||
public async Task GrantPermissionForUserAsync(Guid userId, string permissionName) |
|||
{ |
|||
await _permissionManager.SetForUserAsync(userId, permissionName, true); |
|||
} |
|||
|
|||
public async Task ProhibitPermissionForUserAsync(Guid userId, string permissionName) |
|||
{ |
|||
await _permissionManager.SetForUserAsync(userId, permissionName, false); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
`SetForUserAsync` sets the value (true/false) for a permission of a user. There are more extension methods like `SetForRoleAsync` and `SetForClientAsync`. |
|||
|
|||
`IPermissionManager` is defined by the permission management module. See the [permission management module documentation](Modules/Permission-Management.md) for more information. |
|||
|
|||
## Advanced Topics |
|||
|
|||
### Permission Value Providers |
|||
|
|||
Permission checking system is extensible. Any class derived from `PermissionValueProvider` (or implements `IPermissionValueProvider`) can contribute to the permission check. There are three pre-defined value providers: |
|||
|
|||
- `UserPermissionValueProvider` checks if the current user is granted for the given permission. It gets user id from the current claims. User claim name is defined with the `AbpClaimTypes.UserId` static property. |
|||
- `RolePermissionValueProvider` checks if any of the roles of the current user is granted for the given permission. It gets role names from the current claims. Role claims name is defined with the `AbpClaimTypes.Role` static property. |
|||
- `ClientPermissionValueProvider` checks if the current client is granted for the given permission. This is especially useful on a machine to machine interaction where there is no current user. It gets the client id from the current claims. Client claim name is defined with the `AbpClaimTypes.ClientId` static property. |
|||
|
|||
You can extend the permission checking system by defining your own permission value provider. |
|||
|
|||
Example: |
|||
|
|||
```csharp |
|||
public class SystemAdminPermissionValueProvider : PermissionValueProvider |
|||
{ |
|||
public SystemAdminPermissionValueProvider(IPermissionStore permissionStore) |
|||
: base(permissionStore) |
|||
{ |
|||
} |
|||
|
|||
public override string Name => "SystemAdmin"; |
|||
|
|||
public async override Task<PermissionGrantResult> |
|||
CheckAsync(PermissionValueCheckContext context) |
|||
{ |
|||
if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin") |
|||
{ |
|||
return PermissionGrantResult.Granted; |
|||
} |
|||
|
|||
return PermissionGrantResult.Undefined; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
This provider allows for all permissions to a user with a `User_Type` claim that has `SystemAdmin` value. It is common to use current claims and `IPermissionStore` in a permission value provider. |
|||
|
|||
A permission value provider should return one of the following values from the `CheckAsync` method: |
|||
|
|||
- `PermissionGrantResult.Granted` is returned to grant the user for the permission. If any of the providers return `Granted`, the result will be `Granted`, if no other provider returns `Prohibited`. |
|||
- `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return. |
|||
- `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission. |
|||
|
|||
Once a provider is defined, it should be added to the `AbpPermissionOptions` as shown below: |
|||
|
|||
```csharp |
|||
Configure<AbpPermissionOptions>(options => |
|||
{ |
|||
options.ValueProviders.Add<SystemAdminPermissionValueProvider>(); |
|||
}); |
|||
``` |
|||
|
|||
### Permission Store |
|||
|
|||
`IPermissionStore` is the only interface that needs to be implemented to read the value of permissions from a persistence source, generally a database system. The Permission Management module implements it and pre-installed in the application startup template. See the [permission management module documentation](Modules/Permission-Management.md) for more information |
|||
|
|||
### AlwaysAllowAuthorizationService |
|||
|
|||
`AlwaysAllowAuthorizationService` is a class that is used to bypass the authorization service. It is generally used in integration tests where you may want to disable the authorization system. |
|||
|
|||
Use `IServiceCollection.AddAlwaysAllowAuthorization()` extension method to register the `AlwaysAllowAuthorizationService` to the [dependency injection](Dependency-Injection.md) system: |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAlwaysAllowAuthorization(); |
|||
} |
|||
``` |
|||
|
|||
This is already done for the startup template integration tests. |
|||
|
|||
### Claims Principal Factory |
|||
|
|||
Claims are important elements of authentication and authorization. ABP uses the `IAbpClaimsPrincipalFactory` service to create claims on authentication. This service was designed as extensible. If you need to add your custom claims to the authentication ticket, you can implement the `IAbpClaimsPrincipalContributor` in your application. |
|||
|
|||
**Example: Add a `SocialSecurityNumber` claim and get it:** |
|||
|
|||
```csharp |
|||
public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency |
|||
{ |
|||
public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context) |
|||
{ |
|||
var identity = context.ClaimsPrincipal.Identities.FirstOrDefault(); |
|||
var userId = identity?.FindUserId(); |
|||
if (userId.HasValue) |
|||
{ |
|||
var userService = context.ServiceProvider.GetRequiredService<IUserService>(); //Your custom service |
|||
var socialSecurityNumber = await userService.GetSocialSecurityNumberAsync(userId.Value); |
|||
if (socialSecurityNumber != null) |
|||
{ |
|||
identity.AddClaim(new Claim("SocialSecurityNumber", socialSecurityNumber)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
public static class CurrentUserExtensions |
|||
{ |
|||
public static string GetSocialSecurityNumber(this ICurrentUser currentUser) |
|||
{ |
|||
return currentUser.FindClaimValue("SocialSecurityNumber"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> If you use Identity Server please add your claims to `RequestedClaims` of `AbpClaimsServiceOptions`. |
|||
|
|||
```csharp |
|||
Configure<AbpClaimsServiceOptions>(options => |
|||
{ |
|||
options.RequestedClaims.AddRange(new[]{ "SocialSecurityNumber" }); |
|||
}); |
|||
``` |
|||
|
|||
## See Also |
|||
|
|||
* [Permission Management Module](Modules/Permission-Management.md) |
|||
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](UI/AspNetCore/JavaScript-API/Auth.md) |
|||
* [Permission Management in Angular UI](UI/Angular/Permission-Management.md) |
|||
* [Video tutorial](https://abp.io/video-courses/essentials/authorization) |
|||