mirror of https://github.com/abpframework/abp.git
committed by
GitHub
150 changed files with 36038 additions and 1139 deletions
@ -0,0 +1,186 @@ |
|||
# 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 prázdnou AspNet Core Web aplikaci ve Visual Studio: |
|||
|
|||
 |
|||
|
|||
2. Zvolte prázdnou šablonu |
|||
|
|||
 |
|||
|
|||
Můžete zvolit i jinou šablonu, ale pro demonstraci je lepší čístý projekt. |
|||
|
|||
## 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.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Modularity; |
|||
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(); |
|||
} |
|||
|
|||
app.UseMvcWithDefaultRoute(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
``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ý modul. V kódu výše, náš ``AppModule`` má závislost na ``AbpAspNetCoreMvcModule`` (definován v balíku 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 System; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace BasicAspNetCoreApplication |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public IServiceProvider ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AppModule>(); |
|||
|
|||
return services.BuildServiceProviderFromFactory(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
Změnili jsme metodu ``ConfigureServices`` aby vracela ``IServiceProvider`` místo ``void``. Tato změna nám dovoluje nahradit AspNet Core vkládání závislostí za jiný framework (více v sekci Autofac integrace níže). ``services.AddApplication<AppModule>()`` přidává všechny služby definované ve všech modulech počínaje ``AppModule``. |
|||
|
|||
Volání ``app.InitializeApplication()`` v metodě ``Configure`` inicializuje a spustí aplikaci. |
|||
|
|||
## Ahoj světe! |
|||
|
|||
Aplikace výše zatím nic nedělá. Pojďme proto vytvořit MVC controller, který už něco dělá: |
|||
|
|||
````C# |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace BasicAspNetCoreApplication.Controllers |
|||
{ |
|||
public class HomeController : AbpController |
|||
{ |
|||
public IActionResult Index() |
|||
{ |
|||
return Content("Hello World!"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
```` |
|||
|
|||
Jakmile spustíte aplikaci, uvidíte na stránce zprávu "Hello World!". |
|||
|
|||
Odvození ``HomeController`` od ``AbpController`` místo standardní třídy ``Controller``. Toto není vyžadováno, ale třída ``AbpController`` má užitečné základní vlastnosti a metody, které usnadňují vývoj. |
|||
|
|||
## 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) 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 |
|||
```` |
|||
|
|||
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. Změníme řádek ``services.AddApplication<AppModule>();`` v třídě ``Startup`` následovně: |
|||
|
|||
````C# |
|||
services.AddApplication<AppModule>(options => |
|||
{ |
|||
options.UseAutofac(); // Integrace s Autofac |
|||
}); |
|||
```` |
|||
|
|||
4. Upravíme `Program.cs` aby nepoužíval metodu `WebHost.CreateDefaultBuilder()` jelikož ta používá výchozí DI kontejner: |
|||
|
|||
````csharp |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
/* |
|||
https://github.com/aspnet/AspNetCore/issues/4206#issuecomment-445612167 |
|||
CurrentDirectoryHelpers exists in: \framework\src\Volo.Abp.AspNetCore.Mvc\Microsoft\AspNetCore\InProcess\CurrentDirectoryHelpers.cs |
|||
Will remove CurrentDirectoryHelpers.cs when upgrade to ASP.NET Core 3.0. |
|||
*/ |
|||
CurrentDirectoryHelpers.SetCurrentDirectory(); |
|||
|
|||
BuildWebHostInternal(args).Run(); |
|||
} |
|||
|
|||
public static IWebHost BuildWebHostInternal(string[] args) => |
|||
new WebHostBuilder() |
|||
.UseKestrel() |
|||
.UseContentRoot(Directory.GetCurrentDirectory()) |
|||
.UseIIS() |
|||
.UseIISIntegration() |
|||
.UseStartup<Startup>() |
|||
.Build(); |
|||
} |
|||
```` |
|||
|
|||
## 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/tree/master/samples/BasicAspNetCoreApplication). |
|||
|
|||
@ -0,0 +1,102 @@ |
|||
## 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 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/) |
|||
* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/) |
|||
|
|||
### 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 MVC aplikace](Startup-Templates/Mvc.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) |
|||
@ -0,0 +1,181 @@ |
|||
# 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/tree/master/samples/BasicConsoleApplication). |
|||
@ -1,13 +0,0 @@ |
|||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Client |
|||
{ |
|||
public static class CachedApplicationConfigurationClientExtensions |
|||
{ |
|||
public static ApplicationConfigurationDto Get(this ICachedApplicationConfigurationClient client) |
|||
{ |
|||
return AsyncHelper.RunSync(client.GetAsync); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
(function ($) { |
|||
$('#GlobalRefreshButton').on('click', |
|||
function () { |
|||
$(document).trigger('RefreshWidgets', $('#DashboardGlobalFiltersForm').serializeFormToObject()); |
|||
}); |
|||
})(jQuery); |
|||
@ -0,0 +1,15 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class DashboardGlobalFilterConfiguration |
|||
{ |
|||
[NotNull] |
|||
public string GlobalFilterName { get; } |
|||
|
|||
public DashboardGlobalFilterConfiguration([NotNull] string globalFilterName) |
|||
{ |
|||
GlobalFilterName = Check.NotNullOrWhiteSpace(globalFilterName, nameof(globalFilterName)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class GlobalFilterDefinition |
|||
{ |
|||
/// <summary>
|
|||
/// Unique name of the Global Filter.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Display name of the Global Filter.
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public ILocalizableString DisplayName |
|||
{ |
|||
get => _displayName; |
|||
set => _displayName = Check.NotNull(value, nameof(value)); |
|||
} |
|||
private ILocalizableString _displayName; |
|||
|
|||
[NotNull] |
|||
public Type ViewComponentType { get; } |
|||
|
|||
public GlobalFilterDefinition( |
|||
[NotNull] string name, |
|||
[CanBeNull] ILocalizableString displayName, |
|||
[NotNull] Type viewComponentType) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
DisplayName = displayName ?? new FixedLocalizableString(name); |
|||
ViewComponentType = Check.NotNull(viewComponentType, nameof(viewComponentType)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class GlobalFilterOptions |
|||
{ |
|||
public List<GlobalFilterDefinition> GlobalFilters { get; } |
|||
|
|||
public GlobalFilterOptions() |
|||
{ |
|||
GlobalFilters = new List<GlobalFilterDefinition>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public class GlobalFilterRenderer : IGlobalFilterRenderer |
|||
{ |
|||
private readonly GlobalFilterOptions _globalFilterOptions; |
|||
|
|||
public GlobalFilterRenderer(IOptions<GlobalFilterOptions> widgetOptions) |
|||
{ |
|||
_globalFilterOptions = widgetOptions.Value; |
|||
} |
|||
|
|||
public async Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, string globalFilterName, object args = null) |
|||
{ |
|||
var globalFilter = _globalFilterOptions.GlobalFilters.Single(w => w.Name.Equals(globalFilterName)); |
|||
|
|||
return await componentHelper.InvokeAsync(globalFilter.ViewComponentType, args ?? new object()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Html; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards |
|||
{ |
|||
public interface IGlobalFilterRenderer : ITransientDependency |
|||
{ |
|||
Task<IHtmlContent> RenderAsync(IViewComponentHelper componentHelper, string globalFilterName, object args = null); |
|||
} |
|||
} |
|||
@ -1,168 +0,0 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Application.Services |
|||
{ |
|||
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey> |
|||
: AsyncCrudAppService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto> |
|||
where TEntity : class, IEntity<TKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) |
|||
: base(repository) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput> |
|||
: AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntity : class, IEntity<TKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) |
|||
: base(repository) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput> |
|||
: AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntity : class, IEntity<TKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) |
|||
: base(repository) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
public abstract class AsyncCrudAppService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
|||
: AsyncCrudAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
|||
where TEntity : class, IEntity<TKey> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) |
|||
: base(repository) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override TEntityDto MapToGetListOutputDto(TEntity entity) |
|||
{ |
|||
return MapToGetOutputDto(entity); |
|||
} |
|||
} |
|||
|
|||
public abstract class AsyncCrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
|||
: CrudAppServiceBase<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>, |
|||
IAsyncCrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
|||
where TEntity : class, IEntity<TKey> |
|||
where TGetOutputDto : IEntityDto<TKey> |
|||
where TGetListOutputDto : IEntityDto<TKey> |
|||
{ |
|||
public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } |
|||
|
|||
protected AsyncCrudAppService(IRepository<TEntity, TKey> repository) |
|||
: base(repository) |
|||
{ |
|||
AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance; |
|||
} |
|||
|
|||
public virtual async Task<TGetOutputDto> GetAsync(TKey id) |
|||
{ |
|||
await CheckGetPolicyAsync(); |
|||
|
|||
var entity = await GetEntityByIdAsync(id); |
|||
return MapToGetOutputDto(entity); |
|||
} |
|||
|
|||
public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input) |
|||
{ |
|||
await CheckGetListPolicyAsync(); |
|||
|
|||
var query = CreateFilteredQuery(input); |
|||
|
|||
var totalCount = await AsyncQueryableExecuter.CountAsync(query); |
|||
|
|||
query = ApplySorting(query, input); |
|||
query = ApplyPaging(query, input); |
|||
|
|||
var entities = await AsyncQueryableExecuter.ToListAsync(query); |
|||
|
|||
return new PagedResultDto<TGetListOutputDto>( |
|||
totalCount, |
|||
entities.Select(MapToGetListOutputDto).ToList() |
|||
); |
|||
} |
|||
|
|||
public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input) |
|||
{ |
|||
await CheckCreatePolicyAsync(); |
|||
|
|||
var entity = MapToEntity(input); |
|||
|
|||
TryToSetTenantId(entity); |
|||
|
|||
await Repository.InsertAsync(entity, autoSave: true); |
|||
|
|||
return MapToGetOutputDto(entity); |
|||
} |
|||
|
|||
public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input) |
|||
{ |
|||
await CheckUpdatePolicyAsync(); |
|||
|
|||
var entity = await GetEntityByIdAsync(id); |
|||
//TODO: Check if input has id different than given id and normalize if it's default value, throw ex otherwise
|
|||
MapToEntity(input, entity); |
|||
await Repository.UpdateAsync(entity, autoSave: true); |
|||
|
|||
return MapToGetOutputDto(entity); |
|||
} |
|||
|
|||
public virtual async Task DeleteAsync(TKey id) |
|||
{ |
|||
await CheckDeletePolicyAsync(); |
|||
|
|||
await Repository.DeleteAsync(id); |
|||
} |
|||
|
|||
protected virtual Task<TEntity> GetEntityByIdAsync(TKey id) |
|||
{ |
|||
return Repository.GetAsync(id); |
|||
} |
|||
|
|||
protected virtual async Task CheckGetPolicyAsync() |
|||
{ |
|||
await CheckPolicyAsync(GetPolicyName); |
|||
} |
|||
|
|||
protected virtual async Task CheckGetListPolicyAsync() |
|||
{ |
|||
await CheckPolicyAsync(GetListPolicyName); |
|||
} |
|||
|
|||
protected virtual async Task CheckCreatePolicyAsync() |
|||
{ |
|||
await CheckPolicyAsync(CreatePolicyName); |
|||
} |
|||
|
|||
protected virtual async Task CheckUpdatePolicyAsync() |
|||
{ |
|||
await CheckPolicyAsync(UpdatePolicyName); |
|||
} |
|||
|
|||
protected virtual async Task CheckDeletePolicyAsync() |
|||
{ |
|||
await CheckPolicyAsync(DeletePolicyName); |
|||
} |
|||
} |
|||
} |
|||
@ -1,191 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.ObjectMapping; |
|||
|
|||
namespace Volo.Abp.Application.Services |
|||
{ |
|||
/// <summary>
|
|||
/// This is a common base class for CrudAppService and AsyncCrudAppService classes.
|
|||
/// Inherit either from CrudAppService or AsyncCrudAppService, not from this class.
|
|||
/// </summary>
|
|||
public abstract class CrudAppServiceBase<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> : |
|||
ApplicationService |
|||
where TEntity : class, IEntity<TKey> |
|||
where TGetOutputDto : IEntityDto<TKey> |
|||
where TGetListOutputDto : IEntityDto<TKey> |
|||
{ |
|||
protected IRepository<TEntity, TKey> Repository { get; } |
|||
|
|||
protected virtual string GetPolicyName { get; set; } |
|||
|
|||
protected virtual string GetListPolicyName { get; set; } |
|||
|
|||
protected virtual string CreatePolicyName { get; set; } |
|||
|
|||
protected virtual string UpdatePolicyName { get; set; } |
|||
|
|||
protected virtual string DeletePolicyName { get; set; } |
|||
|
|||
protected CrudAppServiceBase(IRepository<TEntity, TKey> repository) |
|||
{ |
|||
Repository = repository; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Should apply sorting if needed.
|
|||
/// </summary>
|
|||
/// <param name="query">The query.</param>
|
|||
/// <param name="input">The input.</param>
|
|||
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetListInput input) |
|||
{ |
|||
//Try to sort query if available
|
|||
var sortInput = input as ISortedResultRequest; |
|||
if (sortInput != null) |
|||
{ |
|||
if (!sortInput.Sorting.IsNullOrWhiteSpace()) |
|||
{ |
|||
return query.OrderBy(sortInput.Sorting); |
|||
} |
|||
} |
|||
|
|||
//IQueryable.Task requires sorting, so we should sort if Take will be used.
|
|||
if (input is ILimitedResultRequest) |
|||
{ |
|||
return query.OrderByDescending(e => e.Id); |
|||
} |
|||
|
|||
//No sorting
|
|||
return query; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Should apply paging if needed.
|
|||
/// </summary>
|
|||
/// <param name="query">The query.</param>
|
|||
/// <param name="input">The input.</param>
|
|||
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetListInput input) |
|||
{ |
|||
//Try to use paging if available
|
|||
var pagedInput = input as IPagedResultRequest; |
|||
if (pagedInput != null) |
|||
{ |
|||
return query.PageBy(pagedInput); |
|||
} |
|||
|
|||
//Try to limit query result if available
|
|||
var limitedInput = input as ILimitedResultRequest; |
|||
if (limitedInput != null) |
|||
{ |
|||
return query.Take(limitedInput.MaxResultCount); |
|||
} |
|||
|
|||
//No paging
|
|||
return query; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This method should create <see cref="IQueryable{TEntity}"/> based on given input.
|
|||
/// It should filter query if needed, but should not do sorting or paging.
|
|||
/// Sorting should be done in <see cref="ApplySorting"/> and paging should be done in <see cref="ApplyPaging"/>
|
|||
/// methods.
|
|||
/// </summary>
|
|||
/// <param name="input">The input.</param>
|
|||
protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input) |
|||
{ |
|||
return Repository; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
|
|||
/// It uses <see cref="IObjectMapper"/> by default.
|
|||
/// It can be overriden for custom mapping.
|
|||
/// </summary>
|
|||
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity) |
|||
{ |
|||
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
|
|||
/// It uses <see cref="IObjectMapper"/> by default.
|
|||
/// It can be overriden for custom mapping.
|
|||
/// </summary>
|
|||
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity) |
|||
{ |
|||
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Maps <see cref="TCreateInput"/> to <see cref="TEntity"/> to create a new entity.
|
|||
/// It uses <see cref="IObjectMapper"/> by default.
|
|||
/// It can be overriden for custom mapping.
|
|||
/// </summary>
|
|||
protected virtual TEntity MapToEntity(TCreateInput createInput) |
|||
{ |
|||
var entity = ObjectMapper.Map<TCreateInput, TEntity>(createInput); |
|||
SetIdForGuids(entity); |
|||
return entity; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets Id value for the entity if <see cref="TKey"/> is <see cref="Guid"/>.
|
|||
/// It's used while creating a new entity.
|
|||
/// </summary>
|
|||
protected virtual void SetIdForGuids(TEntity entity) |
|||
{ |
|||
var entityWithGuidId = entity as IEntity<Guid>; |
|||
|
|||
if (entityWithGuidId == null || entityWithGuidId.Id != Guid.Empty) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
entityWithGuidId.Id = GuidGenerator.Create(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Maps <see cref="TUpdateInput"/> to <see cref="TEntity"/> to update the entity.
|
|||
/// It uses <see cref="IObjectMapper"/> by default.
|
|||
/// It can be overriden for custom mapping.
|
|||
/// </summary>
|
|||
protected virtual void MapToEntity(TUpdateInput updateInput, TEntity entity) |
|||
{ |
|||
if (updateInput is IEntityDto<TKey> entityDto) |
|||
{ |
|||
entityDto.Id = entity.Id; |
|||
} |
|||
|
|||
ObjectMapper.Map(updateInput, entity); |
|||
} |
|||
|
|||
protected virtual void TryToSetTenantId(TEntity entity) |
|||
{ |
|||
if (entity is IMultiTenant && HasTenantIdProperty(entity)) |
|||
{ |
|||
var tenantId = CurrentTenant.Id; |
|||
|
|||
if (!tenantId.HasValue) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var propertyInfo = entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)); |
|||
|
|||
if (propertyInfo != null && propertyInfo.GetSetMethod() != null) |
|||
{ |
|||
propertyInfo.SetValue(entity, tenantId, null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual bool HasTenantIdProperty(TEntity entity) |
|||
{ |
|||
return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.Application.Services |
|||
{ |
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, PagedAndSortedResultRequestDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, TGetListInput, TEntityDto, TEntityDto> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput> |
|||
: IAsyncCrudAppService<TEntityDto, TKey, TGetListInput, TCreateInput, TCreateInput> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TEntityDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
: IAsyncCrudAppService<TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput> |
|||
where TEntityDto : IEntityDto<TKey> |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IAsyncCrudAppService<TGetOutputDto, TGetListOutputDto, in TKey, in TGetListInput, in TCreateInput, in TUpdateInput> |
|||
: IApplicationService |
|||
where TGetOutputDto : IEntityDto<TKey> |
|||
where TGetListOutputDto : IEntityDto<TKey> |
|||
{ |
|||
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); |
|||
} |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.EventBus |
|||
{ |
|||
public static class EventBusExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Triggers an event.
|
|||
/// </summary>
|
|||
/// <typeparam name="TEvent">Event type</typeparam>
|
|||
/// <param name="eventBus">Event bus instance</param>
|
|||
/// <param name="eventData">Related data for the event</param>
|
|||
public static void Publish<TEvent>([NotNull] this IEventBus eventBus, [NotNull] TEvent eventData) |
|||
where TEvent : class |
|||
{ |
|||
Check.NotNull(eventBus, nameof(eventBus)); |
|||
|
|||
AsyncHelper.RunSync(() => eventBus.PublishAsync(eventData)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Triggers an event.
|
|||
/// </summary>
|
|||
/// <param name="eventBus">Event bus instance</param>
|
|||
/// <param name="eventType">Event type</param>
|
|||
/// <param name="eventData">Related data for the event</param>
|
|||
public static void Publish([NotNull] this IEventBus eventBus, [NotNull] Type eventType, [NotNull] object eventData) |
|||
{ |
|||
Check.NotNull(eventBus, nameof(eventBus)); |
|||
|
|||
AsyncHelper.RunSync(() => eventBus.PublishAsync(eventType, eventData)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace Volo.Abp.Json |
|||
{ |
|||
public class AbpJsonOptions |
|||
{ |
|||
public JsonSerializerSettings SerializerSettings { get; } = new JsonSerializerSettings(); |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public static class LanguageProviderExtensions |
|||
{ |
|||
public static IReadOnlyList<LanguageInfo> GetLanguages(this ILanguageProvider languageProvider) |
|||
{ |
|||
return AsyncHelper.RunSync(languageProvider.GetLanguagesAsync); |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public static class TenantStoreExtensions |
|||
{ |
|||
[CanBeNull] |
|||
public static TenantConfiguration Find(this ITenantStore tenantStore, string name) |
|||
{ |
|||
return AsyncHelper.RunSync(() => tenantStore.FindAsync(name)); |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public static TenantConfiguration Find(this ITenantStore tenantStore, Guid id) |
|||
{ |
|||
return AsyncHelper.RunSync(() => tenantStore.FindAsync(id)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
public static class RunnableExtensions |
|||
{ |
|||
public static void Start([NotNull] this IRunnable runnable) |
|||
{ |
|||
Check.NotNull(runnable, nameof(runnable)); |
|||
|
|||
AsyncHelper.RunSync(() => runnable.StartAsync()); |
|||
} |
|||
|
|||
public static void Stop([NotNull] this IRunnable runnable) |
|||
{ |
|||
Check.NotNull(runnable, nameof(runnable)); |
|||
|
|||
AsyncHelper.RunSync(() => runnable.StopAsync()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json |
|||
{ |
|||
[Route("api/json-result-test")] |
|||
public class JsonResultController : AbpController |
|||
{ |
|||
[HttpGet] |
|||
[Route("json-result-action")] |
|||
public Task<JsonResultModel> ObjectResultAction() |
|||
{ |
|||
return Task.FromResult(new JsonResultModel |
|||
{ |
|||
Time = DateTime.Parse("2019-01-01 11:59:59") |
|||
}); |
|||
} |
|||
|
|||
public class JsonResultModel |
|||
{ |
|||
public DateTime Time { get; set; } |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Json; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json |
|||
{ |
|||
public class JsonResultController_Tests : AspNetCoreMvcTestBase |
|||
{ |
|||
protected override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpJsonOptions>(options => |
|||
{ |
|||
options.SerializerSettings.DateFormatString = "yyyy*MM*dd"; |
|||
}); |
|||
|
|||
base.ConfigureServices(context, services); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DateFormatString_Test() |
|||
{ |
|||
var time = await GetResponseAsStringAsync( |
|||
"/api/json-result-test/json-result-action" |
|||
); |
|||
|
|||
time.ShouldContain("2019*01*01"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Json; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Json |
|||
{ |
|||
public class JsonSerializer_Tests : AspNetCoreMvcTestBase |
|||
{ |
|||
private readonly IJsonSerializer _jsonSerializer; |
|||
|
|||
public JsonSerializer_Tests() |
|||
{ |
|||
_jsonSerializer = ServiceProvider.GetRequiredService<IJsonSerializer>(); |
|||
} |
|||
|
|||
protected override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpJsonOptions>(options => |
|||
{ |
|||
options.SerializerSettings.DateFormatString = "yyyy*MM*dd"; |
|||
}); |
|||
|
|||
base.ConfigureServices(context, services); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DateFormatString_Test() |
|||
{ |
|||
var output = _jsonSerializer.Serialize(new |
|||
{ |
|||
Time = DateTime.Parse("2019-01-01 11:59:59") |
|||
}); |
|||
|
|||
output.ShouldContain("2019*01*01"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace DashboardDemo |
|||
{ |
|||
public class DemoStatisticAppService : ApplicationService, IDemoStatisticAppService |
|||
{ |
|||
public async Task<GetMonthlyUserStatisticDto> GetMonthlyUserStatistic(FilterDto filter) |
|||
{ |
|||
var monthCount = GetLabels(filter, out var monthList); |
|||
|
|||
var data = Enumerable |
|||
.Repeat(0, monthCount) |
|||
.Select(i => new Random().Next(750, 960)) |
|||
.ToArray(); |
|||
|
|||
return new GetMonthlyUserStatisticDto{Labels = monthList.ToArray(), Data = data}; |
|||
} |
|||
|
|||
public async Task<MonthlyProfitStatisticDto> GetMonthlyProfitStatistic(FilterDto filter) |
|||
{ |
|||
var monthCount = GetLabels(filter, out var monthList); |
|||
|
|||
var data = Enumerable |
|||
.Repeat(0, monthCount) |
|||
.Select(i => new Random().Next(-20, 40)) |
|||
.ToArray(); |
|||
|
|||
return new MonthlyProfitStatisticDto { Labels = monthList.ToArray(), Data = data }; |
|||
} |
|||
|
|||
private static int GetLabels(FilterDto filter, out List<string> monthList) |
|||
{ |
|||
DateTime endDate = filter.EndDate ?? DateTime.Now; |
|||
DateTime startDate = filter.StartDate ?? DateTime.Now.AddYears(-1); |
|||
|
|||
if (filter.StartDate > filter.EndDate) |
|||
{ |
|||
throw new BusinessException("Start date can not be greater than end date."); |
|||
} |
|||
|
|||
var months = new[] {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; |
|||
var monthCount = (endDate.Year - startDate.Year) * 12 + endDate.Month - startDate.Month +1; |
|||
monthList = new List<string>(); |
|||
|
|||
for (int i = 0; i < monthCount; i++) |
|||
{ |
|||
monthList.Add(months[endDate.Month-1]); |
|||
endDate = endDate.AddMonths(-1); |
|||
} |
|||
|
|||
|
|||
monthList.Reverse(); |
|||
|
|||
return monthCount; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace DashboardDemo |
|||
{ |
|||
public class FilterDto |
|||
{ |
|||
public DateTime? StartDate { get; set; } |
|||
|
|||
public DateTime? EndDate { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace DashboardDemo |
|||
{ |
|||
public class GetMonthlyUserStatisticDto |
|||
{ |
|||
public string[] Labels { get; set; } |
|||
|
|||
public int[] Data { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace DashboardDemo |
|||
{ |
|||
public interface IDemoStatisticAppService : IApplicationService |
|||
{ |
|||
Task<GetMonthlyUserStatisticDto> GetMonthlyUserStatistic(FilterDto filter); |
|||
|
|||
Task<MonthlyProfitStatisticDto> GetMonthlyProfitStatistic(FilterDto filter); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace DashboardDemo |
|||
{ |
|||
public class MonthlyProfitStatisticDto |
|||
{ |
|||
public string[] Labels { get; set; } |
|||
|
|||
public int[] Data { get; set; } |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue