Browse Source

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

pull/771/head
梁士伟 8 years ago
parent
commit
f0cec1e091
  1. 9
      .gitignore
  2. 61
      abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs
  3. 4
      abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj
  4. 3
      build-all.ps1
  5. 2
      common.props
  6. 3
      docs/en/Event-Bus.md
  7. 30
      docs/en/Microservice-Architecture.md
  8. 40
      docs/en/Samples/Microservice-Demo.md
  9. 6
      docs/en/Virtual-File-System.md
  10. 13
      docs/en/docs-nav.json
  11. BIN
      docs/en/images/microservice-sample-diagram.png
  12. 6
      docs/zh-Hans/Virtual-File-System.md
  13. 3
      docs/zh-Hans/docs-nav.json
  14. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs
  15. 32
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs
  16. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs
  17. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  18. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs
  19. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs
  20. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  21. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisabledInput.cs
  22. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormControlSize.cs
  23. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/InputInfoText.cs
  24. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/Placeholder.cs
  25. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/ReadOnlyInput.cs
  26. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/TextArea.cs
  27. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs
  28. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs
  29. 38
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs
  30. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs
  31. 36
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs
  32. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  33. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs
  34. 39
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs
  35. 26
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs
  36. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs
  37. 6
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  38. 1
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs
  39. 24
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs
  40. 228
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  41. 10
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheOptions.cs
  42. 20
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
  43. 10
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs
  44. 4
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  45. 13
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/AbpEventBusRabbitMqModule.cs
  46. 19
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs
  47. 28
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  48. 4
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventBusOptions.cs
  49. 8
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IocEventHandlerFactory.cs
  50. 1
      framework/src/Volo.Abp.Http.Client.IdentityModel/Volo.Abp.Http.Client.IdentityModel.csproj
  51. 2
      framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs
  52. 6
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  53. 2
      framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj
  54. 2
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs
  55. 10
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs
  56. 27
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs
  57. 14
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs
  58. 1
      framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj
  59. 5
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs
  60. 6
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs
  61. 4
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqOptions.cs
  62. 2
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs
  63. 2
      framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs
  64. 1
      framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs
  65. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj
  66. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  67. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/AbpAspNetCoreMvcUiBootstrapDemoModule.cs
  68. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml
  69. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs
  70. 22
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs
  71. 51
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs
  72. 52
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs
  73. 20
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/PersonCacheItem.cs
  74. 7
      modules/account/Volo.Abp.Account.sln
  75. 14
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs
  76. 239
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  77. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml
  78. 27
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json
  79. 33
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  80. 6
      modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs
  81. 73
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml
  82. 156
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  83. 1
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj
  84. 4
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Volo.BloggingTestApp.EntityFrameworkCore.csproj
  85. 48
      modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs
  86. 2
      modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj
  87. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs
  88. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/Dtos/GetPopularTagsInput.cs
  89. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/ITagAppService.cs
  90. 2
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs
  91. 8
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs
  92. 4
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Tagging/TagAppService.cs
  93. 11
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs
  94. 53
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs
  95. 65
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs
  96. 32
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs
  97. 2
      modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs
  98. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs
  99. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs
  100. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs

9
.gitignore

@ -285,3 +285,12 @@ framework/test/Volo\.Abp\.AspNetCore\.Mvc\.UI\.Bootstrap\.Demo/package-lock\.jso
modules/blogging/app/Volo\.BloggingTestApp/package-lock\.json
templates/mvc/src/MyCompanyName\.MyProjectName\.Web/package-lock\.json
samples/MicroserviceDemo/applications/AuthServer.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/IdentityService.Host/Logs/logs.txt
samples/MicroserviceDemo/applications/BackendAdminApp.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/ProductService.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/InternalGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/applications/PublicWebSite.Host/Logs/logs.txt
samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/Logs/logs.txt
samples/MicroserviceDemo/microservices/BloggingService.Host/Logs/logs.txt

61
abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs

@ -8,13 +8,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Volo.Abp;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
@ -22,6 +22,8 @@ using Volo.Abp.Identity;
using Volo.Abp.Identity.Web;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Threading;
using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem;
@ -41,6 +43,8 @@ namespace Volo.AbpWebSite
typeof(AbpAccountWebModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityWebModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(BloggingApplicationModule),
typeof(BloggingWebModule)
)]
@ -110,14 +114,14 @@ namespace Volo.AbpWebSite
{
services.Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPyhsical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<DocsDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<DocsWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}blogging{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}account{0}src{0}Volo.Abp.Account.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<DocsDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<DocsWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}docs{0}src{0}Volo.Docs.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}blogging{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAccountWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}modules{0}account{0}src{0}Volo.Abp.Account.Web", Path.DirectorySeparatorChar)));
});
}
}
@ -158,29 +162,28 @@ namespace Volo.AbpWebSite
app.UseVirtualFiles();
app.UseAuthentication();
//TODO: Create an extension method!
app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseMvcWithDefaultRouteAndArea();
AsyncHelper.RunSync(async () =>
using (var scope = context.ServiceProvider.CreateScope())
{
await context.ServiceProvider
.GetRequiredService<IIdentityDataSeeder>()
.SeedAsync(
"1q2w3E*",
IdentityPermissions.GetAll()
.Union(BloggingPermissions.GetAll())
);
});
AsyncHelper.RunSync(async () =>
{
await scope.ServiceProvider
.GetRequiredService<IIdentityDataSeeder>()
.SeedAsync(
"1q2w3E*"
);
await scope.ServiceProvider
.GetRequiredService<IPermissionDataSeeder>()
.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
});
}
}
}
}

4
abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj

@ -31,6 +31,8 @@
<ProjectReference Include="..\..\..\modules\blogging\src\Volo.Blogging.Application\Volo.Blogging.Application.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" />
<ProjectReference Include="..\..\..\modules\identity\src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj" />
<ProjectReference Include="..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application\Volo.Abp.PermissionManagement.Application.csproj" />
<ProjectReference Include="..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>
@ -38,7 +40,7 @@
<Content Include=".well-known/README.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Pages\*.js" >
<Content Include="Pages\*.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Pages\*.css" />

3
build-all.ps1

@ -20,7 +20,8 @@ $solutionPaths = (
"abp_io",
"templates/module",
"templates/service",
"templates/mvc"
"templates/mvc",
"samples/MicroserviceDemo"
)
# Build all solutions

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>0.12.0</Version>
<Version>0.13.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io</PackageProjectUrl>

3
docs/en/Event-Bus.md

@ -0,0 +1,3 @@
# Event Bus
TODO

30
docs/en/Microservice-Architecture.md

@ -0,0 +1,30 @@
# Microservice Architecture
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."*
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices)
## Introduction
One of the major goals of the ABP framework is to provide a convenient infrastructure to create microservice solutions. To make this possible,
* Provides a [module system](Module-Development-Basics.md) that allows you to split your application into modules where each module may have its own database, entities, services, APIs, UI components/pages... etc.
* Offers an [architectural model](Best-Practices/Module-Architecture.md) to develop your modules to be compatible to microservice development and deployment.
* Provides [best practices guide](Best-Practices/Index.md) to develop your module standards-compliance.
* Provides base infrastructure to implement [Domain Driven Design](Domain-Driven-Design.md) in your microservices.
* Provide services to [automatically create REST-style APIs](AspNetCore/Auto-API-Controllers.md) from your application services.
* Provide services to [automatically create C# API clients](AspNetCore/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application.
* Provides a [distributed event bus](Event-Bus.md) to communicate your services.
* Provides many other services to make your daily development easier.
## Microservice for New Applications
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture.
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts.
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
## Microservice Demo Solution
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework.

40
docs/en/Samples/Microservice-Demo.md

@ -0,0 +1,40 @@
# Microservice Demo Solution
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."*
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices)
## Introduction
One of the major goals of the ABP framework is to provide a [convenient infrastructure to create microservice solutions](Microservice-Architecture.md).
This sample aims to demonstrate a simple yet complete microservice solution;
* Has multiple, independent, self-deployable **microservices**.
* Multiple **web applications**, each uses a different API gateway.
* Has multiple **gateways** / BFFs (Backend for Frontends) developed using the [Ocelot](https://github.com/ThreeMammals/Ocelot) library.
* Has an **authentication service** developed using the [IdentityServer](https://identityserver.io/) framework. It's also a SSO (Single Sign On) application with necessary UIs.
* Has **multiple databases**. Some microservices has their own database while some services/applications shares a database (to demonstrate different use cases).
* Has different types of databases: **SQL Server** (with **Entity Framework Core** ORM) and **MongoDB**.
* Has a **console application** to show the simplest way of using a service by authenticating.
* Uses [Redis](https://redis.io/) for **distributed caching**.
* Uses [RabbitMQ](https://www.rabbitmq.com/) for service-to-service **messaging**.
* Uses [Kubernates](https://kubernetes.io/) to **deploy** & run all services and applications.
The diagram below shows the system:
![microservice-sample-diagram](../images/microservice-sample-diagram.png)
### Source Code
You can get the source code from [the GitHub repository](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo).
### Status
This sample is still in development, not completed yet.
## Microservices
### Identity Service
...

6
docs/en/Virtual-File-System.md

@ -108,7 +108,7 @@ Embedding a file into a module assembly and being able to use it from another pr
Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious.
What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPyhsical` method makes all this possible.
What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible.
The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time.
@ -124,8 +124,8 @@ public class MyWebAppModule : AbpModule
{
Configure<VirtualFileSystemOptions>(options =>
{
//ReplaceEmbeddedByPyhsical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPyhsical<MyModule>(
//ReplaceEmbeddedByPhysical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}MyModuleProject", Path.DirectorySeparatorChar))
);
});

13
docs/en/docs-nav.json

@ -256,10 +256,23 @@
}
]
},
{
"text": "Samples",
"items": [
{
"text": "Microservice Demo",
"path": "Samples/Microservice-Demo.md"
}
]
},
{
"text": "Application Modules",
"path": "Modules/Index.md"
},
{
"text": "Microservice Architecture",
"path": "Microservice-Architecture.md"
},
{
"text": "Testing"
},

BIN
docs/en/images/microservice-sample-diagram.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

6
docs/zh-Hans/Virtual-File-System.md

@ -109,7 +109,7 @@ public class MyService
假设你正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的.
我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPyhsical` 方法使其成为可能.
我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPhysical` 方法使其成为可能.
下面的示例展示了应用程序依赖于包含嵌入文件的模块("MyModule"), 并且应用程序可以在开发过程中直接使用模块的源代码.
@ -125,8 +125,8 @@ public class MyWebAppModule : AbpModule
{
Configure<VirtualFileSystemOptions>(options =>
{
//ReplaceEmbeddedByPyhsical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPyhsical<MyModule>(
//ReplaceEmbeddedByPhysical gets the root folder of the MyModule project
options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(hostingEnvironment.ContentRootPath, "..\\MyModuleProject")
);
});

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

@ -171,7 +171,8 @@
"text": "应用服务层",
"items": [
{
"text": "应用服务"
"text": "应用服务",
"path": "Application-Services.md"
},
{
"text": "数据传输对象(DTO)"

2
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/CachedApplicationConfigurationClient.cs

@ -45,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Client
async () => await Proxy.Service.GetAsync(),
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5)
}
);

32
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelperLocalizer.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
@ -20,27 +21,38 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
public string GetLocalizedText(string text, ModelExplorer explorer)
{
var localizer = GetStringLocalizer(explorer);
var resourceType = GetResourceTypeFromModelExplorer(explorer);
var localizer = GetStringLocalizer(resourceType);
return localizer == null ? text : localizer[text].Value;
}
public IStringLocalizer GetLocalizer(ModelExplorer explorer)
{
return GetStringLocalizer(explorer);
var resourceType = GetResourceTypeFromModelExplorer(explorer);
return GetStringLocalizer(resourceType);
}
private IStringLocalizer GetStringLocalizer(ModelExplorer explorer)
public IStringLocalizer GetLocalizer(Assembly assembly)
{
IStringLocalizer localizer = null;
var resourceType = _options.AssemblyResources.GetOrDefault(explorer.Container.ModelType.Assembly);
var resourceType = _options.AssemblyResources.GetOrDefault(assembly);
return GetStringLocalizer(resourceType);
}
if (resourceType != null)
{
localizer = _stringLocalizerFactory.Create(resourceType);
}
public IStringLocalizer GetLocalizer(Type resourceType)
{
return GetStringLocalizer(resourceType);
}
return localizer;
private IStringLocalizer GetStringLocalizer(Type resourceType)
{
return resourceType == null ? null : _stringLocalizerFactory.Create(resourceType);
}
private Type GetResourceTypeFromModelExplorer(ModelExplorer explorer)
{
var assembly = explorer.Container.ModelType.Assembly;
return _options.AssemblyResources.GetOrDefault(assembly);
}
}
}

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpFormControlSize.cs

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{
public enum AbpFormControlSize
{

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs

@ -1,5 +1,4 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs

@ -7,8 +7,6 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@ -10,8 +9,6 @@ using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DisabledInput.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/FormControlSize.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/InputInfoText.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/Placeholder.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/ReadOnlyInput.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/TextArea.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/IAbpTagHelperLocalizer.cs

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection;
@ -9,5 +11,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
string GetLocalizedText(string text, ModelExplorer explorer);
IStringLocalizer GetLocalizer(ModelExplorer explorer);
IStringLocalizer GetLocalizer(Assembly assembly);
IStringLocalizer GetLocalizer(Type resourceType);
}
}

14
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs

@ -13,13 +13,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
{
private readonly IHtmlGenerator _generator;
private readonly HtmlEncoder _encoder;
private readonly IStringLocalizer<AbpUiResource> _localizer;
private readonly IAbpTagHelperLocalizer _tagHelperLocalizer;
public AbpPaginationTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IStringLocalizer<AbpUiResource> localizer)
public AbpPaginationTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer)
{
_generator = generator;
_encoder = encoder;
_localizer = localizer;
_tagHelperLocalizer = tagHelperLocalizer;
}
public override void Process(TagHelperContext context, TagHelperOutput output)
@ -117,11 +117,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
protected virtual string RenderAnchorTagHelperLinkHtml(TagHelperContext context, TagHelperOutput output, string currentPage, string localizationKey)
{
var localizer = _tagHelperLocalizer.GetLocalizer(typeof(AbpUiResource));
var anchorTagHelper = GetAnchorTagHelper(currentPage, out var attributeList);
var tagHelperOutput = GetInnerTagHelper(attributeList, context, anchorTagHelper, "a", TagMode.StartTagAndEndTag);
tagHelperOutput.Content.SetHtmlContent(_localizer[localizationKey]);
tagHelperOutput.Content.SetHtmlContent(localizer[localizationKey]);
var renderedHtml = RenderTagHelperOutput(tagHelperOutput, _encoder);
@ -150,8 +152,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
protected virtual string GetOpeningTags(TagHelperContext context, TagHelperOutput output)
{
var localizer = _tagHelperLocalizer.GetLocalizer(typeof(AbpUiResource));
var pagerInfo = (TagHelper.ShowInfo ?? false) ?
" <div class=\"col-sm-12 col-md-5\"> " + _localizer["PagerInfo", TagHelper.Model.ShowingFrom, TagHelper.Model.ShowingTo, TagHelper.Model.TotalItemsCount] + "</div>\r\n"
" <div class=\"col-sm-12 col-md-5\"> " + localizer["PagerInfo", TagHelper.Model.ShowingFrom, TagHelper.Model.ShowingTo, TagHelper.Model.TotalItemsCount] + "</div>\r\n"
: "";
return

38
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs

@ -1,8 +1,8 @@
using System;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
@ -18,6 +18,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
public void Add(BundleContributor contributor)
{
foreach (var dependedType in GetDirectDependencies(contributor.GetType()))
{
AddWithDependencies(dependedType);
}
_contributors.Add(contributor);
}
@ -39,11 +44,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
return _contributors.ToImmutableList();
}
private bool IsAlreadyAdded(Type contributorType)
{
return _contributors.Any(c => c.GetType() == contributorType);
}
private void AddWithDependencies(Type contributorType)
{
if (IsAlreadyAdded(contributorType))
@ -51,20 +51,28 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
return;
}
foreach (var dependedType in GetDirectDependencies(contributorType))
{
AddWithDependencies(dependedType); //Recursive call
}
AddInstanceToContributors(contributorType);
}
private IEnumerable<Type> GetDirectDependencies(Type contributorType)
{
var dependsOnAttributes = contributorType
.GetCustomAttributes(true)
.OfType<IDependedTypesProvider>()
.ToList();
foreach (var dependsOnAttribute in dependsOnAttributes)
{
foreach (var dependedType in dependsOnAttribute.GetDependedTypes())
{
AddWithDependencies(dependedType); //Recursive call
}
}
return dependsOnAttributes
.SelectMany(a => a.GetDependedTypes());
}
AddInstanceToContributors(contributorType);
private bool IsAlreadyAdded(Type contributorType)
{
return _contributors.Any(c => c.GetType() == contributorType);
}
private void AddInstanceToContributors(Type contributorType)

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
@ -46,7 +45,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
{
bundle
.AddBaseBundles(StandardBundles.Styles.Global)
.AddContributors(new BasicThemeGlobalStyleContributor());
.AddContributors(typeof(BasicThemeGlobalStyleContributor));
});
options

36
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs

@ -0,0 +1,36 @@
using System;
using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Builder
{
public static class AbpAspNetCoreMvcApplicationBuilderExtensions
{
/// <summary>
/// Adds MVC to the <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" /> request execution pipeline
/// with the following default routes:
///
/// - a default route named 'defaultWithArea' and the following template: '{area}/{controller=Home}/{action=Index}/{id?}'.
/// - a default route named 'default' and the following template: '{controller=Home}/{action=Index}/{id?}'.
/// </summary>
/// <param name="app">The <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" />.</param>
/// <param name="additionalConfigurationAction">Additional action to configure routes</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseMvcWithDefaultRouteAndArea(
this IApplicationBuilder app,
Action<IRouteBuilder> additionalConfigurationAction = null)
{
return app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
additionalConfigurationAction?.Invoke(routes);
});
}
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -63,14 +63,6 @@ namespace Volo.Abp.AspNetCore.Mvc
options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter));
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcModule).Assembly, o =>
{
o.RootPath = "abp";
});
});
var mvcCoreBuilder = context.Services.AddMvcCore();
context.Services.ExecutePreConfiguredActions(mvcCoreBuilder);

5
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs

@ -17,6 +17,11 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected override bool IsController(TypeInfo typeInfo)
{
//TODO: Move this to a lazy loaded field for efficiency.
if (_application.ServiceProvider == null)
{
return false;
}
var configuration = _application.ServiceProvider
.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().Value
.ConventionalControllers

39
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs

@ -1,5 +1,8 @@
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using System;
using Volo.Abp.Settings;
namespace Volo.Abp.AspNetCore.Mvc.Localization
{
@ -8,12 +11,40 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization
public class AbpLanguagesController : AbpController
{
[HttpGet]
public IActionResult Switch(string culture, string uiCulture = "") //TODO: Implement return URL
public IActionResult Switch(string culture, string uiCulture = "", string returnUrl = "")
{
//TODO: Check allowed languages and so on...
if (!GlobalizationHelper.IsValidCultureCode(culture))
{
throw new AbpException("Unknown language: " + culture + ". It must be a valid culture!");
}
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, $"c={culture}|uic={uiCulture ?? culture}");
string cookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture, uiCulture));
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, cookieValue, new CookieOptions
{
Expires = Clock.Now.AddYears(2)
});
if (!string.IsNullOrWhiteSpace(returnUrl))
{
return Redirect(GetRedirectUrl(returnUrl));
}
return Redirect("/");
}
private string GetRedirectUrl(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return "/";
}
if (Url.IsLocalUrl(returnUrl))
{
return returnUrl;
}
return "/";
}
}
}

26
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/GlobalizationHelper.cs

@ -0,0 +1,26 @@
using System;
using System.Globalization;
namespace Volo.Abp.AspNetCore.Mvc.Localization
{
internal static class GlobalizationHelper
{
public static bool IsValidCultureCode(string cultureCode)
{
if (cultureCode.IsNullOrWhiteSpace())
{
return false;
}
try
{
CultureInfo.GetCultureInfo(cultureCode);
return true;
}
catch (CultureNotFoundException)
{
return false;
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Modularity/ApplicationInitializationContextExtensions.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs

@ -4,7 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Modularity
namespace Volo.Abp
{
public static class ApplicationInitializationContextExtensions
{

6
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization;
@ -17,6 +18,11 @@ namespace Volo.Abp.Caching
context.Services.AddDistributedMemoryCache();
context.Services.AddSingleton(typeof(IDistributedCache<>), typeof(DistributedCache<>));
context.Services.Configure<CacheOptions>(cacheOptions =>
{
cacheOptions.GlobalCacheEntryOptions.SlidingExpiration = TimeSpan.FromMinutes(20);
});
}
}
}

1
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheNameAttribute.cs

@ -1,4 +1,5 @@
using System;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.Abp.Caching

24
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Generic;
namespace Volo.Abp.Caching
{
public class CacheOptions
{
/// <summary>
/// Global Cache entry options.
/// </summary>
public DistributedCacheEntryOptions GlobalCacheEntryOptions { get; set; }
/// <summary>
/// List of all cache configurators.
/// (func argument:Name of cache)
/// </summary>
public List<Func<string, DistributedCacheEntryOptions>> CacheConfigurators { get; set; } //TODO list item use a configurator interface instead?
public CacheOptions()
{
CacheConfigurators = new List<Func<string, DistributedCacheEntryOptions>>();
GlobalCacheEntryOptions = new DistributedCacheEntryOptions();
}
}
}

228
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -3,6 +3,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Nito.AsyncEx;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization;
@ -13,6 +16,8 @@ namespace Volo.Abp.Caching
public class DistributedCache<TCacheItem> : IDistributedCache<TCacheItem>
where TCacheItem : class
{
public ILogger<DistributedCache<TCacheItem>> Logger { get; set; }
protected string CacheName { get; set; }
protected bool IgnoreMultiTenancy { get; set; }
@ -27,23 +32,52 @@ namespace Volo.Abp.Caching
protected AsyncLock AsyncLock { get; } = new AsyncLock();
protected DistributedCacheEntryOptions DefaultCacheOptions;
private readonly CacheOptions _cacheOption;
private readonly DistributedCacheOptions _distributedCacheOption;
public DistributedCache(
IOptions<CacheOptions> cacheOption,
IOptions<DistributedCacheOptions> distributedCacheOption,
IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider,
IObjectSerializer objectSerializer,
ICurrentTenant currentTenant)
{
_distributedCacheOption = distributedCacheOption.Value;
_cacheOption = cacheOption.Value;
Cache = cache;
CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<DistributedCache<TCacheItem>>.Instance;
ObjectSerializer = objectSerializer;
CurrentTenant = currentTenant;
SetDefaultOptions();
}
public virtual TCacheItem Get(string key)
public virtual TCacheItem Get(string key, bool? hideErrors = null)
{
var cachedBytes = Cache.Get(NormalizeKey(key));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[] cachedBytes;
try
{
cachedBytes = Cache.Get(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return null;
}
throw;
}
if (cachedBytes == null)
{
return null;
@ -52,9 +86,26 @@ namespace Volo.Abp.Caching
return ObjectSerializer.Deserialize<TCacheItem>(cachedBytes);
}
public virtual async Task<TCacheItem> GetAsync(string key, CancellationToken token = default)
public virtual async Task<TCacheItem> GetAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{
var cachedBytes = await Cache.GetAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[] cachedBytes;
try
{
cachedBytes = await Cache.GetAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return null;
}
throw;
}
if (cachedBytes == null)
{
return null;
@ -64,11 +115,12 @@ namespace Volo.Abp.Caching
}
public TCacheItem GetOrAdd(
string key,
string key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null)
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null)
{
var value = Get(key);
var value = Get(key, hideErrors);
if (value != null)
{
return value;
@ -76,26 +128,28 @@ namespace Volo.Abp.Caching
using (AsyncLock.Lock())
{
value = Get(key);
value = Get(key, hideErrors);
if (value != null)
{
return value;
}
value = factory();
Set(key, value, optionsFactory?.Invoke());
Set(key, value, optionsFactory?.Invoke(), hideErrors);
}
return value;
}
public async Task<TCacheItem> GetOrAddAsync(
string key,
string key,
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
CancellationToken token = default)
{
var value = await GetAsync(key, token);
token = CancellationTokenProvider.FallbackToProvider(token);
var value = await GetAsync(key, hideErrors, token);
if (value != null)
{
return value;
@ -103,56 +157,142 @@ namespace Volo.Abp.Caching
using (await AsyncLock.LockAsync(token))
{
value = await GetAsync(key, token);
value = await GetAsync(key, hideErrors, token);
if (value != null)
{
return value;
}
value = await factory();
await SetAsync(key, value, optionsFactory?.Invoke(), token);
await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, token);
}
return value;
}
public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null)
public virtual void Set(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null)
{
Cache.Set(
NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(20) } //TODO: implement per cache item and global defaults!!!
);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Set(
NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return;
}
throw;
}
}
public virtual Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, CancellationToken token = default)
public virtual Task SetAsync(string key, TCacheItem value, DistributedCacheEntryOptions options = null, bool? hideErrors = null, CancellationToken token = default)
{
return Cache.SetAsync(
NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(20) }, //TODO: implement per cache item and global defaults!!!
CancellationTokenProvider.FallbackToProvider(token)
);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return Cache.SetAsync(
NormalizeKey(key),
ObjectSerializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
}
public virtual void Refresh(string key)
public virtual void Refresh(string key, bool? hideErrors = null)
{
Cache.Refresh(NormalizeKey(key));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Refresh(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return;
}
throw;
}
}
public virtual Task RefreshAsync(string key, CancellationToken token = default)
public virtual Task RefreshAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{
return Cache.RefreshAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return Cache.RefreshAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
}
public virtual void Remove(string key)
public virtual void Remove(string key, bool? hideErrors = null)
{
Cache.Remove(NormalizeKey(key));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Remove(NormalizeKey(key));
}
catch (Exception ex)
{
if ((bool) hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
}
throw;
}
}
public virtual Task RemoveAsync(string key, CancellationToken token = default)
public virtual Task RemoveAsync(string key, bool? hideErrors = null, CancellationToken token = default)
{
return Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if ((bool)hideErrors)
{
Logger.LogException(ex, LogLevel.Warning);
return Task.CompletedTask;
}
throw;
}
}
protected virtual string NormalizeKey(string key)
@ -167,6 +307,19 @@ namespace Volo.Abp.Caching
return normalizedKey;
}
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
{
foreach (var configure in _cacheOption.CacheConfigurators)
{
var options = configure.Invoke(CacheName);
if (options != null)
{
return options;
}
}
return _cacheOption.GlobalCacheEntryOptions;
}
protected virtual void SetDefaultOptions()
{
//CacheName
@ -175,10 +328,13 @@ namespace Volo.Abp.Caching
.OfType<CacheNameAttribute>()
.FirstOrDefault();
CacheName = cacheNameAttribute != null ? cacheNameAttribute.Name : typeof(TCacheItem).Name;
CacheName = cacheNameAttribute != null ? cacheNameAttribute.Name : typeof(TCacheItem).FullName;
//IgnoreMultiTenancy
IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
}
}
}

10
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCacheOptions.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.Caching
{
public class DistributedCacheOptions
{
/// <summary>
/// Throw or hide exceptions for the distributed cache.
/// </summary>
public bool HideErrors { get; set; } = true;
}
}

20
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs

@ -10,55 +10,65 @@ namespace Volo.Abp.Caching
where TCacheItem : class
{
TCacheItem Get(
string key
string key,
bool? hideErrors = null
);
Task<TCacheItem> GetAsync(
[NotNull] string key,
bool? hideErrors = null,
CancellationToken token = default
);
TCacheItem GetOrAdd(
string key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null
);
Task<TCacheItem> GetOrAddAsync(
[NotNull] string key,
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
CancellationToken token = default
);
void Set(
string key,
TCacheItem value,
DistributedCacheEntryOptions options = null
DistributedCacheEntryOptions options = null,
bool? hideErrors = null
);
Task SetAsync(
[NotNull] string key,
[NotNull] TCacheItem value,
[CanBeNull] DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default
);
void Refresh(
string key
string key,
bool? hideErrors = null
);
Task RefreshAsync(
string key,
bool? hideErrors = null,
CancellationToken token = default
);
void Remove(
string key
string key,
bool? hideErrors = null
);
Task RemoveAsync(
string key,
bool? hideErrors = null,
CancellationToken token = default
);
}

10
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs

@ -8,6 +8,16 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionCommonExtensions
{
public static bool IsAdded<T>(this IServiceCollection services)
{
return services.IsAdded(typeof(T));
}
public static bool IsAdded(this IServiceCollection services, Type type)
{
return services.Any(d => d.ServiceType == type);
}
public static T GetSingletonInstanceOrNull<T>(this IServiceCollection services)
{
return (T)services

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

@ -14,7 +14,7 @@
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9.2" />
<PackageReference Include="System.Linq.Queryable" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="JetBrains.Annotations" Version="2018.2.1" />
<PackageReference Include="JetBrains.Annotations" Version="2018.3.0" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="1.0.2" />
<PackageReference Include="Nito.AsyncEx.Context" Version="1.1.0" />
</ItemGroup>

13
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/AbpEventBusRabbitMqModule.cs

@ -1,13 +0,0 @@
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
namespace Volo.Abp.EventBus.Distributed.RabbitMq
{
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpRabbitMqModule))]
public class AbpEventBusRabbitMqModule : AbpModule
{
}
}

19
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.RabbitMQ;
namespace Volo.Abp.EventBus.RabbitMq
{
[DependsOn(
typeof(AbpEventBusModule),
typeof(AbpRabbitMqModule))]
public class AbpEventBusRabbitMqModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<RabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus"));
}
}
}

28
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs → framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -4,13 +4,14 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.RabbitMQ;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Distributed.RabbitMq
namespace Volo.Abp.EventBus.RabbitMq
{
/* TODO: How to handle unsubscribe to unbind on RabbitMq (may not be possible for)
* TODO: Implement Retry system
@ -20,7 +21,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
[ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus))]
public class RabbitMqDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
{
protected RabbitMqDistributedEventBusOptions RabbitMqDistributedEventBusOptions { get; }
protected RabbitMqEventBusOptions RabbitMqEventBusOptions { get; }
protected DistributedEventBusOptions DistributedEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; }
@ -32,7 +33,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
protected IRabbitMqMessageConsumer Consumer { get; }
public RabbitMqDistributedEventBus(
IOptions<RabbitMqDistributedEventBusOptions> options,
IOptions<RabbitMqEventBusOptions> options,
IConnectionPool connectionPool,
IRabbitMqSerializer serializer,
IHybridServiceScopeFactory serviceScopeFactory,
@ -44,24 +45,24 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
Serializer = serializer;
MessageConsumerFactory = messageConsumerFactory;
DistributedEventBusOptions = distributedEventBusOptions.Value;
RabbitMqDistributedEventBusOptions = options.Value;
RabbitMqEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
Consumer = MessageConsumerFactory.Create(
new ExchangeDeclareConfiguration(
RabbitMqDistributedEventBusOptions.ExchangeName,
RabbitMqEventBusOptions.ExchangeName,
type: "direct",
durable: true
),
new QueueDeclareConfiguration(
RabbitMqDistributedEventBusOptions.ClientName,
RabbitMqEventBusOptions.ClientName,
durable: true,
exclusive: false,
autoDelete: false
),
RabbitMqDistributedEventBusOptions.ConnectionName
RabbitMqEventBusOptions.ConnectionName
);
Consumer.OnMessageReceived(ProcessEventAsync);
@ -96,8 +97,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
if (handlerFactories.Count == 1) //TODO: Multi-threading!
{
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
Consumer.BindAsync(eventName);
Consumer.BindAsync(EventNameAttribute.GetNameOrDefault(eventType));
}
return new EventHandlerFactoryUnregistrar(this, eventType, factory);
@ -162,10 +162,10 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
var eventName = EventNameAttribute.GetNameOrDefault(eventType);
var body = Serializer.Serialize(eventData);
using (var channel = ConnectionPool.Get(RabbitMqDistributedEventBusOptions.ConnectionName).CreateModel())
using (var channel = ConnectionPool.Get(RabbitMqEventBusOptions.ConnectionName).CreateModel())
{
channel.ExchangeDeclare(
RabbitMqDistributedEventBusOptions.ExchangeName,
RabbitMqEventBusOptions.ExchangeName,
"direct",
durable: true
);
@ -174,7 +174,7 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
properties.DeliveryMode = RabbitMqConsts.DeliveryModes.Persistent;
channel.BasicPublish(
exchange: RabbitMqDistributedEventBusOptions.ExchangeName,
exchange: RabbitMqEventBusOptions.ExchangeName,
routingKey: eventName,
mandatory: true,
basicProperties: properties,

4
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs → framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqEventBusOptions.cs

@ -1,6 +1,6 @@
namespace Volo.Abp.EventBus.Distributed.RabbitMq
namespace Volo.Abp.EventBus.RabbitMq
{
public class RabbitMqDistributedEventBusOptions
public class RabbitMqEventBusOptions
{
public string ConnectionName { get; set; }

8
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IocEventHandlerFactory.cs

@ -12,12 +12,12 @@ namespace Volo.Abp.EventBus
{
public Type HandlerType { get; }
protected IServiceScope ServiceScope { get; }
protected IHybridServiceScopeFactory ScopeFactory { get; }
public IocEventHandlerFactory(IHybridServiceScopeFactory scopeFactory, Type handlerType)
{
ScopeFactory = scopeFactory;
HandlerType = handlerType;
ServiceScope = scopeFactory.CreateScope();
}
/// <summary>
@ -26,7 +26,7 @@ namespace Volo.Abp.EventBus
/// <returns>Resolved handler object</returns>
public IEventHandlerDisposeWrapper GetHandler()
{
var scope = ServiceScope.ServiceProvider.CreateScope();
var scope = ScopeFactory.CreateScope();
return new EventHandlerDisposeWrapper(
(IEventHandler) scope.ServiceProvider.GetRequiredService(HandlerType),
() => scope.Dispose()
@ -35,7 +35,7 @@ namespace Volo.Abp.EventBus
public void Dispose()
{
ServiceScope.Dispose();
}
}
}

1
framework/src/Volo.Abp.Http.Client.IdentityModel/Volo.Abp.Http.Client.IdentityModel.csproj

@ -14,7 +14,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.4" />
<ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
<ProjectReference Include="..\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj" />
</ItemGroup>

2
framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Http.Client.IdentityModel
}
else
{
await IdentityModelHttpClientAuthenticator.Authenticate(
await IdentityModelHttpClientAuthenticator.AuthenticateAsync(
new IdentityModelHttpClientAuthenticateContext(
context.Client,
context.RemoteService.GetIdentityClient()

6
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -4,6 +4,8 @@ using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
@ -29,6 +31,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
private readonly IJsonSerializer _jsonSerializer;
private readonly IRemoteServiceHttpClientAuthenticator _clientAuthenticator;
public ILogger<DynamicHttpProxyInterceptor<TService>> Logger { get; set; }
static DynamicHttpProxyInterceptor()
{
GenericInterceptAsyncMethod = typeof(DynamicHttpProxyInterceptor<TService>)
@ -50,6 +54,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
_clientAuthenticator = clientAuthenticator;
_clientOptions = clientOptions.Value;
_remoteServiceOptions = remoteServiceOptions.Value;
Logger = NullLogger<DynamicHttpProxyInterceptor<TService>>.Instance;
}
public override void Intercept(IAbpMethodInvocation invocation)

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

@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.4" />
<PackageReference Include="IdentityModel" Version="3.10.5" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>

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

@ -4,6 +4,6 @@ namespace Volo.Abp.IdentityModel
{
public interface IIdentityModelHttpClientAuthenticator
{
Task Authenticate(IdentityModelHttpClientAuthenticateContext context);
Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context);
}
}

10
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs

@ -1,4 +1,5 @@
using System.Net.Http;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityModel
{
@ -11,16 +12,13 @@ namespace Volo.Abp.IdentityModel
/// </summary>
public string IdentityClientName { get; }
/// <summary>
///
/// </summary>
/// <param name="client"><see cref="HttpClient"/> object to be authorized</param>
/// <param name="client"><see cref="HttpClient"/> object to be authenticated</param>
/// <param name="identityClientName">The identity client name configured with the <see cref="IdentityClientOptions"/>.</param>
public IdentityModelHttpClientAuthenticateContext(
HttpClient client,
[NotNull] HttpClient client,
string identityClientName = null)
{
Client = client;
Client = Check.NotNull(client, nameof(client));
IdentityClientName = identityClientName;
}
}

27
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs

@ -0,0 +1,27 @@
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.IdentityModel
{
public static class IdentityModelHttpClientAuthenticatorExtensions
{
/// <param name="authenticator">Authenticator object</param>
/// <param name="client"><see cref="HttpClient"/> object to be authenticated</param>
/// <param name="identityClientName">The identity client name configured with the <see cref="IdentityClientOptions"/>.</param>
public static Task AuthenticateAsync(
[NotNull] this IIdentityModelHttpClientAuthenticator authenticator,
[NotNull] HttpClient client,
string identityClientName = null)
{
Check.NotNull(authenticator, nameof(authenticator));
return authenticator.AuthenticateAsync(
new IdentityModelHttpClientAuthenticateContext(
client,
identityClientName
)
);
}
}
}

14
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs

@ -4,6 +4,8 @@ using System.Net.Http.Headers;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@ -12,15 +14,18 @@ namespace Volo.Abp.IdentityModel
[Dependency(ReplaceServices = true)]
public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency
{
protected IdentityClientOptions ClientOptions { get; }
public ILogger<IdentityModelHttpClientAuthenticator> Logger { get; set; }
protected IdentityClientOptions ClientOptions { get; }
public IdentityModelHttpClientAuthenticator(
IOptions<IdentityClientOptions> options)
{
ClientOptions = options.Value;
Logger = NullLogger<IdentityModelHttpClientAuthenticator>.Instance;
}
public async Task Authenticate(IdentityModelHttpClientAuthenticateContext context)
public async Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context)
{
var accessToken = await GetAccessTokenFromServerOrNullAsync(context);
@ -34,22 +39,27 @@ namespace Volo.Abp.IdentityModel
protected virtual async Task<string> GetAccessTokenFromServerOrNullAsync(IdentityModelHttpClientAuthenticateContext context)
{
//TODO: Better logging
var configuration = GetClientConfiguration(context);
if (configuration == null)
{
Logger.LogWarning($"Could not find {nameof(IdentityClientConfiguration)} for {context.IdentityClientName}. Either define a configuration for {context.IdentityClientName} or set a default configuration.");
return null;
}
var discoveryResponse = await GetDiscoveryResponse(configuration);
if (discoveryResponse.IsError)
{
Logger.LogError($"Could not retrieve the OpenId Connect discovery document! ErrorType: {discoveryResponse.ErrorType}. Error: {discoveryResponse.Error}");
return null;
}
var tokenResponse = await GetTokenResponse(discoveryResponse, configuration);
if (tokenResponse.IsError)
{
Logger.LogError($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}");
return null;
}

1
framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj

@ -18,7 +18,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
</ItemGroup>

5
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs

@ -10,7 +10,10 @@ namespace Volo.Abp.MongoDB
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.TryAddTransient(typeof(IMongoDbContextProvider<>), typeof(UnitOfWorkMongoDbContextProvider<>));
context.Services.TryAddTransient(
typeof(IMongoDbContextProvider<>),
typeof(UnitOfWorkMongoDbContextProvider<>)
);
}
}
}

6
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqModule.cs

@ -11,6 +11,12 @@ namespace Volo.Abp.RabbitMQ
)]
public class AbpRabbitMqModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpRabbitMqOptions>(configuration.GetSection("RabbitMQ"));
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
context.ServiceProvider

4
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/AbpRabbitMqOptions.cs

@ -2,11 +2,11 @@
{
public class AbpRabbitMqOptions
{
public RabbitMqConnections ConnectionFactories { get; }
public RabbitMqConnections Connections { get; }
public AbpRabbitMqOptions()
{
ConnectionFactories = new RabbitMqConnections();
Connections = new RabbitMqConnections();
}
}
}

2
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/ConnectionPool.cs

@ -28,7 +28,7 @@ namespace Volo.Abp.RabbitMQ
return Connections.GetOrAdd(
connectionName,
() => Options
.ConnectionFactories
.Connections
.GetOrDefault(connectionName)
.CreateConnection()
);

2
framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/VirtualFileSetListExtensions.cs

@ -21,7 +21,7 @@ namespace Volo.Abp.VirtualFileSystem
);
}
public static void ReplaceEmbeddedByPyhsical<T>([NotNull] this VirtualFileSetList list, [NotNull] string pyhsicalPath)
public static void ReplaceEmbeddedByPhysical<T>([NotNull] this VirtualFileSetList list, [NotNull] string pyhsicalPath)
{
Check.NotNull(list, nameof(list));
Check.NotNull(pyhsicalPath, nameof(pyhsicalPath));

1
framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/App/AppModule.cs

@ -2,7 +2,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Json;

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

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

1
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs

@ -2,7 +2,6 @@
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.Localization.Resource;

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/AbpAspNetCoreMvcUiBootstrapDemoModule.cs

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;

2
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml

@ -66,7 +66,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
&lt;/div&gt;
&lt;div class=&quot;col-sm-12 col-md-7&quot;&gt;
&lt;nav aria-label=&quot;Page navigation&quot;&gt;
&lt;ul class=&quot;pagination justify-localizationKey-end&quot;&gt;
&lt;ul class=&quot;pagination justify-content-end&quot;&gt;
&lt;li class=&quot;page-item &quot;&gt;
&lt;a tabindex=&quot;-1&quot; class=&quot;page-link&quot; href=&quot;/Components/Paginator?currentPage=7&quot;&gt;Previous&lt;/a&gt;
&lt;/li&gt;

1
framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs

@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac;
using Volo.Abp.Http.Client;

22
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/AbpCachingTestModule.cs

@ -1,10 +1,30 @@
using Volo.Abp.Modularity;
using Microsoft.Extensions.Caching.Distributed;
using System;
using Volo.Abp.Modularity;
namespace Volo.Abp.Caching
{
[DependsOn(typeof(AbpCachingModule))]
public class AbpCachingTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<CacheOptions>(option =>
{
option.CacheConfigurators.Add(cacheName =>
{
if (cacheName == typeof(Sail.Testing.Caching.PersonCacheItem).FullName)
{
return new DistributedCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Parse("2099-01-01 12:00:00")
};
}
return null;
});
option.GlobalCacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(20));
});
}
}
}

51
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_ConfigureOptions_Test.cs

@ -0,0 +1,51 @@
using Microsoft.Extensions.Caching.Distributed;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Volo.Abp.Caching
{
public class DistributedCache_ConfigureOptions_Test : AbpIntegratedTest<AbpCachingTestModule>
{
[Fact]
public async Task Configure_CacheOptions()
{
var personCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
GetDefaultCachingOptions(personCache).SlidingExpiration.ShouldBeNull();
GetDefaultCachingOptions(personCache).AbsoluteExpiration.ShouldBe(new DateTime(2099, 1, 1, 12, 0, 0));
}
[Fact]
public async Task Default_CacheOptions_Should_Be_20_Mins()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
GetDefaultCachingOptions(personCache).SlidingExpiration.ShouldBe(TimeSpan.FromMinutes(20));
}
private static DistributedCacheEntryOptions GetDefaultCachingOptions(object instance)
{
var defaultOptionsField = instance.GetType().GetTypeInfo().GetField("DefaultCacheOptions", BindingFlags.Instance | BindingFlags.NonPublic);
return (DistributedCacheEntryOptions)defaultOptionsField.GetValue(instance);
}
}
}

52
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

@ -72,5 +72,55 @@ namespace Volo.Abp.Caching
factoryExecuted.ShouldBeFalse();
cacheItem.Name.ShouldBe(personName);
}
[Fact]
public async Task SameClassName_But_DiffNamespace_Should_Not_Use_Same_Cache()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var otherPersonCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
const string personName = "john nash";
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
var cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//Set
cacheItem = new PersonCacheItem(personName);
await personCache.SetAsync(cacheKey, cacheItem);
//Get (it should be available now, but otherPersonCache not exists now.
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//set other person cache
cacheItem1 = new Sail.Testing.Caching.PersonCacheItem(personName);
await otherPersonCache.SetAsync(cacheKey, cacheItem1);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
}
}
}
}

20
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/PersonCacheItem.cs

@ -9,7 +9,25 @@ namespace Volo.Abp.Caching
private PersonCacheItem()
{
}
public PersonCacheItem(string name)
{
Name = name;
}
}
}
namespace Sail.Testing.Caching
{
[Serializable]
public class PersonCacheItem
{
public string Name { get; private set; }
private PersonCacheItem()
{
}
public PersonCacheItem(string name)

7
modules/account/Volo.Abp.Account.sln

@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5881429-EFF
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Account.Web", "src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj", "{FCAC4354-7B13-4A91-A2F4-04D00F253C91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Account.Web.IdentityServer", "src\Volo.Abp.Account.Web.IdentityServer\Volo.Abp.Account.Web.IdentityServer.csproj", "{841C216F-B0E9-472C-BC19-2C31ADF0664F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -17,12 +19,17 @@ Global
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCAC4354-7B13-4A91-A2F4-04D00F253C91}.Release|Any CPU.Build.0 = Release|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{841C216F-B0E9-472C-BC19-2C31ADF0664F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FCAC4354-7B13-4A91-A2F4-04D00F253C91} = {B5881429-EFF7-4F30-8C0B-0AC41E36B74E}
{841C216F-B0E9-472C-BC19-2C31ADF0664F} = {B5881429-EFF7-4F30-8C0B-0AC41E36B74E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B054393-D2B2-4EA8-8A15-D60CBCF3E7A9}

14
modules/account/src/Volo.Abp.Account.Web.IdentityServer/AbpAccountWebIdentityServerModule.cs

@ -0,0 +1,14 @@
using Volo.Abp.IdentityServer;
using Volo.Abp.Modularity;
namespace Volo.Abp.Account.Web
{
[DependsOn(
typeof(AbpAccountWebModule),
typeof(AbpIdentityServerDomainModule)
)]
public class AbpAccountWebIdentityServerModule : AbpModule
{
}
}

239
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -0,0 +1,239 @@
using IdentityModel;
using IdentityServer4.Events;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace Volo.Abp.Account.Web.Pages.Account
{
[ExposeServices(typeof(LoginModel))]
public class IdentityServerSupportedLoginModel : LoginModel
{
protected IIdentityServerInteractionService Interaction { get; }
protected IClientStore ClientStore { get; }
protected IEventService IdentityServerEvents { get; }
public IdentityServerSupportedLoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IEventService identityServerEvents)
:base(
schemeProvider,
accountOptions)
{
_schemeProvider = schemeProvider;
Interaction = interaction;
ClientStore = clientStore;
IdentityServerEvents = identityServerEvents;
_accountOptions = accountOptions.Value;
}
public override async Task OnGetAsync()
{
LoginInput = new LoginInputModel();
var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl);
if (context != null)
{
LoginInput.UserNameOrEmailAddress = context.LoginHint;
//TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key!
var tenant = context.Parameters[TenantResolverConsts.DefaultTenantKey];
if (string.IsNullOrEmpty(tenant))
{
if (Request.Cookies.ContainsKey(TenantResolverConsts.DefaultTenantKey))
{
CurrentTenant.Change(null);
Response.Cookies.Delete(TenantResolverConsts.DefaultTenantKey);
}
}
else
{
CurrentTenant.Change(Guid.Parse(tenant));
Response.Cookies.Append(TenantResolverConsts.DefaultTenantKey, tenant);
}
}
if (context?.IdP != null)
{
LoginInput.UserNameOrEmailAddress = context.LoginHint;
ExternalProviders = new[] { new ExternalProviderModel { AuthenticationScheme = context.IdP } };
return;
}
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null || x.Name.Equals(_accountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
.Select(x => new ExternalProviderModel
{
DisplayName = x.DisplayName,
AuthenticationScheme = x.Name
})
.ToList();
EnableLocalLogin = true; //TODO: We can get default from a setting?
if (context?.ClientId != null)
{
var client = await ClientStore.FindEnabledClientByIdAsync(context.ClientId);
if (client != null)
{
EnableLocalLogin = client.EnableLocalLogin;
if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
{
providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
}
}
}
ExternalProviders = providers.ToArray();
if (IsExternalLoginOnly)
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException();
}
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
public override async Task<IActionResult> OnPostAsync(string action)
{
EnableLocalLogin = true; //TODO: We can get default from a setting?
if (action == "Cancel")
{
var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl);
if (context == null)
{
return Redirect("~/");
}
await Interaction.GrantConsentAsync(context, ConsentResponse.Denied);
return Redirect(ReturnUrl);
}
ValidateModel();
await ReplaceEmailToUsernameOfInputIfNeeds();
var result = await SignInManager.PasswordSignInAsync(
LoginInput.UserNameOrEmailAddress,
LoginInput.Password,
LoginInput.RememberMe,
true
);
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = LoginInput.RememberMe
});
}
if (result.IsLockedOut)
{
Alerts.Warning(L["UserLockedOutMessage"]);
return Page();
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode");
}
if (result.IsNotAllowed)
{
Alerts.Warning(L["LoginIsNotAllowed"]);
return Page();
}
if (!result.Succeeded)
{
Alerts.Danger(L["InvalidUserNameOrPassword"]);
return Page();
}
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
Debug.Assert(user != null, nameof(user) + " != null");
await IdentityServerEvents.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName)); //TODO: Use user's name once implemented
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
[UnitOfWork]
public override async Task<IActionResult> OnPostExternalLogin(string provider)
{
if (_accountOptions.WindowsAuthenticationSchemeName == provider)
{
return await ProcessWindowsLoginAsync();
}
return await base.OnPostExternalLogin(provider);
}
private async Task<IActionResult> ProcessWindowsLoginAsync()
{
var result = await HttpContext.AuthenticateAsync(_accountOptions.WindowsAuthenticationSchemeName);
if (!(result?.Principal is WindowsPrincipal windowsPrincipal))
{
return Challenge(_accountOptions.WindowsAuthenticationSchemeName);
}
var props = new AuthenticationProperties
{
RedirectUri = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash }),
Items =
{
{"scheme", _accountOptions.WindowsAuthenticationSchemeName},
}
};
var identity = new ClaimsIdentity(_accountOptions.WindowsAuthenticationSchemeName);
identity.AddClaim(new Claim(JwtClaimTypes.Subject, windowsPrincipal.Identity.Name));
identity.AddClaim(new Claim(JwtClaimTypes.Name, windowsPrincipal.Identity.Name));
//TODO: Consider to add Windows groups the the identity
//if (_accountOptions.IncludeWindowsGroups)
//{
// var windowsIdentity = windowsPrincipal.Identity as WindowsIdentity;
// if (windowsIdentity != null)
// {
// var groups = windowsIdentity.Groups?.Translate(typeof(NTAccount));
// var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
// identity.AddClaims(roles);
// }
//}
await HttpContext.SignInAsync(
IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme,
new ClaimsPrincipal(identity),
props
);
return RedirectSafely(props.RedirectUri);
}
}
}

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml

@ -0,0 +1,2 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

27
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49583/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.Account.Web.IdentityServer": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:49584/"
}
}
}

33
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Account.Web.IdentityServer</AssemblyName>
<PackageId>Volo.Abp.Account.Web.IdentityServer</PackageId>
<IsPackable>true</IsPackable>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace>Volo.Abp.Account.Web</RootNamespace>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.*" Exclude="*.cs" />
</ItemGroup>
<ItemGroup>
<Content Remove="Properties\launchSettings.json" />
<EmbeddedResource Remove="Pages\Account\IdentityServerSupportedLoginModel.cs" />
<None Include="Properties\launchSettings.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\identityserver\src\Volo.Abp.IdentityServer.Domain\Volo.Abp.IdentityServer.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>
</Project>

6
modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs

@ -15,8 +15,10 @@ using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Account.Web
{
[DependsOn(typeof(AbpIdentityAspNetCoreModule))]
[DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))]
[DependsOn(
typeof(AbpIdentityAspNetCoreModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule)
)]
public class AbpAccountWebModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)

73
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml

@ -1,30 +1,59 @@
@page
@model Volo.Abp.Account.Web.Pages.Account.LoginModel
@using Volo.Abp.Account.Web.Settings
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage
@model Volo.Abp.Account.Web.Pages.Account.LoginModel
@inject Volo.Abp.Settings.ISettingManager SettingManager
<h2>@L["Login"]</h2>
@if (Model.EnableLocalLogin)
{
<form method="post">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
<div class="form-group">
<label asp-for="LoginInput.UserNameOrEmailAddress"></label>
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" />
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LoginInput.Password"></label>
<input asp-for="LoginInput.Password" class="form-control" />
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input" />
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
<abp-button type="button" button-type="Secondary" name="Action" value="Cancel">Cancel</abp-button> @* TODO: Only show if identity server is used *@
<abp-button type="submit" button-type="Primary" name="Action" value="Login">Login</abp-button>
</form>
<form method="post">
<abp-input asp-for="Input.UserNameOrEmailAddress" auto-focus="true" />
<abp-input asp-for="Input.Password" />
<abp-input asp-for="Input.RememberMe" class="mb-3" />
<abp-button button-type="Primary" type="submit">@L["Login"]</abp-button>
@if (string.Equals(await SettingManager.GetOrNullAsync(AccountSettingNames.IsSelfRegistrationEnabled), "true", StringComparison.OrdinalIgnoreCase))
{
<a abp-button="Secondary" href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["Register"]</a>
}
</form>
<div style="padding-top: 20px">
@if (string.Equals(await SettingManager.GetOrNullAsync(AccountSettingNames.IsSelfRegistrationEnabled), "true", StringComparison.OrdinalIgnoreCase))
{
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">Register</a>
}
</div>
}
@if (Model.ExternalLogins.Any())
@if (Model.VisibleExternalProviders.Any())
{
<h4>Use another service to log in.</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<div>
@foreach (var provider in Model.ExternalLogins)
<div class="col-md-6">
<h4>Use another service to log in.</h4>
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post">
<input asp-for="ReturnUrl" />
<input asp-for="ReturnUrlHash" />
@foreach (var provider in Model.VisibleExternalProviders)
{
<abp-button button-type="Primary" type="submit" name="provider" value="@provider.Name">@provider.DisplayName</abp-button>
<button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</div>
</form>
}
</form>
</div>
}
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>Invalid login request</strong>
There are no login schemes configured for this client.
</div>
}

156
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -1,49 +1,112 @@
using System;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
namespace Volo.Abp.Account.Web.Pages.Account
{
public class LoginModel : AccountPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[BindProperty]
public PostInput Input { get; set; }
public LoginInputModel LoginInput { get; set; }
public bool EnableLocalLogin { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
//TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable<ExternalProviderModel> ExternalProviders { get; set; }
public IEnumerable<ExternalProviderModel> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
//Optional IdentityServer services
//public IIdentityServerInteractionService Interaction { get; set; }
//public IClientStore ClientStore { get; set; }
//public IEventService IdentityServerEvents { get; set; }
protected IAuthenticationSchemeProvider _schemeProvider;
protected AbpAccountOptions _accountOptions;
public LoginModel(
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions)
{
_schemeProvider = schemeProvider;
_accountOptions = accountOptions.Value;
}
public async Task OnGetAsync()
public virtual async Task OnGetAsync()
{
ExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
LoginInput = new LoginInputModel();
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null || x.Name.Equals(_accountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase))
.Select(x => new ExternalProviderModel
{
DisplayName = x.DisplayName,
AuthenticationScheme = x.Name
})
.ToList();
EnableLocalLogin = true; //TODO: We can get default from a setting?
ExternalProviders = providers.ToArray();
if (IsExternalLoginOnly)
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException();
}
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
public virtual async Task<IActionResult> OnPostAsync()
public virtual async Task<IActionResult> OnPostAsync(string action)
{
EnableLocalLogin = true; //TODO: We can get default from a setting?
ValidateModel();
await ReplaceEmailToUsernameOfInputIfNeeds();
var result = await SignInManager.PasswordSignInAsync(
Input.UserNameOrEmailAddress,
Input.Password,
Input.RememberMe,
LoginInput.UserNameOrEmailAddress,
LoginInput.Password,
LoginInput.RememberMe,
true
);
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash,
rememberMe = LoginInput.RememberMe
});
}
if (result.IsLockedOut)
{
Alerts.Warning(L["UserLockedOutMessage"]);
@ -67,22 +130,33 @@ namespace Volo.Abp.Account.Web.Pages.Account
return Page();
}
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
Debug.Assert(user != null, nameof(user) + " != null");
return RedirectSafely(ReturnUrl, ReturnUrlHash);
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
public virtual IActionResult OnPostExternalLogin(string provider, string returnUrl = "", string returnUrlHash = "")
{
var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { returnUrl, returnUrlHash });
[UnitOfWork]
public virtual async Task<IActionResult> OnPostExternalLogin(string provider)
{
var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash });
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
properties.Items["scheme"] = provider;
return new ChallengeResult(provider, properties);
return Challenge(properties, provider);
}
[UnitOfWork] //TODO: Will be removed when we implement action filter
[UnitOfWork]
public virtual async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
{
//TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample)
/* Also did not implement these:
* - Logout(string logoutId)
*/
if (remoteError != null)
{
Logger.LogWarning($"External login callback error: {remoteError}");
@ -108,13 +182,13 @@ namespace Volo.Abp.Account.Web.Pages.Account
throw new UserFriendlyException("Cannot proceed because user is locked out!");
}
//TODO: Handle other cases
if (result.Succeeded)
{
return RedirectSafely(returnUrl, returnUrlHash);
}
//TODO: Handle other cases for result!
// Get the information about the user from the external login provider
var info = await SignInManager.GetExternalLoginInfoAsync();
if (info == null)
@ -128,7 +202,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
return RedirectSafely(returnUrl, returnUrlHash);
}
private async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
{
var emailAddress = info.Principal.FindFirstValue(AbpClaimTypes.Email);
@ -141,18 +215,46 @@ namespace Volo.Abp.Account.Web.Pages.Account
return user;
}
public class PostInput
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds()
{
if (!ValidationHandler.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress))
{
return;
}
var userByUsername = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
if (userByUsername != null)
{
return;
}
var userByEmail = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
if (userByEmail == null)
{
return;
}
LoginInput.UserNameOrEmailAddress = userByEmail.UserName;
}
public class LoginInputModel
{
[Required]
[StringLength(255)]
[StringLength(IdentityUserConsts.MaxEmailLength)]
public string UserNameOrEmailAddress { get; set; }
[Required]
[StringLength(32)]
[StringLength(IdentityUserConsts.MaxPasswordLength)]
[DataType(DataType.Password)]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
public class ExternalProviderModel
{
public string DisplayName { get; set; }
public string AuthenticationScheme { get; set; }
}
}
}

1
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj

@ -6,7 +6,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundJobs.EntityFrameworkCore\Volo.Abp.BackgroundJobs.EntityFrameworkCore.csproj" />

4
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Volo.BloggingTestApp.EntityFrameworkCore.csproj

@ -8,10 +8,6 @@
<Compile Remove="Migrations\20181108103917_Identity_Changes.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Blogging.EntityFrameworkCore\Volo.Blogging.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />

48
modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs

@ -13,18 +13,20 @@ using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.Swagger;
using Volo.Abp;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Web;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.Threading;
using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem;
@ -45,6 +47,8 @@ namespace Volo.BloggingTestApp
typeof(AbpAccountWebModule),
typeof(AbpIdentityWebModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule)
)]
@ -75,13 +79,13 @@ namespace Volo.BloggingTestApp
{
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPyhsical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<AbpAspNetCoreMvcUiBasicThemeModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPyhsical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBootstrapModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiThemeSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<AbpAspNetCoreMvcUiBasicThemeModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}..{0}..{0}framework{0}src{0}Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<BloggingDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Domain", Path.DirectorySeparatorChar)));
options.FileSets.ReplaceEmbeddedByPhysical<BloggingWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}Volo.Blogging.Web", Path.DirectorySeparatorChar)));
});
}
@ -90,6 +94,7 @@ namespace Volo.BloggingTestApp
{
options.SwaggerDoc("v1", new Info { Title = "Blogging API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
var cultures = new List<CultureInfo> { new CultureInfo("en"), new CultureInfo("tr") };
@ -142,15 +147,26 @@ namespace Volo.BloggingTestApp
template: "{controller=Home}/{action=Index}/{id?}");
});
AsyncHelper.RunSync(async () =>
using (var scope = context.ServiceProvider.CreateScope())
{
await context.ServiceProvider
.GetRequiredService<IIdentityDataSeeder>()
.SeedAsync(
"1q2w3E*",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
});
AsyncHelper.RunSync(async () =>
{
await scope.ServiceProvider
.GetRequiredService<IIdentityDataSeeder>()
.SeedAsync(
"1q2w3E*"
);
await scope.ServiceProvider
.GetRequiredService<IPermissionDataSeeder>()
.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
IdentityPermissions.GetAll().Union(BloggingPermissions.GetAll())
);
});
}
}
}
}

2
modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj

@ -31,6 +31,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.Web\Volo.Abp.Identity.Web.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.PermissionManagement.Domain.Identity\Volo.Abp.PermissionManagement.Domain.Identity.csproj" />
<ProjectReference Include="..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.Application\Volo.Abp.PermissionManagement.Application.csproj" />
<ProjectReference Include="..\..\..\..\modules\account\src\Volo.Abp.Account.Web\Volo.Abp.Account.Web.csproj" />
</ItemGroup>

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs

@ -8,7 +8,7 @@ namespace Volo.Blogging.Comments
{
public interface ICommentAppService : IApplicationService
{
Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input);
Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId);
Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input);

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/Dtos/GetPopularTagsInput.cs

@ -7,7 +7,5 @@ namespace Volo.Blogging.Tagging.Dtos
public int ResultCount { get; set; } = 10;
public int? MinimumPostCount { get; set; }
public Guid BlogId { get; set; }
}
}

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Tagging/ITagAppService.cs

@ -8,7 +8,7 @@ namespace Volo.Blogging.Tagging
{
public interface ITagAppService : IApplicationService
{
Task<List<TagDto>> GetPopularTags(GetPopularTagsInput input);
Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input);
}
}

2
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs

@ -15,7 +15,7 @@ namespace Volo.Blogging
public BloggingApplicationAutoMapperProfile()
{
CreateMap<Blog, BlogDto>();
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount);
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount).Ignore(x=>x.Tags);
CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer);
CreateMap<Tag, TagDto>();
}

8
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs

@ -26,9 +26,9 @@ namespace Volo.Blogging.Comments
UserLookupService = userLookupService;
}
public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input)
public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId)
{
var comments = await GetListOfPostAsync(input);
var comments = await GetListOfPostAsync(postId);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
foreach (var commentDto in comments)
@ -73,9 +73,9 @@ namespace Volo.Blogging.Comments
return hierarchicalComments;
}
private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(GetCommentListOfPostAsync input)
private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(Guid postId)
{
var comments = await _commentRepository.GetListOfPostAsync(input.PostId);
var comments = await _commentRepository.GetListOfPostAsync(postId);
return new List<CommentWithDetailsDto>(
ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments));

4
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Tagging/TagAppService.cs

@ -22,9 +22,9 @@ namespace Volo.Blogging.Tagging
_tagRepository = tagRepository;
}
public async Task<List<TagDto>> GetPopularTags(GetPopularTagsInput input)
public async Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input)
{
var postTags = (await _tagRepository.GetListAsync(input.BlogId)).OrderByDescending(t=>t.UsageCount)
var postTags = (await _tagRepository.GetListAsync(blogId)).OrderByDescending(t=>t.UsageCount)
.WhereIf(input.MinimumPostCount != null, t=>t.UsageCount >= input.MinimumPostCount)
.Take(input.ResultCount).ToList();

11
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
@ -14,10 +12,7 @@ namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Controller]
[ControllerName("Blogs")]
[Route("api/blogging/blogs")]
[DisableAuditing]
public class BlogsController : AbpController, IBlogAppService
{
private readonly IBlogAppService _blogAppService;
@ -28,21 +23,20 @@ namespace Volo.Blogging
}
[HttpGet]
[Route("")]
public async Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{
return await _blogAppService.GetListPagedAsync(input);
}
[HttpGet]
[Route("/all")]
[Route("all")]
public async Task<ListResultDto<BlogDto>> GetListAsync()
{
return await _blogAppService.GetListAsync();
}
[HttpGet]
[Route("{shortName}")]
[Route("by-shortname/{shortName}")]
public async Task<BlogDto> GetByShortNameAsync(string shortName)
{
return await _blogAppService.GetByShortNameAsync(shortName);
@ -69,6 +63,7 @@ namespace Volo.Blogging
}
[HttpDelete]
[Route("{id}")]
public async Task Delete(Guid id)
{
await _blogAppService.Delete(id);

53
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/CommentsController.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/comments")]
public class CommentsController : AbpController, ICommentAppService
{
private readonly ICommentAppService _commentAppService;
public CommentsController(ICommentAppService commentAppService)
{
_commentAppService = commentAppService;
}
[HttpGet]
[Route("hierarchical/{postId}")]
public Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(Guid postId)
{
return _commentAppService.GetHierarchicalListOfPostAsync(postId);
}
[HttpPost]
public Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input)
{
return _commentAppService.CreateAsync(input);
}
[HttpPut]
[Route("{id}")]
public Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input)
{
return _commentAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
throw new NotImplementedException();
}
}
}

65
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs

@ -0,0 +1,65 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Posts;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/posts")]
public class PostsController : AbpController, IPostAppService
{
private readonly IPostAppService _postAppService;
public PostsController(IPostAppService postAppService)
{
_postAppService = postAppService;
}
[HttpGet]
[Route("{blogId}/all")]
public Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid blogId, string tagName)
{
return _postAppService.GetListByBlogIdAndTagName(blogId, tagName);
}
[HttpGet]
[Route("read/{id}")]
public Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input)
{
return _postAppService.GetForReadingAsync(input);
}
[HttpGet]
[Route("{id}")]
public Task<PostWithDetailsDto> GetAsync(Guid id)
{
return _postAppService.GetAsync(id);
}
[HttpPost]
public Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{
return _postAppService.CreateAsync(input);
}
[HttpPut]
[Route("{id}")]
public Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{
return _postAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
return _postAppService.DeleteAsync(id);
}
}
}

32
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/TagsController.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Route("api/blogging/tags")]
public class TagsController : AbpController, ITagAppService
{
private readonly ITagAppService _tagAppService;
public TagsController(ITagAppService tagAppService)
{
_tagAppService = tagAppService;
}
[HttpGet]
[Route("popular/{blogId}")]
public Task<List<TagDto>> GetPopularTags(Guid blogId, GetPopularTagsInput input)
{
return _tagAppService.GetPopularTags(blogId, input);
}
}
}

2
modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs

@ -40,8 +40,6 @@ namespace Volo.Blogging
context.Menu.AddItem(managementRootMenuItem);
}
}
}
}
}

4
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs

@ -35,9 +35,9 @@ namespace Volo.Blogging.Pages.Admin.Blogs
public async Task<IActionResult> OnPostAsync()
{
var language = ObjectMapper.Map<BlogCreateModalView, CreateBlogDto>(Blog);
var blogDto = ObjectMapper.Map<BlogCreateModalView, CreateBlogDto>(Blog);
await _blogAppService.Create(language);
await _blogAppService.Create(blogDto);
return NoContent();
}

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs

@ -70,7 +70,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl });
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(new GetCommentListOfPostAsync() { PostId = Post.Id });
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(Post.Id);
CountComments();
}

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs

@ -39,7 +39,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = (await _postAppService.GetListByBlogIdAndTagName(Blog.Id, TagName)).Items;
PopularTags = (await _tagAppService.GetPopularTags(new GetPopularTagsInput {BlogId = Blog.Id, ResultCount = 10, MinimumPostCount = 2}));
PopularTags = (await _tagAppService.GetPopularTags(Blog.Id, new GetPopularTagsInput {ResultCount = 10, MinimumPostCount = 2}));
}
}
}

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

Loading…
Cancel
Save