From 10733a68f4a9579305a4f4a9a382f2d4a49e0275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 19:47:43 +0300 Subject: [PATCH 01/13] Initial SignalR-Integration document --- docs/en/SignalR-Integration.md | 102 ++++++++++++++++++++++++++++++ docs/en/images/signal-js-file.png | Bin 0 -> 57358 bytes 2 files changed, 102 insertions(+) create mode 100644 docs/en/SignalR-Integration.md create mode 100644 docs/en/images/signal-js-file.png diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md new file mode 100644 index 0000000000..cbfa165083 --- /dev/null +++ b/docs/en/SignalR-Integration.md @@ -0,0 +1,102 @@ +## SignalR Integration + +> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. + +## Installation + +### Server Side + +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 your project (.csproj file) and type the following command: + +```bash +abp add-package Volo.Abp.AspNetCore.SignalR +``` + +> You typically want to add this package to the web or API layer of your application, depending on your architecture. + +#### Manual Installation + +If you want to manually install; + +1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: + + ``` + Install-Package Volo.Abp.BackgroundJobs.HangFire + ``` + + Or use the Visual Studio NuGet package management UI to install it. + +2. Add the `AbpAspNetCoreSignalRModule` to the dependency list of your module: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpAspNetCoreSignalRModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +``` + +### Client Side + +Client side installation depends on your UI framework. + +#### ASP.NET Core MVC / Razor Pages UI + +Run the following command in the root folder of your web project: + +````bash +yarn add @abp/signalr +```` + +> This requires to [install yarn](https://yarnpkg.com/) if you haven't install before. + +This will add the `@abp/signalr` to the dependencies in the `package.json` of your project: + +````json +{ + ... + "dependencies": { + ... + "@abp/signalr": "~2.7.0" + } +} +```` + +Run the `gulp` in the root folder of your web project: + +````bash +gulp +```` + +This will copy the SignalR JavaScript files into your project: + +![signal-js-file](D:\Github\abp\docs\en\images\signal-js-file.png) + +Finally, add the following code to your page/view to include the `signalr.js` file + +````xml +@section scripts { + +} +```` + +It requires to add `@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR` to your page/view. + +> You could add the `signalr.js` file in a standard way. But using the `SignalRBrowserScriptContributor` has additional benefits. See the [Client Side Package Management](UI/AspNetCore/Client-Side-Package-Management.md) and [Bundling & Minification](UI/AspNetCore/Bundling-Minification.md) documents for details. + +That's all. you can use the [SignalR JavaScript API](https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client) in your page. + +#### Other UI Frameworks / Clients + +Please refer to [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) for other type of clients. + +## The ABP Framework Integration + +When you use the ABP Framework integration, you have some additional benefits. + diff --git a/docs/en/images/signal-js-file.png b/docs/en/images/signal-js-file.png new file mode 100644 index 0000000000000000000000000000000000000000..19fc6cefe3480204c2f0a31131cb50f1b2690d1d GIT binary patch literal 57358 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 LNkvXXu0mjf{{oRT literal 0 HcmV?d00001 From 51435d0c3d6c53e78b86e10edc2c88fd508126ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 20:05:30 +0300 Subject: [PATCH 02/13] Create HubRouteAttribute.GetRoutePatternForType() --- .../Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs index 5de916a89d..0373fbf691 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs @@ -13,6 +13,11 @@ namespace Volo.Abp.AspNetCore.SignalR RoutePattern = routePattern; } + public virtual string GetRoutePatternForType(Type hubType) + { + return RoutePattern; + } + public static string GetRoutePattern() where THub : Hub { @@ -24,7 +29,7 @@ namespace Volo.Abp.AspNetCore.SignalR var routeAttribute = hubType.GetSingleAttributeOrNull(); if (routeAttribute != null) { - return routeAttribute.RoutePattern; + return routeAttribute.GetRoutePatternForType(hubType); } return "/signalr-hubs/" + hubType.Name.RemovePostFix("Hub").ToKebabCase(); From 5651bbb8d088d63fed23405d7bd7f75e8cd02444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 20:05:40 +0300 Subject: [PATCH 03/13] Update SignalR-Integration.md --- docs/en/SignalR-Integration.md | 54 +++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index cbfa165083..585b47d76d 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -42,6 +42,8 @@ public class YourModule : AbpModule } ``` +> You don't need to use the `services.AddSignalR()` and the `app.UseEndpoints(...)`, it's done by the `AbpAspNetCoreSignalRModule`. + ### Client Side Client side installation depends on your UI framework. @@ -98,5 +100,55 @@ Please refer to [Microsoft's documentation](https://docs.microsoft.com/en-us/asp ## The ABP Framework Integration -When you use the ABP Framework integration, you have some additional benefits. +This section covers the additional benefits when you use the ABP Framework integration packages. + +### Hub Route & Mapping + +ABP automatically registers Hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. + +Example: + +````csharp +public class MessagingHub : Hub +{ + //... +} +```` + +The hub route will be `/signalr-hubs/messasing` for the `MessasingHub`: + +* Adding a standard `/signalr-hubs/` prefix +* Continue with the **camel case** hub name, without the `Hub` postfix. + +If you want to specify the route, you can use the `HubRoute` attribute: + +````csharp +[HubRoute("/my-messasing-hub")] +public class MessagingHub : Hub +{ + //... +} +```` + +### AbpHub Base Class + +Instead of the standard `Hub` class, you can inherit from the `AbpHub` which has useful base properties, like `CurrentUser`. + +Example: + +````csharp +public class MessagingHub : AbpHub +{ + public async Task SendMessage(string targetUserName, string message) + { + var currentUserName = CurrentUser.UserName; //Access to the current user info + var txt = L["MyText"]; //Localization + } +} +```` + + + +## Example Application +See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messaged between (authenticated) users. \ No newline at end of file From 22938921d303b0940ac5cc86833e57bcfbd4ce18 Mon Sep 17 00:00:00 2001 From: ChangYinShung Date: Wed, 13 May 2020 01:57:46 +0800 Subject: [PATCH 04/13] add Identity Module's Localize for zh-hant add description of RequireConfirmedPhoneNumber in zh-hant --- .../Volo/Abp/Identity/Localization/zh-Hant.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json index 240b7babdf..8ad18c7c1c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json @@ -82,6 +82,7 @@ "DisplayName:Abp.Identity.Lockout.LockoutDuration": "鎖定時間(秒)", "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "最大失敗存取嘗試次數", "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "要求驗證的電子信箱", + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "啟用手機號碼驗證", "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "要求驗證的手機號碼", "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "啟用使用者名稱更新", "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "啟用電子信箱更新", @@ -95,9 +96,9 @@ "Description:Abp.Identity.Lockout.LockoutDuration": "當鎖定發生時使用者被鎖定的時間(秒).", "Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "如果啟用鎖定,當使用者被鎖定前失敗的存取嘗試次數.", "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "登入時是否需要驗證電子信箱.", + "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "使用者手機號碼是否需要驗證.", "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登入時是否需要驗證手機號碼.", "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "是否允許使用者更新使用者名稱.", "Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允許使用者更新電子信箱." - } -} \ No newline at end of file +} From 8dd8805c82edde1b37cfb98bb66987974d08fea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 01:23:13 +0300 Subject: [PATCH 05/13] Resolved #3912: Finalize the signalr integration document. --- docs/en/SignalR-Integration.md | 83 +++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 585b47d76d..5b4176b123 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -1,4 +1,4 @@ -## SignalR Integration +# SignalR Integration > It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. @@ -46,7 +46,7 @@ public class YourModule : AbpModule ### Client Side -Client side installation depends on your UI framework. +Client side installation depends on your UI framework / client type. #### ASP.NET Core MVC / Razor Pages UI @@ -104,7 +104,7 @@ This section covers the additional benefits when you use the ABP Framework integ ### Hub Route & Mapping -ABP automatically registers Hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. +ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. Example: @@ -118,7 +118,7 @@ public class MessagingHub : Hub The hub route will be `/signalr-hubs/messasing` for the `MessasingHub`: * Adding a standard `/signalr-hubs/` prefix -* Continue with the **camel case** hub name, without the `Hub` postfix. +* Continue with the **camel case** hub name, without the `Hub` suffix. If you want to specify the route, you can use the `HubRoute` attribute: @@ -130,9 +130,9 @@ public class MessagingHub : Hub } ```` -### AbpHub Base Class +### AbpHub Base Classes -Instead of the standard `Hub` class, you can inherit from the `AbpHub` which has useful base properties, like `CurrentUser`. +Instead of the standard `Hub` and `Hub` classes, you can inherit from the `AbpHub` or `AbpHub` which hve useful base properties like `CurrentUser`. Example: @@ -147,7 +147,78 @@ public class MessagingHub : AbpHub } ```` +> While you could inject the same properties into your hub constructor, this way simplifies your hub class. +### Manual Registration / Mapping + +ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) as a **transient service**. If you want to **disable auto dependency injection** registration for your hub class, just add a `DisableConventionalRegistration` attribute. You can still register your hub class to dependency injection in the `ConfigureServices` method of your module if you like: + +````csharp +context.Services.AddTransient(); +```` + +When **you or ABP** register the class to the dependency injection, it is automatically mapped to the endpoint route configuration just as described in the previous sections. You can use `DisableAutoHubMap` attribute if you want to manually map your hub class. + +For manual mapping, you have two options: + +1. Use the `AbpSignalROptions` to add your map configuration (in the `ConfigureServices` method of your [module](Module-Development-Basics.md)), so ABP still performs the endpoint mapping for your hub: + +````csharp +Configure(options => +{ + options.Hubs.Add( + new HubConfig( + typeof(MessagingHub), //Hub type + "/my-messaging/route", //Hub route (URL) + hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + } + ) + ); +}); +```` + +This is a good way to provide additional SignalR options. + +If you don't want to disable auto hub map, but still want to perform additional SignalR configuration, use the `options.Hubs.AddOrUpdate(...)` method: + +````csharp +Configure(options => +{ + options.Hubs.AddOrUpdate( + typeof(MessagingHub), //Hub type + config => //Additional configuration + { + config.RoutePattern = "/my-messaging-hub"; //override the default route + config.ConfigureActions.Add(hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); + } + ); +}); +```` + +This is the way you can modify the options of a hub class defined in a depended module (where you don't have the source code access). + +2. Change `app.UseConfiguredEndpoints` in the `OnApplicationInitialization` method of your [module](Module-Development-Basics.md) as shown below (added a lambda method as the parameter). + +````csharp +app.UseConfiguredEndpoints(endpoints => +{ + endpoints.MapHub("/my-messaging-hub", options => + { + options.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); +}); +```` + +### UserIdProvider + +ABP implements SignalR's `IUserIdProvider` interface to provide the current user id from the `ICurrentUser` service of the ABP framework (see [the current user service](CurrentUser.md)), so it will be integrated to the authentication system of your application. The implementing class is the `AbpSignalRUserIdProvider`, if you want to change/override it. ## Example Application From 2619eada343b2a66f46323d7becde90edfe5bafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 01:24:51 +0300 Subject: [PATCH 06/13] Added HubConfigList and DisableAutoHubMapAttribute for the signalr integration. --- .../SignalR/AbpAspNetCoreSignalRModule.cs | 21 ++++++++++++---- .../AspNetCore/SignalR/AbpSignalROptions.cs | 8 +++---- .../SignalR/DisableAutoHubMapAttribute.cs | 9 +++++++ .../Volo/Abp/AspNetCore/SignalR/HubConfig.cs | 8 ++++++- .../Abp/AspNetCore/SignalR/HubConfigList.cs | 24 +++++++++++++++++++ 5 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs index e26de184af..b89e5e9158 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.SignalR @@ -30,7 +31,7 @@ namespace Volo.Abp.AspNetCore.SignalR public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddSignalR(); - + Configure(options => { options.EndpointConfigureActions.Add(endpointContext => @@ -65,7 +66,7 @@ namespace Volo.Abp.AspNetCore.SignalR services.OnRegistred(context => { - if (typeof(Hub).IsAssignableFrom(context.ImplementationType)) + if (IsHubClass(context) && !IsDisabledForAutoMap(context)) { hubTypes.Add(context.ImplementationType); } @@ -80,7 +81,17 @@ namespace Volo.Abp.AspNetCore.SignalR }); } - private void MapHubType( + private static bool IsHubClass(IOnServiceRegistredContext context) + { + return typeof(Hub).IsAssignableFrom(context.ImplementationType); + } + + private static bool IsDisabledForAutoMap(IOnServiceRegistredContext context) + { + return context.ImplementationType.IsDefined(typeof(DisableAutoHubMapAttribute), true); + } + + private void MapHubType( Type hubType, IEndpointRouteBuilder endpoints, string pattern, @@ -101,8 +112,8 @@ namespace Volo.Abp.AspNetCore.SignalR // ReSharper disable once UnusedMember.Local (used via reflection) private static void MapHub( - IEndpointRouteBuilder endpoints, - string pattern, + IEndpointRouteBuilder endpoints, + string pattern, Action configureOptions) where THub : Hub { diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs index c0a07a2ea3..9adbe8d7a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs @@ -1,14 +1,12 @@ -using System.Collections.Generic; - -namespace Volo.Abp.AspNetCore.SignalR +namespace Volo.Abp.AspNetCore.SignalR { public class AbpSignalROptions { - public List Hubs { get; } + public HubConfigList Hubs { get; } public AbpSignalROptions() { - Hubs = new List(); + Hubs = new HubConfigList(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs new file mode 100644 index 0000000000..2d2489bda9 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class DisableAutoHubMapAttribute : Attribute + { + + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs index 59c9342fce..be2e2a6877 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs @@ -19,11 +19,17 @@ namespace Volo.Abp.AspNetCore.SignalR public HubConfig( [NotNull] Type hubType, - [NotNull] string routePattern) + [NotNull] string routePattern, + [CanBeNull] Action configureAction = null) { HubType = Check.NotNull(hubType, nameof(hubType)); RoutePattern = Check.NotNullOrWhiteSpace(routePattern, nameof(routePattern)); ConfigureActions = new List>(); + + if (configureAction != null) + { + ConfigureActions.Add(configureAction); + } } public static HubConfig Create() diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs new file mode 100644 index 0000000000..434e50e515 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class HubConfigList : List + { + public void AddOrUpdate(Action configAction = null) + { + AddOrUpdate(typeof(THub)); + } + + public void AddOrUpdate(Type hubType, Action configAction = null) + { + var hubConfig = this.GetOrAdd( + c => c.HubType == hubType, + () => HubConfig.Create(hubType) + ); + + configAction?.Invoke(hubConfig); + } + } +} \ No newline at end of file From 383d9fc59d8be2d257f04af23a8de749818271ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:09:08 +0300 Subject: [PATCH 07/13] Add signalr demo to samples. --- docs/en/Samples/Index.md | 3 +++ docs/en/SignalR-Integration.md | 6 ++++-- docs/en/images/signalr-demo-chat.png | Bin 0 -> 46010 bytes 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 docs/en/images/signalr-demo-chat.png diff --git a/docs/en/Samples/Index.md b/docs/en/Samples/Index.md index 0c9ce68886..12095bb2c9 100644 --- a/docs/en/Samples/Index.md +++ b/docs/en/Samples/Index.md @@ -35,6 +35,9 @@ While there is no Razor Pages & MongoDB combination, you can check both document * **Entity Framework Migrations**: A solution to demonstrate how to split your application into multiple databases each database contains different modules. * [Source code](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) * [EF Core database migrations document](../Entity-Framework-Core-Migrations.md) +* **SignalR Demo**: A simple chat application that allows to send and receive messages among authenticated users. + * [Source code](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) + * [Signal Integration document](../SignalR-Integration.md) * **Dashboard Demo**: A simple application to show how to use the widget system for the ASP.NET Core MVC UI. * [Source code](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) * [Widget documentation](../UI/AspNetCore/Widgets.md) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 5b4176b123..415b585f3f 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -78,7 +78,7 @@ gulp This will copy the SignalR JavaScript files into your project: -![signal-js-file](D:\Github\abp\docs\en\images\signal-js-file.png) +![signal-js-file](images/signal-js-file.png) Finally, add the following code to your page/view to include the `signalr.js` file @@ -222,4 +222,6 @@ ABP implements SignalR's `IUserIdProvider` interface to provide the current user ## Example Application -See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messaged between (authenticated) users. \ No newline at end of file +See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messages between (authenticated) users. + +![signalr-demo-chat](images/signalr-demo-chat.png) \ No newline at end of file diff --git a/docs/en/images/signalr-demo-chat.png b/docs/en/images/signalr-demo-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..120d7eda6ed54fd4e6a90ef31cfd93f4fc9e4b43 GIT binary patch literal 46010 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 C2)D3+kz51_-t zC^figU=iUg9Ko4O(*zcsG|8xpAT5us7f9&{*{{iC8NMK@I^oQr3}6j{UZj@!=f1pY z_wl%TeyGG;%RK+97hRQWyW}+Fiu%B=>`=*8OMI1;2MKzobG-y(VCkWk z_V>ilJN)U)g`d%;TK+P#CJHxWylrHp?0By@=(~7qt~D4t$Y-9xUrMw=?8&C&)Kg1k zxx`W|<}(qyPxLF)GbuJc!%l$5AaVVKfv~jYFGD9l!k;YtK7GbY~dZYwuJEhzUlH!Y=6wB3}0fq@h`4iRhnZrbsCI>^)KSxi){!lmP%e=_l!To7@X342Z<%MjS~%Btm@O8vLuB0b>t!T)NthsA{**2G@cG(buhAe(1$RTguyXK?N{SJKD@RzlI8*$Jt4ebL z*^!>LgU453@H4{zqK-*Qxi-g_{4*~m4pO-*J{L7hURPdvT0VMy^H%?WVU9}_n@d%b zNliqZDwibZ54P+IW!iu&96DeJQ<}NcqFGtm<}Oe=?hBc_C^*whnhVZUX1QUmxj`r+{o{Rq=e(!qJmex z{9-44RS$JWR{ehWKxnzsm*d}V)QPFh(|4avJZ& zYfokkB<&U*GR>b@haNO~DhbVH_!K|&^S*mDqN=LL67-j((pcj0%gB{cB*sB2xo%~B zm@JJTPLhA)p<0YyT~EJzrlIFn-XZI}9#S!M z)0M97C%AZAd@k?ovv|^V!t_lzHgkA7Br^lqWr8+`N_m-FNw!e9D3eiPCJ1w`aFWpW z!yl@cIsUM&adZl<0N*%Kj)p#1C3dlCOE(Kg5k>_B&X>JJv)5~Vk~VWM#C#LcF54J9 zLL!Jua$mz_MP%l1E&PH_Y#?k!@-oI)KjnB}y|gti<;_AXdR#Y^QYEc8$Bk_Lfjd+r z7P+KxMhTH|F&Zw zikvl{kEv-8>#@HE)DjTwGF!74b{>q13bL?h%}dL|P9oC6*HUm0Kidyzk;EvKF*yHj zo0K3r=aF#%EsT?n9S&WQ`x>{ z74x#C)bzy>J?oQkdMmLU2Nj#)3rL}vy!(|0w=*41`_lJb;Z7IL?XrY_r%&=Sh%rzN z`Y4Dg9XUbvm?Q=&rx2EZXYoN~@u)kWkI>C3Sova2q#oYZ(41Fgt-B>)H2iZQHdBAR zDgJn*=ua=V2i$Q~qdjKoD#M)Yk1X1Ti;oy9c1p#UVYsV;hTW2(Nqn(ssPT_R_d4%* z;m!A|onW$U?bi|em(8b?l~b`-j0Js_KWUz45NGW@e1FB;z%?d_q+732l+0z#GCgRd zpuCp*@~jxz4vQ!n6NE$N1=2;9+;!VcFtal2uUb>u*?ViY;{|9=V$$$*ejC zH9E_?cmi}2mApsT0EV8sAHl(lOWGgXKMB{~VN*B$`)hoS^UQ)nQKmHShdUEGrIA&} z-&Rsp?Xg{BKuX)3bXKVme=a;(Vc<0z+bXPD|7$Cpa0?o+13%xSpk_waH`RWH>25xu z98_p*S1hIu4sCJizE zI7pQo4)>x&Eq0!SrtF^eGk-p9n>DJSR!xulT4l0jG;#%qT-R5Zq^ny#-NWL6{j3$^ z6$-+k&+97V+?E%YFkzfpx%d+|Ta;HUCDi9=gZw`lyleOLa}bPWkr~k_SEqU{wr@(_ zW#nm}=@_xhr>i_~=Rw9OMq0jApsj8&ToyDrQ*9K{;j^E00AtAe;OABD-}t`T^xNlc zZMu56AzRa$z7zo)ltHUzcsmXK@n;fNG0L&t4Ieh(jSq2$=6As zp1m&HB~Io3=2yfcNcw?t-tbF;`%*!A+7MMDFiF*WV1PmxQ;PZr%GKM>&4f?nv62RJ z2(SH2+}6NF*x-v(Zii-b z;WIVD9+F)LMi;2@IAeu$whu#7I;FGr_%Ig1j^D;FfNO$gQcFGFYS99z{|_631^QrYhy($7%&BNI8jR@;yNVEg=1VpVtF>dd={ot67(z^ zj=mc>Hw_g6mGrpbg`E+VKyPZCsDA7z@{eXAMGHjy+9JvJIg&jTG>5TM8voW`-WGwB z@Rs`Hs_wDz*@VMgU2rs=t)vrCv|#BX|8&RJzUsy-Ni}NViJcPSy*8X&!3$Fdo=Y8H@1S{X+GniRuKYpbvAU*;4bYL$G4Na*q3QAhOx z8^Yk4_lX2EE$}!Ab{)Cc_X<((=y!`rAen|9!ffrM`z=n`SFe^2*A%ZPbT25VVH?@V zn~bg&M#ZUG7wj=(U3YGw_~-|B_H2BHz~s&o`m0O_-UGoeCEVz3iempTd zBf94V%i;06356fE$lF}nFKiQ+9sl8|o@TU^rjsEb`_SX#*sd&-6)*&KYt?rMrNJFT zxe{e9+N*FzayZqg0N2srzoe_?{PvXk3+;%d-0n0pI~Ii)7?;;&Uf^{@LWubETH|CI zFB2`hs%eW^S?K4k(+jx*EKB9KiNhO;on-N-J%t9(lb@#b_tON zPoHXfrk*1aE>UkSQ$1TAJ%--)@L^wN;PoX_e?n8iUVVSG{fQjL9YC083QR(ZO`DI3 zU1M68>Y=HW3Z9VAV0bO2`r!Ptlh%S4jjN@>I{4n$d}rdeqXuS_}Z^>SrB6QZxys5*X1nMv7zfMS2X*HiV*-gWHtfxkht` z9tllM)OR43KD0dIWl8KD=$W*f_lsf1xWh6E8P_5Q0|oawRkX0gx$3-l=s#iHua|^s!^pt(r^OJOIQV~)U0MH zCQ7rT2JVaTQNvP?rpP(3IM_-RDXv%Y@=1{$A<+2VCwZq(hpBUvI2!fgLhTFG;M-?E z1LcL23}w&J)R^v>%Bs()r3_^M`njUM>KXqV)1k2t$ndhD_w+=g8l>WD6Ii39t6WPk zME1IlSA|$BrNLF<%Q3|~X@AgHAV#fotdXZ&e)1eb7A2WPwbh+e`SE0DdBs zZsXeR=G~A*F8roP?@=ag)p=&v%uW7#2^hOF)=q4B*FaPOYEQqLWbV*gBoJz(qGCp< zv!B4R8^_3sSqWR_`o(qm3H5T{^w#RG4}DZEF|BO6#TpG+4J`MiE>#o4BwekKoXdD# z0c=v~0-RhEz^Z_&h5%_&x5>bRf3#;i<(KPJU(%n*M1^d_2^BwDuBFQ^ zHS9tx8%NloYK1c)JUrvvL77+y7L;>Bnx^}BAPx0gw9u>uG5g!eV1A8|WG(HFa|KMi zDD98wBVreM6qu}9SM3+Uz`7h!duVroI4DiN(A@B6*DBRK^z`StX`7cy+nBHb=ClD) zc4w%(-2vzl&abgUyq@5g%sQ?<7%l;$MoI0sT?dW>#dz@=_~8iU78AVT6nzmqENSmd zR98J8*XX$cau&VxvQ$tne7sl6%7d|l%f*Qcdke>)Ah_6Ue%eLHks-M~ zey5)I+g>{K7F+DH8zvoV3iC(o>n@wkW?VNd_6HsxU1Gq0abt8QsHc-=!$&L`;OYZA z01rYS72TEN%QWK$V}B^pGb8P{ST#)!QM{H`ZcTkC>IBQi^I*kIBQ=&ZSQxDes$+6% zST$6(tw>xfQ9d5KN<)JBE-cUryHv0OTWo_U`7|JwkXqr+$kr2#>=1liG+D&`iYRt0 z30@=$?TWVFK+<^OV27V42pW%1hU5R-=m4nc{3SY2x$LJ`KTfRPo7UuW8X?-m~Xu(l~fm^u106mmf{} zeKNoLE84s6FvNgTEyxk(YS6wT*HYN+;QCA5YpuLnaTMM}{Lg!=)!3#+#?Rw`ECz7A$tZF7*f6>InSNy`XR^y| zLlYA8nfv$u-lI9EK@ePv5Jr0aBeLv3vb-WBFU3J$>52TW9d=s@Kke zkqs^ym{QiuNUEnzRbG@c+(EI-dJlZ_59`BA%6f{=I2%`#h~HNlifwPGp!b15D)|-Q zeG|gto5oR%EHVPzwOs9H7tJf&T4rzru*pq|k)na`%n=aC{~aQd1~P#F4tO()Ue^S^ z?Bjk&**VAB@b39#`!fF-2yod)d&0?gHvdz^_#Yh3Nh+@<9p1+XNwCRaC}li1v%kFu zs-4`9dJ;U5h8j3JDz~YIvuw|};%AT&!&VxdmpD|lfJn1e_2U`1)IL7lXh@xAb)_|-L#82LU_-|P1doiE<6g3IvYz|waq&L{oT}{R_?WRLn!F5gbMD2N z139sqcH5aB)A+zK#Cs3^R3ZbB^u(6FsK|q6T(0eOe@8g&AGea@&Ov2^h;<>YKR4={V*NrP}cR7 z8^iSH!sZ;M?p*kFm?`y)(F=o!H=sY=-tPoD-RWMd1c4~7I~R=XAIPnHh_Pt4i5Y8D z+CI$h*QI;tQy_H>@A0EHs<`jM?qcX^LMXW+{!dIHPHTyf#HtSwq!uM ze-?4cvC{3{8`uO^ZX(c57R>`l+^vjgcHwf3;conXYmIaiG+cQ5lNmFStaRBT+J}GO z?)Vp<_2b{FKz*A8asK}Gh$Am7nw7`edU=3Q5Z?(wId_br45?t6pKi=aeb6%ufy}q8 zM5QXE5srVo1L{lNbmIL+BQqT4#5Hw;(NlC)mBo}N_mQr;wjncpFUe=z7VCbEI1(=H zqqR|2eACWitLKn@=3D26-m*^tR@lBsb{ShX8ynctUF_sv6)=K{YLtZ~rSHQ!inkIJ zVl?f`mp{+M<+7?nVV#YJ)AbP#CcRlkfq_t~4y|_|HPq#5is~g)u3DQ!V$e=6u!Rq5`w^dc32)4;cihKaFC_8 z4=(VXd#RD@Q5_#2U#q;&4F&r1-X^8;89j9m9{LB{U0-*wXbwYa&#k_-M#cxBEG$9< z2bEEFv8p&{ZM5L6!3tHgQ&FWv$%&&U&mHSQ@5;Y0ZTevu@y~)|D%+uCe9@ z3GdM+)d3dGJ>k5D9m1VCH#gBgP?h4|!_`nX2vmEtlQQ`p%y=3N*?xz=W+@iRH`fjaj z2hNgnc-y=W{DY^Jri%GaU)@B`Nu2B6&IQgi8%zDt zO}J`t)m2W8sG8gMuQ#yLjkt01KmfbN%-v1EUlY;#ROE>?V?)+OX*g5$wj zE)A2tA~T$6eaX>f*Obqqs@HDMd^)vbDN)13B5eW!!AI}o5fT+gk-_apn#Sl^c`J`r zL@oRR)2)5iH(mbAHG2#FK5fRJ5n?GV_K=tGK`b`U0C zHKCs__Z@i?P-aYPU^vi+QH(FN1Xzq~CpblZt=fA}g~wse(L$>0@H}W=Lc1qmtK&%= zP5Nm)UaRFi^J%4(xx`Q|xo{nAym})^riP?3F&j{?+&!H3yqTQn#tshp$jsyP_otEL z>%Qn#M0lK%d5z?ZY)O8&xP+)@SO?2GvRk8{O>Sfak9#a zbg!oC&A);+AOrFn^d4GxN(VOL0LRT;|5HmN|3}H-@;Y40DCd23-iM7rU$wNcAY8on G`+ot40&F<| literal 0 HcmV?d00001 From cd3d1e57bd3d0f4ccd09c1de38a967a141c19828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:49:13 +0300 Subject: [PATCH 08/13] Create signalr test project. --- framework/Volo.Abp.sln | 7 +++++ .../Volo.Abp.AspNetCore.SignalR.Tests.csproj | 20 ++++++++++++++ .../SignalR/AbpAspNetCoreSignalRTestBase.cs | 12 +++++++++ .../SignalR/AbpAspNetCoreSignalRTestModule.cs | 15 +++++++++++ .../SignalR/AbpSignalROptions_Tests.cs | 26 +++++++++++++++++++ .../SampleHubs/DisableAutoHubMapHub.cs | 10 +++++++ .../DisableConventionalRegistrationHub.cs | 11 ++++++++ .../SignalR/SampleHubs/RegularAbpHub.cs | 6 +++++ .../SignalR/SampleHubs/RegularHub.cs | 8 ++++++ 9 files changed, 115 insertions(+) create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 3ff151439f..2db0442389 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -287,6 +287,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Validation.Abstrac EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR", "src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj", "{B64FCE08-E9D2-4984-BF12-FE199F257416}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -853,6 +855,10 @@ Global {B64FCE08-E9D2-4984-BF12-FE199F257416}.Debug|Any CPU.Build.0 = Debug|Any CPU {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.ActiveCfg = Release|Any CPU {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.Build.0 = Release|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -998,6 +1004,7 @@ Global {251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA} {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj new file mode 100644 index 0000000000..8e1ab29329 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj @@ -0,0 +1,20 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs new file mode 100644 index 0000000000..c7a31c8b8a --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs new file mode 100644 index 0000000000..eee1dd6ed3 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.SignalR +{ + [DependsOn( + typeof(AbpAspNetCoreSignalRModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpAspNetCoreSignalRTestModule : AbpModule + { + + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs new file mode 100644 index 0000000000..38e262abe3 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.AspNetCore.SignalR.SampleHubs; +using Xunit; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpSignalROptions_Tests : AbpAspNetCoreSignalRTestBase + { + private readonly AbpSignalROptions _options; + + public AbpSignalROptions_Tests() + { + _options = GetRequiredService>().Value; + } + + [Fact] + public void Should_Auto_Add_Maps() + { + _options.Hubs.ShouldContain(h => h.HubType == typeof(RegularHub)); + _options.Hubs.ShouldContain(h => h.HubType == typeof(RegularAbpHub)); + _options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableConventionalRegistrationHub)); + _options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableAutoHubMapHub)); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs new file mode 100644 index 0000000000..3ceae70af4 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + [DisableAutoHubMap] + public class DisableAutoHubMapHub : Hub + { + + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs new file mode 100644 index 0000000000..ff28b6eca9 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.SignalR; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + [DisableConventionalRegistration] + public class DisableConventionalRegistrationHub : Hub + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs new file mode 100644 index 0000000000..2afceffa8d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + public class RegularAbpHub : AbpHub + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs new file mode 100644 index 0000000000..8d3987f314 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + public class RegularHub : Hub + { + } +} \ No newline at end of file From 3154fbcbfe620ee6e9014fec5f71bff04be15c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:50:33 +0300 Subject: [PATCH 09/13] Make AbpAspNetCoreSignalRTestBase abstract. --- .../Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs index c7a31c8b8a..eb3cb0c265 100644 --- a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.AspNetCore.SignalR { - public class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest + public abstract class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest { protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) { From 9ddd879b6727b41b8f0aafe92f47f63569ad42f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 05:22:33 +0300 Subject: [PATCH 10/13] Fixed #3915: Use SemaphoreSlim in the LocalizedTemplateContentReaderFactory --- .../LocalizedTemplateContentReaderFactory.cs | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs index 092815ff6e..b403b37068 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; +using Nito.AsyncEx; using Volo.Abp.DependencyInjection; using Volo.Abp.VirtualFileSystem; @@ -9,50 +10,33 @@ namespace Volo.Abp.TextTemplating.VirtualFiles public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency { private readonly IVirtualFileProvider _virtualFileProvider; - private readonly Dictionary _readerCache; - private readonly ReaderWriterLockSlim _lock; + private readonly ConcurrentDictionary _readerCache; + protected SemaphoreSlim SyncObj; public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) { _virtualFileProvider = virtualFileProvider; - _readerCache = new Dictionary(); - _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + _readerCache = new ConcurrentDictionary(); + SyncObj = new SemaphoreSlim(1, 1); } public async Task CreateAsync(TemplateDefinition templateDefinition) { - _lock.EnterUpgradeableReadLock(); + if (_readerCache.TryGetValue(templateDefinition.Name, out var reader)) + { + return reader; + } - try + using (await SyncObj.LockAsync()) { - var reader = _readerCache.GetOrDefault(templateDefinition.Name); - if (reader != null) + if (_readerCache.TryGetValue(templateDefinition.Name, out reader)) { return reader; } - _lock.EnterWriteLock(); - - try - { - reader = await CreateInternalAsync(templateDefinition); - _readerCache[templateDefinition.Name] = reader; - return reader; - } - finally - { - if (_lock.IsWriteLockHeld) - { - _lock.ExitWriteLock(); - } - } - } - finally - { - if (_lock.IsUpgradeableReadLockHeld) - { - _lock.ExitUpgradeableReadLock(); - } + reader = await CreateInternalAsync(templateDefinition); + _readerCache[templateDefinition.Name] = reader; + return reader; } } From eb59629d21f90accd3f36867b97b379981a8f17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 05:25:43 +0300 Subject: [PATCH 11/13] LocalizedTemplateContentReaderFactory: Make CreateAsync method virtual and private fields protected to easier extend. --- .../LocalizedTemplateContentReaderFactory.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs index b403b37068..02b857bf5d 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -9,33 +9,33 @@ namespace Volo.Abp.TextTemplating.VirtualFiles { public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency { - private readonly IVirtualFileProvider _virtualFileProvider; - private readonly ConcurrentDictionary _readerCache; + protected IVirtualFileProvider VirtualFileProvider { get; } + protected ConcurrentDictionary ReaderCache { get; } protected SemaphoreSlim SyncObj; public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) { - _virtualFileProvider = virtualFileProvider; - _readerCache = new ConcurrentDictionary(); + VirtualFileProvider = virtualFileProvider; + ReaderCache = new ConcurrentDictionary(); SyncObj = new SemaphoreSlim(1, 1); } - public async Task CreateAsync(TemplateDefinition templateDefinition) + public virtual async Task CreateAsync(TemplateDefinition templateDefinition) { - if (_readerCache.TryGetValue(templateDefinition.Name, out var reader)) + if (ReaderCache.TryGetValue(templateDefinition.Name, out var reader)) { return reader; } using (await SyncObj.LockAsync()) { - if (_readerCache.TryGetValue(templateDefinition.Name, out reader)) + if (ReaderCache.TryGetValue(templateDefinition.Name, out reader)) { return reader; } reader = await CreateInternalAsync(templateDefinition); - _readerCache[templateDefinition.Name] = reader; + ReaderCache[templateDefinition.Name] = reader; return reader; } } @@ -49,7 +49,7 @@ namespace Volo.Abp.TextTemplating.VirtualFiles return NullLocalizedTemplateContentReader.Instance; } - var fileInfo = _virtualFileProvider.GetFileInfo(virtualPath); + var fileInfo = VirtualFileProvider.GetFileInfo(virtualPath); if (!fileInfo.Exists) { throw new AbpException("Could not find a file/folder at the location: " + virtualPath); @@ -58,7 +58,7 @@ namespace Volo.Abp.TextTemplating.VirtualFiles if (fileInfo.IsDirectory) { var folderReader = new VirtualFolderLocalizedTemplateContentReader(); - await folderReader.ReadContentsAsync(_virtualFileProvider, virtualPath); + await folderReader.ReadContentsAsync(VirtualFileProvider, virtualPath); return folderReader; } else //File From cc1408521f0b422cf73cb298b8b647262e58d4ba Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 13 May 2020 12:03:08 +0800 Subject: [PATCH 12/13] Make AbpPaginationTagHelper generate correct href attribute --- .../en/UI/AspNetCore/Tag-Helpers/Paginator.md | 2 +- .../UI/AspNetCore/Tag-Helpers/Paginator.md | 2 +- .../AbpPaginationTagHelperService.cs | 21 ++++++++++++++++++- .../TagHelpers/Pagination/PagerModel.cs | 5 +++++ .../Pages/Components/Paginator.cshtml | 6 +++--- .../Pages/Components/Paginator.cshtml.cs | 4 ++-- .../Pages/Components/Paginator/Index.cshtml | 7 +------ .../Components/Paginator/Index.cshtml.cs | 4 ++-- .../Pages/Components/Paginator/Index.js | 9 -------- 9 files changed, 35 insertions(+), 25 deletions(-) delete mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md b/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md index 0cc63d49e8..9969bcf3cc 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } diff --git a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md index 7a9b3de126..945da6fa67 100644 --- a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md +++ b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs index 2ab9ccef96..76bfcb3ef1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs @@ -1,4 +1,6 @@ -using System.Text; +using System; +using System.Linq; +using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; using Localization.Resources.AbpUi; @@ -123,6 +125,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination var tagHelperOutput = await anchorTagHelper.ProcessAndGetOutputAsync(attributeList, context, "a", TagMode.StartTagAndEndTag); + SetHrefAttribute(currentPage, attributeList); + tagHelperOutput.Content.SetHtmlContent(localizer[localizationKey]); var renderedHtml = tagHelperOutput.Render(_encoder); @@ -172,5 +176,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination " \r\n" + " \r\n"; } + + protected virtual void SetHrefAttribute(string currentPage, TagHelperAttributeList attributeList) + { + var hrefAttribute = attributeList.FirstOrDefault(x => x.Name.Equals("href", StringComparison.OrdinalIgnoreCase)); + + if (hrefAttribute != null) + { + var pageUrl = TagHelper.Model.PageUrl; + var routeValue = $"currentPage={currentPage}{(TagHelper.Model.Sort.IsNullOrWhiteSpace()? "" : "&sort="+TagHelper.Model.Sort)}"; + pageUrl += pageUrl.Contains("?") ? "&" + routeValue : "?" + routeValue; + + attributeList.Remove(hrefAttribute); + attributeList.Add(new TagHelperAttribute("href", pageUrl, hrefAttribute.ValueStyle)); + } + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs index 8c6eeb34a4..d071db223a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs @@ -38,6 +38,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination PageSize = pageSize; TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize)); Sort = sort; + + if (pageUrl.IsNullOrWhiteSpace() || !pageUrl.StartsWith("/")) + { + pageUrl = "/" + pageUrl; + } PageUrl = pageUrl; if (currentPage > TotalPageCount) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml index be26fb5251..b13e1bc4ef 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml @@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } @@ -60,7 +60,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components


-<div class="row mt-3">    
+<div class="row mt-3">
     <div class="col-sm-12 col-md-5">
         Showing 80 to 90 of 100 entries.
     </div>
@@ -105,4 +105,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
             
         
     
-
\ No newline at end of file
+
diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
index 15b31db6da..3723631381 100644
--- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
+++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
@@ -9,7 +9,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
 
         public void OnGet(int currentPage, string sort)
         {
-            PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort);
+            PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
index 6eb48dcd43..d987bce207 100644
--- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
+++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
@@ -7,14 +7,9 @@
 @{
     PageLayout.Content.Title = "Paginator";
 }
-@section scripts {
-    
-        
-    
-}
 
 

Paginator

Check the ABP Documentation.

-@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel }) \ No newline at end of file +@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel }) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs index 50659a0ab4..bc51f80f0a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.Pages.Components.Paginator public void OnGet(int currentPage = 1, string sort = null) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js deleted file mode 100644 index 843e0dea00..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js +++ /dev/null @@ -1,9 +0,0 @@ -$(function () { - var links = $("a.page-link"); - - $.each(links, function (key, value) { - var oldUrl = links[key].getAttribute("href"); - var value = Number(oldUrl.match(/currentPage=(\d+)&page/)[1]); - links[key].setAttribute("href", "/Components/Paginator?currentPage=" + value); - }) -}); \ No newline at end of file From ae3ae56d6d09d7b928d1bdd5858f63225a7025de Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 13 May 2020 16:48:50 +0800 Subject: [PATCH 13/13] Update PagerModel.cs --- .../TagHelpers/Pagination/PagerModel.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs index d071db223a..db74266f38 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs @@ -39,11 +39,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize)); Sort = sort; - if (pageUrl.IsNullOrWhiteSpace() || !pageUrl.StartsWith("/")) - { - pageUrl = "/" + pageUrl; - } - PageUrl = pageUrl; + PageUrl = pageUrl?.EnsureStartsWith('/') ?? "/"; if (currentPage > TotalPageCount) {