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

pull/3940/head
liangshiwei 5 years ago
commit 62a69fa9b1

@ -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)

@ -0,0 +1,227 @@
# 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
{
}
```
> 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 / client type.
#### 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](images/signal-js-file.png)
Finally, add the following code to your page/view to include the `signalr.js` file
````xml
@section scripts {
<abp-script type="typeof(SignalRBrowserScriptContributor)" />
}
````
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
This section covers the additional benefits when you use the ABP Framework integration packages.
### Hub Route & Mapping
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:
````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` suffix.
If you want to specify the route, you can use the `HubRoute` attribute:
````csharp
[HubRoute("/my-messasing-hub")]
public class MessagingHub : Hub
{
//...
}
````
### AbpHub Base Classes
Instead of the standard `Hub` and `Hub<T>` classes, you can inherit from the `AbpHub` or `AbpHub<T>` which hve 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
}
}
````
> 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<MessagingHub>();
````
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<AbpSignalROptions>(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<AbpSignalROptions>(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<MessagingHub>("/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
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)

@ -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);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@ -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);
}
}
}

@ -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}

@ -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
" </ nav>\r\n" +
" </div>\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));
}
}
}
}

@ -38,7 +38,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
PageSize = pageSize;
TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize));
Sort = sort;
PageUrl = pageUrl;
PageUrl = pageUrl?.EnsureStartsWith('/') ?? "/";
if (currentPage > TotalPageCount)
{

@ -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<AbpEndpointRouterOptions>(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<THub>(
IEndpointRouteBuilder endpoints,
string pattern,
IEndpointRouteBuilder endpoints,
string pattern,
Action<HttpConnectionDispatcherOptions> configureOptions)
where THub : Hub
{

@ -1,14 +1,12 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.SignalR
namespace Volo.Abp.AspNetCore.SignalR
{
public class AbpSignalROptions
{
public List<HubConfig> Hubs { get; }
public HubConfigList Hubs { get; }
public AbpSignalROptions()
{
Hubs = new List<HubConfig>();
Hubs = new HubConfigList();
}
}
}

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.AspNetCore.SignalR
{
public class DisableAutoHubMapAttribute : Attribute
{
}
}

@ -19,11 +19,17 @@ namespace Volo.Abp.AspNetCore.SignalR
public HubConfig(
[NotNull] Type hubType,
[NotNull] string routePattern)
[NotNull] string routePattern,
[CanBeNull] Action<HttpConnectionDispatcherOptions> configureAction = null)
{
HubType = Check.NotNull(hubType, nameof(hubType));
RoutePattern = Check.NotNullOrWhiteSpace(routePattern, nameof(routePattern));
ConfigureActions = new List<Action<HttpConnectionDispatcherOptions>>();
if (configureAction != null)
{
ConfigureActions.Add(configureAction);
}
}
public static HubConfig Create<THub>()

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.AspNetCore.SignalR
{
public class HubConfigList : List<HubConfig>
{
public void AddOrUpdate<THub>(Action<HubConfig> configAction = null)
{
AddOrUpdate(typeof(THub));
}
public void AddOrUpdate(Type hubType, Action<HubConfig> configAction = null)
{
var hubConfig = this.GetOrAdd(
c => c.HubType == hubType,
() => HubConfig.Create(hubType)
);
configAction?.Invoke(hubConfig);
}
}
}

@ -13,6 +13,11 @@ namespace Volo.Abp.AspNetCore.SignalR
RoutePattern = routePattern;
}
public virtual string GetRoutePatternForType(Type hubType)
{
return RoutePattern;
}
public static string GetRoutePattern<THub>()
where THub : Hub
{
@ -24,7 +29,7 @@ namespace Volo.Abp.AspNetCore.SignalR
var routeAttribute = hubType.GetSingleAttributeOrNull<HubRouteAttribute>();
if (routeAttribute != null)
{
return routeAttribute.RoutePattern;
return routeAttribute.GetRoutePatternForType(hubType);
}
return "/signalr-hubs/" + hubType.Name.RemovePostFix("Hub").ToKebabCase();

@ -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;
@ -8,51 +9,34 @@ namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency
{
private readonly IVirtualFileProvider _virtualFileProvider;
private readonly Dictionary<string, ILocalizedTemplateContentReader> _readerCache;
private readonly ReaderWriterLockSlim _lock;
protected IVirtualFileProvider VirtualFileProvider { get; }
protected ConcurrentDictionary<string, ILocalizedTemplateContentReader> ReaderCache { get; }
protected SemaphoreSlim SyncObj;
public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider)
{
_virtualFileProvider = virtualFileProvider;
_readerCache = new Dictionary<string, ILocalizedTemplateContentReader>();
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
VirtualFileProvider = virtualFileProvider;
ReaderCache = new ConcurrentDictionary<string, ILocalizedTemplateContentReader>();
SyncObj = new SemaphoreSlim(1, 1);
}
public async Task<ILocalizedTemplateContentReader> CreateAsync(TemplateDefinition templateDefinition)
public virtual async Task<ILocalizedTemplateContentReader> 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;
}
}
@ -65,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);
@ -74,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

@ -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
</abp-tab>
<abp-tab title="Rendered">
<pre><code>
&lt;div class=&quot;row mt-3&quot;&gt;
&lt;div class=&quot;row mt-3&quot;&gt;
&lt;div class=&quot;col-sm-12 col-md-5&quot;&gt;
Showing 80 to 90 of 100 entries.
&lt;/div&gt;
@ -105,4 +105,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
</abp-tab>
</abp-tabs>
</div>
</div>
</div>

@ -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);
}
}
}
}

@ -7,14 +7,9 @@
@{
PageLayout.Content.Title = "Paginator";
}
@section scripts {
<abp-script-bundle name="@typeof(IndexModel).FullName">
<abp-script src="/Pages/Components/Paginator/index.js" />
</abp-script-bundle>
}
<h2>Paginator</h2>
<p>Check the <a href="https://docs.abp.io/en/abp/latest/UI/AspNetCore/Tag-Helpers/Paginator" target="_blank">ABP Documentation</a>.</p>
@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel })
@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel })

@ -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);
}
}
}
}

@ -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);
})
});

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,12 @@
using Volo.Abp.Testing;
namespace Volo.Abp.AspNetCore.SignalR
{
public abstract class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest<AbpAspNetCoreSignalRTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

@ -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
{
}
}

@ -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<IOptions<AbpSignalROptions>>().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));
}
}
}

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.SignalR;
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs
{
[DisableAutoHubMap]
public class DisableAutoHubMapHub : Hub
{
}
}

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.SignalR;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs
{
[DisableConventionalRegistration]
public class DisableConventionalRegistrationHub : Hub
{
}
}

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs
{
public class RegularAbpHub : AbpHub
{
}
}

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.SignalR;
namespace Volo.Abp.AspNetCore.SignalR.SampleHubs
{
public class RegularHub : Hub
{
}
}

@ -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": "是否允許使用者更新電子信箱."
}
}
}

Loading…
Cancel
Save