Browse Source

Merge branch 'dev' into gterdem/organization_unit_enhancement

pull/4186/head
Galip Tolga Erdem 6 years ago
parent
commit
955376c1ab
  1. 20
      docs/en/AspNet-Boilerplate-Migration-Guide.md
  2. 1
      docs/en/Customizing-Application-Modules-Overriding-Services.md
  3. 12
      docs/en/Multi-Tenancy.md
  4. 55
      docs/en/Object-To-Object-Mapping.md
  5. 1
      docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md
  6. 12
      docs/zh-Hans/Multi-Tenancy.md
  7. 12
      docs/zh-Hans/SignalR-Integration.md
  8. 46
      docs/zh-Hans/Tutorials/Part-1.md
  9. 26
      docs/zh-Hans/Tutorials/Part-2.md
  10. 474
      docs/zh-Hans/UI/Angular/Component-Replacement.md
  11. 9
      docs/zh-Hans/docs-nav.json
  12. BIN
      docs/zh-Hans/images/hello-template.png
  13. BIN
      docs/zh-Hans/images/multiple-file-template.png
  14. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs
  15. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs
  16. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
  17. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs
  18. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs
  19. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs
  20. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs
  21. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs
  22. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs
  23. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs
  24. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs
  25. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs
  26. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs
  27. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs
  28. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs
  29. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs
  30. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj
  31. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json
  32. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml
  33. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml
  34. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/Default.cshtml
  35. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml
  36. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj
  37. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj
  38. 19
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponent.cs
  39. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponentModel.cs
  40. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/Default.cshtml
  41. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj
  42. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js
  43. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj
  44. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj
  45. 15
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/ViewFeatures/ViewContextExtensions.cs
  46. 17
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs
  47. 26
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  48. 167
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs
  49. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs
  50. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs
  51. 9
      framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs
  52. 1
      framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
  53. 5
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
  54. 137
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs
  55. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs
  56. 5
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs
  57. 74
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  58. 70
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs
  59. 6
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/cs.json
  60. 5
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json
  61. 51
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs
  62. 6
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  63. 7
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs
  64. 2
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json
  65. 7
      framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/cs.json
  66. 5
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs
  67. 2
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json
  68. 8
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json
  69. 2
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json
  70. 4
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json
  71. 177
      framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions_Tests.cs
  72. 34
      framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs
  73. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json
  74. 2
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json
  75. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json
  76. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json
  77. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json
  78. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json
  79. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/cs.json
  80. 3
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json
  81. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj
  82. 6
      modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs
  83. 2
      modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml
  84. 3
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
  85. 2
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs
  86. 6
      modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj
  87. 9
      modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln
  88. 21
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.sln
  89. 23
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobService.cs
  90. 32
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj
  91. 32
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppHostedService.cs
  92. 56
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppModule.cs
  93. 20
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContext.cs
  94. 29
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContextFactory.cs
  95. 26
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbSchemaMigrator.cs
  96. 9
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/IBlobStoringHostDbSchemaMigrator.cs
  97. 9
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/IBlobService.cs
  98. 97
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs
  99. 62
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs
  100. 108
      modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs

20
docs/en/AspNet-Boilerplate-Migration-Guide.md

@ -216,9 +216,9 @@ However, it provides the following methods those can be used to query a single e
* `FindAsync(id)` returns the entity or null if not found.
* `GetAsync(id)` method returns the entity or throws an `EntityNotFoundException` (which causes HTTP 404 status code) if not found.
#### Sync vs Async
#### Sync vs Async
ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions.
ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions.
In general, ABP Framework forces you to completely use async everywhere, because mixing async & sync methods is not a recommended approach.
@ -444,7 +444,7 @@ ASP.NET Boilerplate uses Castle Windsor's [logging facility](http://docs.castlep
using Castle.Core.Logging; //1: Import Logging namespace
public class TaskAppService : ITaskAppService
{
{
//2: Getting a logger using property injection
public ILogger Logger { get; set; }
@ -693,26 +693,22 @@ public class AbpTenantManagementWebMainMenuContributor : IMenuContributor
var administrationMenu = context.Menu.GetAdministration();
//Resolve some needed services from the DI container
var authorizationService = context.ServiceProvider
.GetRequiredService<IAuthorizationService>();
var l = context.ServiceProvider
.GetRequiredService<IStringLocalizer<AbpTenantManagementResource>>();
var l = context.GetLocalizer<AbpTenantManagementResource>();
var tenantManagementMenuItem = new ApplicationMenuItem(
TenantManagementMenuNames.GroupName,
l["Menu:TenantManagement"],
icon: "fa fa-users");
administrationMenu.AddItem(tenantManagementMenuItem);
//Conditionally add the "Tenants" menu item based on the permission
if (await authorizationService
.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
{
tenantManagementMenuItem.AddItem(
new ApplicationMenuItem(
TenantManagementMenuNames.Tenants,
l["Tenants"],
l["Tenants"],
url: "/TenantManagement/Tenants"));
}
}
@ -731,4 +727,4 @@ The following features are not present for the ABP Framework. Here, a list of so
* [Real time notification system](https://aspnetboilerplate.com/Pages/Documents/Notification-System) ([#633](https://github.com/abpframework/abp/issues/633))
* [NHibernate Integration](https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration) ([#339](https://github.com/abpframework/abp/issues/339)) - We don't intent to work on this, but any community contribution welcome.
Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated.
Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated.

1
docs/en/Customizing-Application-Modules-Overriding-Services.md

@ -59,6 +59,7 @@ In most cases, you will want to change one or a few methods of the current imple
### Example: Overriding an Application Service
````csharp
//[RemoteService(IsEnabled = false)] // If you use dynamic controller feature you can disable remote service. Prevent creating duplicate controller for the application service.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService))]
public class MyIdentityUserAppService : IdentityUserAppService

12
docs/en/Multi-Tenancy.md

@ -303,10 +303,10 @@ TODO:...
Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box:
* **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for security**.
* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default.
* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route.
* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default.
* **CookieTenantResolver**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default.
* **QueryStringTenantResolveContributor**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default.
* **RouteTenantResolveContributor**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route.
* **HeaderTenantResolveContributor**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default.
* **CookieTenantResolveContributor**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default.
> If you use nginx as a reverse proxy server, please note that if `TenantKey` contains an underscore or other special characters, there may be a problem, please refer to:
http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers
@ -346,7 +346,7 @@ namespace MyCompany.MyProject
//Subdomain format: {0}.mydomain.com
//Adding as the second highest priority resolver after 'CurrentUserTenantResolveContributor' to
//ensure the user cannot impersonate a different tenant.
options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));
});
//...
@ -357,7 +357,7 @@ namespace MyCompany.MyProject
{0} is the the placeholder to determine current tenant's unique name.
Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut:
Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));`` you can use this shortcut:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");

55
docs/en/Object-To-Object-Mapping.md

@ -162,6 +162,61 @@ public class MyProfile : Profile
It is suggested to use the `MapExtraProperties()` method if both classes are extensible objects (implement the `IHasExtraProperties` interface). See the [object extension document](Object-Extensions.md) for more.
### Other Useful Extension Methods
There are some more extension methods those can simplify your mapping code.
#### Ignoring Audit Properties
It is common to ignore audit properties when you map an object to another.
Assume that you need to map a `ProductDto` ([DTO](Data-Transfer-Objects.md)) to a `Product` [entity](Entities.md) and the entity is inheriting from the `AuditedEntity` class (which provides properties like `CreationTime`, `CreatorId`, `IHasModificationTime`... etc).
You probably want to ignore these base properties while mapping from the DTO. You can use `IgnoreAuditedObjectProperties()` method to ignore all audit properties (instead of manually ignoring them one by one):
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<ProductDto, Product>()
.IgnoreAuditedObjectProperties();
}
}
````
There are more extension methods like `IgnoreFullAuditedObjectProperties()` and `IgnoreCreationAuditedObjectProperties()` those can be used based on your entity type.
> See the "*Base Classes & Interfaces for Audit Properties*" section in the [entities document](Entities.md) to know more about auditing properties.
#### Ignoring Other Properties
In AutoMapper, you typically write such a mapping code to ignore a property:
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<SimpleClass1, SimpleClass2>()
.ForMember(x => x.CreationTime, map => map.Ignore());
}
}
````
We found it unnecessarily long and created the `Ignore()` extension method:
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<SimpleClass1, SimpleClass2>()
.Ignore(x => x.CreationTime);
}
}
````
## Advanced Topics
### IObjectMapper<TContext> Interface

1
docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md

@ -59,6 +59,7 @@ context.Services.Replace(
### 示例: 重写服务方法
````csharp
//[RemoteService(IsEnabled = false)] // 如果你在使用动态控制器,为了避免为应用服务创建重复的控制器, 你可以禁用远程访问.
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService))]
public class MyIdentityUserAppService : IdentityUserAppService

12
docs/zh-Hans/Multi-Tenancy.md

@ -304,10 +304,10 @@ TODO:...
Volo.Abp.AspNetCore.MultiTenancy 添加了下面这些租户解析器,从当前Web请求(按优先级排序)中确定当前租户.
* **CurrentUserTenantResolveContributor**: 如果当前用户已登录,从当前用户的声明中获取租户Id. **出于安全考虑,应该始终将其做为第一个Contributor**.
* **QueryStringTenantResolver**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant".
* **RouteTenantResolver**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户.
* **HeaderTenantResolver**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant".
* **CookieTenantResolver**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant".
* **QueryStringTenantResolveContributor**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant".
* **RouteTenantResolveContributor**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户.
* **HeaderTenantResolveContributor**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant".
* **CookieTenantResolveContributor**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant".
> 如果你使用nginx作为反向代理服务器,请注意如果`TenantKey`包含下划线或其他特殊字符可能存在问题, 请参考:
http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers
@ -344,7 +344,7 @@ namespace MyCompany.MyProject
Configure<AbpTenantResolveOptions>(options =>
{
//子域名格式: {0}.mydomain.com (作为第二优先级解析器添加, 位于CurrentUserTenantResolveContributor之后)
options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));
options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));
});
//...
@ -355,7 +355,7 @@ namespace MyCompany.MyProject
{0}是用来确定当前租户唯一名称的占位符.
你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));``:
你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));``:
````C#
options.AddDomainTenantResolver("{0}.mydomain.com");

12
docs/zh-Hans/SignalR-Integration.md

@ -224,4 +224,14 @@ ABP实现 `SignalR` 的 `IUserIdProvider` 接口,从ABP框架的 `ICurrentUser`
参阅 [SignalR集成Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo),它有一个简单的聊天页面,可以在(经过身份验证的)用户之间发送消息.
![signalr-demo-chat](images/signalr-demo-chat.png)
![signalr-demo-chat](images/signalr-demo-chat.png)
## 备注
ABP框架不会更改SignalR. 就像在其他ASP.NET Core应用程序中一样,它也可以在基于ABP框架的应用程序中工作.
参考[微软文档](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/scale)托管和扩展您的应用程序,集成[Azure](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/publish-to-azure-web-app)或[Redis底版](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/redis-backplane)...等.
## 另请参阅
* [微软SignalR文档](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction)

46
docs/zh-Hans/Tutorials/Part-1.md

@ -30,7 +30,7 @@ ASP.NET Core {{UI_Value}} 系列教程包括三个3个部分:
- [Part-2: 创建,编辑,删除书籍](Part-2.md)
- [Part-3: 集成测试](Part-3.md)
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-booktore-application).
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application).
### 创建新项目
@ -44,19 +44,19 @@ ASP.NET Core {{UI_Value}} 系列教程包括三个3个部分:
abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none
```
![Creating project](./images/booktore-create-project-{{UI_Text}}.png)
![Creating project](./images/bookstore-create-project-{{UI_Text}}.png)
### 应用迁移
项目创建后,需要应用初始化迁移创建数据库. 运行 `Acme.BookStore.DbMigrator` 应用程序. 它会应用所有迁移,完成流程后你会看到以下结果,数据库已经准备好了!
![Migrations applied](./images/booktore-migrations-applied-{{UI_Text}}.png)
![Migrations applied](./images/bookstore-migrations-applied-{{UI_Text}}.png)
> 另外你也可以在 Visual Studio 包管理控制台运行 `Update-Database` 命令应用迁移.
#### 初始化数据库表
![Initial database tables](./images/booktore-database-tables-{{DB}}.png)
![Initial database tables](./images/bookstore-database-tables-{{DB}}.png)
### 运行应用程序
@ -64,7 +64,7 @@ abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}
更多信息,参阅[入门教程](../../Getting-Started?UI={{UI}})的运行应用程序部分.
![Set as startup project](./images/booktore-start-project-{{UI_Text}}.png)
![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png)
{{if UI == "NG"}}
@ -105,7 +105,7 @@ http://localhost:4200/
下面的图片展示了从启动模板创建的项目是如何分层的.
![booktore-visual-studio-solution](./images/booktore-solution-structure-{{UI_Text}}.png)
![bookstore-visual-studio-solution](./images/bookstore-solution-structure-{{UI_Text}}.png)
> 你可以查看[应用程序模板文档](../startup-templates/application#solution-structure)以详细了解解决方案结构.
@ -295,7 +295,7 @@ namespace Acme.BookStore
这个启动模板使用了[EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/)来创建并维护数据库结构.打开 **程序包管理器控制台(Package Manager Console) (PMC)** (工具/Nuget包管理器菜单)
![Open Package Manager Console](./images/booktore-open-package-manager-console.png)
![Open Package Manager Console](./images/bookstore-open-package-manager-console.png)
选择 `Acme.BookStore.EntityFrameworkCore.DbMigrations`作为默认的项目然后执行下面的命令:
@ -303,7 +303,7 @@ namespace Acme.BookStore
Add-Migration "Created_Book_Entity"
```
![booktore-pmc-add-book-migration](./images/booktore-pmc-add-book-migration-v2.png)
![bookstore-pmc-add-book-migration](./images/bookstore-pmc-add-book-migration-v2.png)
这样就会在 `Migrations` 文件夹中创建一个新的migration类.然后执行 `Update-Database` 命令更新数据库结构:
@ -311,7 +311,7 @@ Add-Migration "Created_Book_Entity"
Update-Database
````
![booktore-update-database-after-book-entity](./images/booktore-update-database-after-book-entity.png)
![bookstore-update-database-after-book-entity](./images/bookstore-update-database-after-book-entity.png)
#### 添加示例数据
@ -328,7 +328,7 @@ INSERT INTO AppBook (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES
('4fa024a1-95ac-49c6-a709-6af9e4d54b54', '2018-07-02', 'Pet Sematary',5,'1983-11-14','23.7')
```
![booktore-book-table](./images/booktore-book-table.png)
![bookstore-book-table](./images/bookstore-book-table.png)
{{end}}
@ -501,7 +501,7 @@ namespace Acme.BookStore
你会看到一些内置的接口和`Book`的接口,它们都是REST风格的:
![booktore-swagger](images/booktore-swagger.png)
![bookstore-swagger](images/bookstore-swagger.png)
Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表.
@ -529,11 +529,11 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); })
运行这段代码会产生下面的输出:
![booktore-test-js-proxy-getlist](./images/booktore-test-js-proxy-getlist.png)
![bookstore-test-js-proxy-getlist](./images/bookstore-test-js-proxy-getlist.png)
你可以看到服务器返回的 **book list**.你还可以切换到开发者工具的 **network** 查看客户端到服务器端的通讯信息:
![booktore-test-js-proxy-getlist-network](./images/booktore-test-js-proxy-getlist-network.png)
![bookstore-test-js-proxy-getlist-network](./images/bookstore-test-js-proxy-getlist-network.png)
我们使用`create`方法 **创建一本新书**:
@ -555,7 +555,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246
在 `Acme.BookStore.Web`项目的`Pages`文件夹下创建一个新的文件夹叫`Book`并添加一个名为`Index.cshtml`的Razor Page.
![booktore-add-index-page](./images/booktore-add-index-page-v2.png)
![bookstore-add-index-page](./images/bookstore-add-index-page-v2.png)
打开`Index.cshtml`并把内容修改成下面这样:
@ -621,7 +621,7 @@ namespace Acme.BookStore.Web.Menus
本地化文本位于`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下:
![booktore-localization-files](./images/booktore-localization-files-v2.png)
![bookstore-localization-files](./images/bookstore-localization-files-v2.png)
打开`en.json`文件,将`Menu:BookStore`和`Menu:Book`键的本地化文本添加到文件末尾:
@ -653,7 +653,7 @@ namespace Acme.BookStore.Web.Menus
运行该应用程序,看到新菜单项已添加到顶部栏:
![booktore-menu-items](./images/booktore-new-menu-item.png)
![bookstore-menu-items](./images/bookstore-new-menu-item.png)
点击BookStore下Book子菜单项就会跳转到新增的书籍页面.
@ -701,7 +701,7 @@ namespace Acme.BookStore.Web.Menus
在`Pages/Book/`文件夹中创建 `index.js`文件
![booktore-index-js-file](./images/booktore-index-js-file-v2.png)
![bookstore-index-js-file](./images/bookstore-index-js-file-v2.png)
`index.js`的内容如下:
@ -727,7 +727,7 @@ $(function () {
最终的页面如下:
![Book list](./images/booktore-book-list-2.png)
![Book list](./images/bookstore-book-list-2.png)
{{end}}
@ -760,7 +760,7 @@ yarn
yarn ng generate module book --routing true
```
![Generating book module](./images/booktore-creating-book-module-terminal.png)
![Generating book module](./images/bookstore-creating-book-module-terminal.png)
#### 路由
@ -793,7 +793,7 @@ import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; //==> added th
yarn ng generate component book/book-list
```
![Creating book list](./images/booktore-creating-book-list-terminal.png)
![Creating book list](./images/bookstore-creating-book-list-terminal.png)
打开 `app\book` 目录下的 `book.module.ts` 文件,使用以下内容替换它:
@ -849,7 +849,7 @@ yarn start
我们将看到book页面的 **book-list works!**:
![Initial book list page](./images/booktore-initial-book-list-page.png)
![Initial book list page](./images/bookstore-initial-book-list-page.png)
#### 创建 BookState
@ -1048,11 +1048,11 @@ export class BookListComponent implements OnInit {
现在你可以在浏览器看到最终结果:
![Book list final result](./images/booktore-book-list.png)
![Book list final result](./images/bookstore-book-list.png)
项目的文件系统结构:
![Book list final result](./images/booktore-angular-file-tree.png)
![Book list final result](./images/bookstore-angular-file-tree.png)
在本教程中我们遵循了官方的[Angular风格指南](https://angular.io/guide/styleguide#file-tree).

26
docs/zh-Hans/Tutorials/Part-2.md

@ -29,7 +29,7 @@ end
- **Part 2: 创建,编辑,删除书籍(本章)**
- [Part-3: 集成测试](Part-3.md)
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-booktore-application).
> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application).
{{if UI == "MVC"}}
@ -37,13 +37,13 @@ end
通过本节, 你将会了解如何创建一个 modal form 来实现新增书籍的功能. 最终成果如下图所示:
![booktore-create-dialog](./images/booktore-create-dialog-2.png)
![bookstore-create-dialog](./images/bookstore-create-dialog-2.png)
#### 新建 modal form
在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面:
![booktore-add-create-dialog](./images/booktore-add-create-dialog-v2.png)
![bookstore-add-create-dialog](./images/bookstore-add-create-dialog-v2.png)
##### CreateModal.cshtml.cs
@ -130,7 +130,7 @@ namespace Acme.BookStore.Web.Pages.Books
如下图所示,只是在表格 **右上方** 添加了 **New book** 按钮:
![booktore-new-book-button](./images/booktore-new-book-button.png)
![bookstore-new-book-button](./images/bookstore-new-book-button.png)
打开 `Pages/book/index.js` 在 `datatable` 配置代码后面添加如下代码:
@ -155,7 +155,7 @@ $('#NewBookButton').click(function (e) {
在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个名叫 `EditModal.cshtml` 的Razor页面:
![booktore-add-edit-dialog](./images/booktore-add-edit-dialog.png)
![bookstore-add-edit-dialog](./images/bookstore-add-edit-dialog.png)
#### EditModal.cshtml.cs
@ -258,7 +258,7 @@ namespace Acme.BookStore.Web
我们将为表格每行添加下拉按钮 ("Actions") . 最终效果如下:
![booktore-book-table-actions](images/booktore-book-table-actions.png)
![bookstore-book-table-actions](images/bookstore-book-table-actions.png)
打开 `Pages/Books/Index.cshtml` 页面,并按下方所示修改表格部分的代码:
@ -450,6 +450,8 @@ $(function () {
{{end}}
{{if UI == "NG"}}
### 新增 Book 实体
下面的章节中,你将学习到如何创建一个新的模态对话框来新增Book实体.
@ -654,7 +656,7 @@ export class BookListComponent implements OnInit {
你可以打开浏览器,点击**New book**按钮看到模态框.
![Empty modal for new book](./images/booktore-empty-new-book-modal.png)
![Empty modal for new book](./images/bookstore-empty-new-book-modal.png)
#### 添加响应式表单
@ -878,7 +880,7 @@ export class BookListComponent implements OnInit {
现在你可以打开浏览器看到以下变化:
![New book modal](./images/booktore-new-book-form.png)
![New book modal](./images/bookstore-new-book-form.png)
#### 保存图书
@ -990,7 +992,7 @@ export class BookListComponent implements OnInit {
模态框最终看起来像这样:
![Save button to the modal](./images/booktore-new-book-form-v2.png)
![Save button to the modal](./images/bookstore-new-book-form-v2.png)
### 更新图书
@ -1192,7 +1194,7 @@ export class BookListComponent implements OnInit {
UI最终看起来像这样:
![Action buttons](./images/booktore-actions-buttons.png)
![Action buttons](./images/bookstore-actions-buttons.png)
打开 `app\app\book\book-list` 文件夹下的 `book-list.component.html` 文件,使用以下内容替换 `<ng-template #abpHeader>` 标签:
@ -1322,7 +1324,7 @@ delete(id: string) {
`delete` 方法会显示一个确认弹层并订阅用户响应. 只在用户点击 `Yes` 按钮时分派动作. 确认弹层看起来如下:
![booktore-confirmation-popup](./images/booktore-confirmation-popup.png)
![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png)
#### 添加删除按钮
@ -1339,7 +1341,7 @@ delete(id: string) {
最终操作下拉框UI看起来如下:
![booktore-final-actions-dropdown](./images/booktore-final-actions-dropdown.png)
![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png)
{{end}}

474
docs/zh-Hans/UI/Angular/Component-Replacement.md

@ -15,16 +15,20 @@ import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddRepl
import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum
import { Store } from '@ngxs/store'; // imported Store
//...
export class AppComponent {
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
// added dispatch
this.store.dispatch(
new AddReplaceableComponent({
component: YourNewRoleComponent,
key: eIdentityComponents.Roles,
}),
);
//...
}
}
@ -54,7 +58,7 @@ yarn ng generate component shared/my-application-layout --export --entryComponen
<router-outlet></router-outlet>
```
打开 `app.component.ts` 添加以下内容:
打开 `src/app` 文件夹下的 `app.component.ts` 文件添加以下内容:
```js
import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent
@ -62,11 +66,13 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB
import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent
import { Store } from '@ngxs/store'; // imported Store
//...
export class AppComponent {
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
// added below content
// added dispatch
this.store.dispatch(
new AddReplaceableComponent({
component: MyApplicationLayoutComponent,
@ -79,6 +85,464 @@ export class AppComponent {
}
```
### 布局组件
![Layout Components](./images/layout-components.png)
#### 如何替换LogoComponent
![LogoComponent](./images/logo-component.png)
在 `angular` 目录下运行以下命令创建新的组件 `LogoComponent`:
```bash
yarn ng generate component logo --inlineTemplate --inlineStyle --entryComponent
# You don't need the --entryComponent option in Angular 9
```
打开 `src/app/logo` 目录下生成的 `logo.component.ts` 并使用以下内容替换它:
```js
import { Component } from '@angular/core';
@Component({
selector: 'app-logo',
template: `
<a class="navbar-brand" routerLink="/">
<!-- Change the img src -->
<img
src="https://via.placeholder.com/100x50/343a40/FF0000?text=MyLogo"
alt="logo"
width="100%"
height="auto"
/>
</a>
`,
})
export class LogoComponent {}
```
打开 `src/app` 目录下的 `app.component.ts` 做以下修改:
```js
import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent
import { Store } from '@ngxs/store'; // imported Store
import { LogoComponent } from './logo/logo.component'; // imported NavItemsComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents
//...
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
//...
// added dispatch
this.store.dispatch(
new AddReplaceableComponent({
component: LogoComponent,
key: eThemeBasicComponents.Logo,
}),
);
}
}
```
最终UI如下:
![New logo](./images/replaced-logo-component.png)
#### 如何替换RoutesComponent
![RoutesComponent](./images/routes-component.png)
在 `angular` 目录下运行以下命令创建新的组件 `RoutesComponent`:
```bash
yarn ng generate component routes --entryComponent
# You don't need the --entryComponent option in Angular 9
```
打开 `src/app/routes` 目录下生成的 `routes.component.ts` 并使用以下内容替换它:
```js
import { ABP, ReplaceableComponents } from '@abp/ng.core';
import {
Component,
HostBinding,
Inject,
Renderer2,
TrackByFunction,
AfterViewInit,
} from '@angular/core';
import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
@Component({
selector: 'app-routes',
templateUrl: 'routes.component.html',
})
export class RoutesComponent implements AfterViewInit {
@HostBinding('class.mx-auto')
marginAuto = true;
smallScreen = window.innerWidth < 992;
constructor(private renderer: Renderer2) {}
ngAfterViewInit() {
fromEvent(window, 'resize')
.pipe(debounceTime(150))
.subscribe(() => {
this.smallScreen = window.innerWidth < 992;
});
}
}
```
打开 `src/app/routes` 目录下生成的 `routes.component.html` 并使用以下内容替换它:
```html
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/"
><i class="fas fa-home"></i> {%{{{ '::Menu:Home' | abpLocalization }}}%}</a
>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/my-page"><i class="fas fa-newspaper mr-1"></i>My Page</a>
</li>
<li
#navbarRootDropdown
[abpVisibility]="routeContainer"
class="nav-item dropdown"
display="static"
(click)="
navbarRootDropdown.expand
? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true)
"
>
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
<i class="fas fa-wrench"></i>
{%{{{ 'AbpUiNavigation::Menu:Administration' | abpLocalization }}}%}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpIdentity.Roles || AbpIdentity.Users'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-left dropdown-toggle"
>
<i class="fa fa-id-card-o"></i>
{%{{{ 'AbpIdentity::Menu:IdentityManagement' | abpLocalization }}}%}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Roles'">
<a class="dropdown-item" routerLink="/identity/roles">
{%{{{ 'AbpIdentity::Roles' | abpLocalization }}}%}</a
>
</div>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Users'">
<a class="dropdown-item" routerLink="/identity/users">
{%{{{ 'AbpIdentity::Users' | abpLocalization }}}%}</a
>
</div>
</div>
</div>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-left dropdown-toggle"
>
<i class="fa fa-users"></i>
{%{{{ 'AbpTenantManagement::Menu:TenantManagement' | abpLocalization }}}%}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div class="dropdown-submenu" *abpPermission="'AbpTenantManagement.Tenants'">
<a class="dropdown-item" routerLink="/tenant-management/tenants">
{%{{{ 'AbpTenantManagement::Tenants' | abpLocalization }}}%}</a
>
</div>
</div>
</div>
</div>
</li>
</ul>
```
打开 `src/app` 目录下的 `app.component.ts` 做以下修改:
```js
import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent
import { Store } from '@ngxs/store'; // imported Store
import { RoutesComponent } from './routes/routes.component'; // imported NavItemsComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents
//...
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
//...
// added dispatch
this.store.dispatch(
new AddReplaceableComponent({
component: RoutesComponent,
key: eThemeBasicComponents.Routes,
}),
);
}
}
```
最终UI如下:
![New routes](./images/replaced-routes-component.png)
#### 如何替换NavItemsComponent
![NavItemsComponent](./images/nav-items-component.png)
在 `angular` 目录下运行以下命令创建新的组件 `NavItemsComponent`:
```bash
yarn ng generate component nav-items --entryComponent
# You don't need the --entryComponent option in Angular 9
```
打开 `src/app/nav-items` 目录下生成的 `nav-items.component.ts` 并使用以下内容替换它:
```js
import {
ApplicationConfiguration,
AuthService,
ConfigState,
SessionState,
SetLanguage,
} from '@abp/ng.core';
import { Component, AfterViewInit } from '@angular/core';
import { Navigate, RouterState } from '@ngxs/router-plugin';
import { Select, Store } from '@ngxs/store';
import { Observable, fromEvent } from 'rxjs';
import { map, debounceTime } from 'rxjs/operators';
import snq from 'snq';
@Component({
selector: 'app-nav-items',
templateUrl: 'nav-items.component.html',
})
export class NavItemsComponent implements AfterViewInit {
@Select(ConfigState.getOne('currentUser'))
currentUser$: Observable<ApplicationConfiguration.CurrentUser>;
@Select(ConfigState.getDeep('localization.languages'))
languages$: Observable<ApplicationConfiguration.Language[]>;
smallScreen = window.innerWidth < 992;
get defaultLanguage$(): Observable<string> {
return this.languages$.pipe(
map(
languages =>
snq(
() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName,
),
'',
),
);
}
get dropdownLanguages$(): Observable<ApplicationConfiguration.Language[]> {
return this.languages$.pipe(
map(
languages =>
snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)),
[],
),
);
}
get selectedLangCulture(): string {
return this.store.selectSnapshot(SessionState.getLanguage);
}
constructor(private store: Store, private authService: AuthService) {}
ngAfterViewInit() {
fromEvent(window, 'resize')
.pipe(debounceTime(150))
.subscribe(() => {
this.smallScreen = window.innerWidth < 992;
});
}
onChangeLang(cultureName: string) {
this.store.dispatch(new SetLanguage(cultureName));
}
logout() {
this.authService.logout().subscribe(() => {
this.store.dispatch(
new Navigate(['/'], null, {
state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url },
}),
);
});
}
}
```
打开 `src/app/nav-items` 目录下生成的 `nav-items.component.html` 并使用以下内容替换它:
```html
<ul class="navbar-nav">
<input type="search" placeholder="Search" class="bg-transparent border-0 text-white" />
<li *ngIf="(dropdownLanguages$ | async)?.length > 0" class="nav-item">
<div class="dropdown" ngbDropdown #languageDropdown="ngbDropdown" display="static">
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{%{{{ defaultLanguage$ | async }}}%}
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && languageDropdown.isOpen()"
>
<a
*ngFor="let lang of dropdownLanguages$ | async"
href="javascript:void(0)"
class="dropdown-item"
(click)="onChangeLang(lang.cultureName)"
>{%{{{ lang?.displayName }}}%}</a
>
</div>
</div>
</li>
<li class="nav-item">
<ng-template #loginBtn>
<a role="button" class="nav-link" routerLink="/account/login">{%{{{
'AbpAccount::Login' | abpLocalization
}}}%}</a>
</ng-template>
<div
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn"
ngbDropdown
class="dropdown"
#currentUserDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{%{{{ (currentUser$ | async)?.userName }}}%}
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && currentUserDropdown.isOpen()"
>
<a class="dropdown-item" routerLink="/account/manage-profile"
><i class="fa fa-cog mr-1"></i>{%{{{ 'AbpAccount::ManageYourProfile' | abpLocalization }}}%}</a
>
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()"
><i class="fa fa-power-off mr-1"></i>{%{{{ 'AbpUi::Logout' | abpLocalization }}}%}</a
>
</div>
</div>
</li>
</ul>
```
打开 `src/app` 目录下的 `app.component.ts` 做以下修改:
```js
import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent
import { Store } from '@ngxs/store'; // imported Store
import { NavItemsComponent } from './nav-items/nav-items.component'; // imported NavItemsComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents
//...
@Component(/* component metadata */)
export class AppComponent implements OnInit {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
//...
// added dispatch
this.store.dispatch(
new AddReplaceableComponent({
component: NavItemsComponent,
key: eThemeBasicComponents.NavItems,
}),
);
}
}
```
最终UI如下:
![New nav-items](./images/replaced-nav-items-component.png)
## 下一步是什么?
- [自定义设置页面](./Custom-Setting-Page.md)
- [自定义设置页面](./Custom-Setting-Page.md)

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

@ -454,6 +454,15 @@
}
]
},
{
"text": "实时",
"items": [
{
"text": "SignalR集成",
"path": "SignalR-Integration.md"
}
]
},
{
"text": "后台服务",
"items": [

BIN
docs/zh-Hans/images/hello-template.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
docs/zh-Hans/images/multiple-file-template.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

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

@ -1,5 +1,7 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
@ -8,17 +10,21 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
{
public abstract class AbpTagHelper : TagHelper, ITransientDependency
{
}
public abstract class AbpTagHelper<TTagHelper, TService> : AbpTagHelper
where TTagHelper : AbpTagHelper<TTagHelper, TService>
where TService : class, IAbpTagHelperService<TTagHelper>
where TService : class, IAbpTagHelperService<TTagHelper>
{
protected TService Service { get; }
public override int Order => Service.Order;
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
protected AbpTagHelper(TService service)
{
Service = service;
@ -40,4 +46,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers
return Service.ProcessAsync(context, output);
}
}
}
}

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

@ -12,10 +12,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
[HtmlAttributeName("abp-model")]
public ModelExpression Model { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public bool? SubmitButton { get; set; }
public bool? RequiredSymbols { get; set; } = true;

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs

@ -29,15 +29,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
[HtmlAttributeName("required-symbol")]
public bool DisplayRequiredSymbol { get; set; } = true;
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName("asp-format")]
public string Format { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public AbpInputTagHelper(AbpInputTagHelperService tagHelperService)

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

@ -18,10 +18,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public IEnumerable<SelectListItem> AspItems { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public AbpRadioInputTagHelper(AbpRadioInputTagHelperService tagHelperService)
: base(tagHelperService)
{

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

@ -21,10 +21,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
[HtmlAttributeName("required-symbol")]
public bool DisplayRequiredSymbol { get; set; } = true;
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public AbpSelectTagHelper(AbpSelectTagHelperService tagHelperService)
: base(tagHelperService)
{

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs

@ -11,10 +11,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination
public bool? ShowInfo { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public AbpPaginationTagHelper(AbpPaginationTagHelperService tagHelperService)
: base(tagHelperService)
{

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs

@ -3,9 +3,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public abstract class AbpBundleItemTagHelper<TTagHelper, TTagHelperService> : AbpTagHelper<TTagHelper, TTagHelperService>, IBundleItemTagHelper
public abstract class AbpBundleItemTagHelper<TTagHelper, TTagHelperService> : AbpTagHelper<TTagHelper, TTagHelperService>, IBundleItemTagHelper
where TTagHelper : AbpTagHelper<TTagHelper, TTagHelperService>, IBundleItemTagHelper
where TTagHelperService: AbpBundleItemTagHelperService<TTagHelper>
where TTagHelperService: AbpBundleItemTagHelperService<TTagHelper, TTagHelperService>
{
/// <summary>
/// A file path.
@ -57,4 +57,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
protected abstract string GetFileExtension();
}
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs

@ -5,8 +5,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public abstract class AbpBundleItemTagHelperService<TTagHelper> : AbpTagHelperService<TTagHelper>
where TTagHelper : TagHelper, IBundleItemTagHelper
public abstract class AbpBundleItemTagHelperService<TTagHelper, TService> : AbpTagHelperService<TTagHelper>
where TTagHelper : AbpTagHelper<TTagHelper, TService>, IBundleItemTagHelper
where TService : class, IAbpTagHelperService<TTagHelper>
{
protected AbpTagHelperResourceService ResourceService { get; }
@ -26,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
else
{
await ResourceService.ProcessAsync(
TagHelper.ViewContext,
context,
output,
new List<BundleTagHelperItem>
@ -37,4 +39,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
}
}
}
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs

@ -5,8 +5,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public abstract class AbpBundleTagHelperService<TTagHelper> : AbpTagHelperService<TTagHelper>
where TTagHelper : TagHelper, IBundleTagHelper
public abstract class AbpBundleTagHelperService<TTagHelper, TService> : AbpTagHelperService<TTagHelper>
where TTagHelper : AbpTagHelper<TTagHelper, TService>, IBundleTagHelper
where TService : class, IAbpTagHelperService<TTagHelper>
{
protected AbpTagHelperResourceService ResourceService { get; }
@ -18,6 +19,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
await ResourceService.ProcessAsync(
TagHelper.ViewContext,
context,
output,
await GetBundleItems(context, output),
@ -33,4 +35,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
return bundleItems;
}
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs

@ -1,10 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public class AbpScriptBundleTagHelperService : AbpBundleTagHelperService<AbpScriptBundleTagHelper>
public class AbpScriptBundleTagHelperService : AbpBundleTagHelperService<AbpScriptBundleTagHelper, AbpScriptBundleTagHelperService>
{
public AbpScriptBundleTagHelperService(AbpTagHelperScriptService resourceHelper)
public AbpScriptBundleTagHelperService(AbpTagHelperScriptService resourceHelper)
: base(resourceHelper)
{
}
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs

@ -1,10 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public class AbpScriptTagHelperService : AbpBundleItemTagHelperService<AbpScriptTagHelper>
public class AbpScriptTagHelperService : AbpBundleItemTagHelperService<AbpScriptTagHelper, AbpScriptTagHelperService>
{
public AbpScriptTagHelperService(AbpTagHelperScriptService resourceService)
: base(resourceService)
{
}
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs

@ -1,10 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public class AbpStyleBundleTagHelperService : AbpBundleTagHelperService<AbpStyleBundleTagHelper>
public class AbpStyleBundleTagHelperService : AbpBundleTagHelperService<AbpStyleBundleTagHelper, AbpStyleBundleTagHelperService>
{
public AbpStyleBundleTagHelperService(AbpTagHelperStyleService resourceHelper)
public AbpStyleBundleTagHelperService(AbpTagHelperStyleService resourceHelper)
: base(resourceHelper)
{
}
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs

@ -1,10 +1,10 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
{
public class AbpStyleTagHelperService : AbpBundleItemTagHelperService<AbpStyleTagHelper>
public class AbpStyleTagHelperService : AbpBundleItemTagHelperService<AbpStyleTagHelper, AbpStyleTagHelperService>
{
public AbpStyleTagHelperService(AbpTagHelperStyleService resourceService)
: base(resourceService)
{
}
}
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@ -20,7 +22,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
protected IWebContentFileProvider WebContentFileProvider { get; }
protected IWebHostEnvironment HostingEnvironment { get; }
protected readonly AbpBundlingOptions Options;
protected AbpTagHelperResourceService(
IBundleManager bundleManager,
IWebContentFileProvider webContentFileProvider,
@ -36,11 +38,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
}
public virtual async Task ProcessAsync(
[NotNull] ViewContext viewContext,
[NotNull] TagHelperContext context,
[NotNull] TagHelperOutput output,
[NotNull] List<BundleTagHelperItem> bundleItems,
[CanBeNull] string bundleName = null)
{
Check.NotNull(viewContext, nameof(viewContext));
Check.NotNull(context, nameof(context));
Check.NotNull(output, nameof(output));
Check.NotNull(bundleItems, nameof(bundleItems));
@ -69,7 +73,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
throw new AbpException($"Could not find the bundle file '{bundleFile}' from {nameof(IWebContentFileProvider)}");
}
AddHtmlTag(context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks);
AddHtmlTag(viewContext, context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks);
}
stopwatch.Stop();
@ -80,11 +84,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
protected abstract Task<IReadOnlyList<string>> GetBundleFilesAsync(string bundleName);
protected abstract void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file);
protected abstract void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file);
protected virtual string GenerateBundleName(List<BundleTagHelperItem> bundleItems)
{
return bundleItems.JoinAsString("|").ToMd5();
}
}
}
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs

@ -3,7 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.VirtualFileSystem;
@ -39,9 +43,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
return await BundleManager.GetScriptBundleFilesAsync(bundleName);
}
protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file)
protected override void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file)
{
output.Content.AppendHtml($"<script src=\"{file}\"></script>{Environment.NewLine}");
output.Content.AppendHtml($"<script src=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\"></script>{Environment.NewLine}");
}
}
}
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs

@ -3,7 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.VirtualFileSystem;
@ -39,9 +43,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers
return await BundleManager.GetStyleBundleFilesAsync(bundleName);
}
protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file)
protected override void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file)
{
output.Content.AppendHtml($"<link rel=\"stylesheet\" href=\"{file}\" />{Environment.NewLine}");
output.Content.AppendHtml($"<link rel=\"stylesheet\" href=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\" />{Environment.NewLine}");
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj

@ -18,10 +18,8 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.cshtml" />
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\MultiTenancy\Localization\*.json" />
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Pages\**\*.js" />
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\MultiTenancy\Localization\*.json" />
</ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json

@ -9,4 +9,4 @@
"SwitchTenant": "Změnit tenant",
"NotSelected": "Nevybrán"
}
}
}

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml

@ -5,12 +5,13 @@
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";
var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass;
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.Content(menuItem.Url);
if (menuItem.IsLeaf)
{
if (menuItem.Url != null)
{
<li class="nav-item @cssClass @disabled" @elementId>
<a class="nav-link" href="@(menuItem.Url ?? "#")">
<a class="nav-link" href="@url">
@if (menuItem.Icon != null)
{
if (menuItem.Icon.StartsWith("fa"))
@ -46,4 +47,4 @@
</div>
</li>
}
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml

@ -4,12 +4,13 @@
var elementId = string.IsNullOrEmpty(Model.ElementId) ? string.Empty : $"id=\"{Model.ElementId}\"";
var cssClass = string.IsNullOrEmpty(Model.CssClass) ? string.Empty : Model.CssClass;
var disabled = Model.IsDisabled ? "disabled" : string.Empty;
var url = string.IsNullOrEmpty(Model.Url) ? "#" : Url.Content(Model.Url);
}
@if (Model.IsLeaf)
{
if (Model.Url != null)
{
<a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)>
<a class="dropdown-item @cssClass @disabled" href="@url" @Html.Raw(elementId)>
@if (Model.Icon != null)
{
if (Model.Icon.StartsWith("fa"))

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/LanguageSwitch/Default.cshtml

@ -12,7 +12,7 @@
<div class="dropdown-menu dropdown-menu-right border-0 shadow-sm" aria-labelledby="dropdownMenuLink">
@foreach (var language in Model.OtherLanguages)
{
<a class="dropdown-item" href="/Abp/Languages/Switch?culture=@(language.CultureName)&uiCulture=@(language.UiCultureName)&returnUrl=@(System.Net.WebUtility.UrlEncode(Context.Request.GetEncodedPathAndQuery()))">@language.DisplayName</a>
<a class="dropdown-item" href="~/Abp/Languages/Switch?culture=@(language.CultureName)&uiCulture=@(language.UiCultureName)&returnUrl=@(System.Net.WebUtility.UrlEncode(Context.Request.GetEncodedPathAndQuery()))">@language.DisplayName</a>
}
</div>
</div>

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml

@ -27,8 +27,9 @@
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";
var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass;
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.Content(menuItem.Url);
<a class="dropdown-item @cssClass @disabled" href="@(menuItem.Url ?? "#")" target="@menuItem.Target" @Html.Raw(elementId)>
<a class="dropdown-item @cssClass @disabled" href="@url" target="@menuItem.Target" @Html.Raw(elementId)>
@if (menuItem.Icon != null)
{
if (menuItem.Icon.StartsWith("fa"))

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj

@ -17,17 +17,11 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Themes\**\*.cshtml" />
<EmbeddedResource Include="Pages\**\*.cshtml" />
<EmbeddedResource Include="Views\**\*.cshtml" />
<EmbeddedResource Include="wwwroot\**\*.*" />
</ItemGroup>
<ItemGroup>
<Content Remove="wwwroot\**\*.*" />
<Content Remove="Themes\**\*.cshtml" />
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Views\**\*.cshtml" />
<Content Remove="Properties\launchSettings.json" />
<None Include="Properties\launchSettings.json" />
</ItemGroup>
@ -36,5 +30,5 @@
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy\Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
</ItemGroup>
</Project>

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj

@ -17,11 +17,6 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Views\**\*.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Remove="Views\**\*.cshtml" />
<Content Remove="compilerconfig.json" />
<Content Remove="Properties\launchSettings.json" />
<None Include="Properties\launchSettings.json" />

19
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponent.cs

@ -0,0 +1,19 @@
using System;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath
{
public class AbpApplicationPathViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
var applicationPath = ViewContext.HttpContext.Request.PathBase.Value;
var model = new AbpApplicationPathViewComponentModel
{
ApplicationPath = applicationPath == null ? "/" : applicationPath.EnsureEndsWith('/')
};
return View("~/Pages/Shared/Components/AbpApplicationPath/Default.cshtml",model);
}
}
}

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponentModel.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath
{
public class AbpApplicationPathViewComponentModel
{
public string ApplicationPath { get; set; }
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/Default.cshtml

@ -0,0 +1,4 @@
@model Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath.AbpApplicationPathViewComponentModel
<script type="text/javascript">
var abp = abp || {}; abp.appPath = '@Model.ApplicationPath';
</script>

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj

@ -18,16 +18,10 @@
<ItemGroup>
<EmbeddedResource Include="wwwroot\**\*.*" />
<EmbeddedResource Include="Pages\**\*.cshtml" />
<EmbeddedResource Include="Views\**\*.cshtml" />
<EmbeddedResource Include="Areas\**\*.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Remove="wwwroot\**\*.*" />
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Views\**\*.cshtml" />
<Content Remove="Areas\**\*.cshtml" />
<Content Remove="compilerconfig.json" />
<Content Remove="Properties\launchSettings.json" />
<None Include="compilerconfig.json" />

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js

@ -104,7 +104,7 @@
locale: abp.localization.currentCulture.name
}).toLocaleString();
},
getOptions($input) { //$input may needed if developer wants to override this method
getOptions: function($input) { //$input may needed if developer wants to override this method
return {
todayBtn: "linked",
autoclose: true,

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj

@ -17,9 +17,4 @@
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bundling\Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\Widgets\Components\**\*.cshtml" />
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\Widgets\Components\**\*.cshtml" />
</ItemGroup>
</Project>

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj

@ -14,11 +14,6 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\**\*.cshtml" />
<Content Remove="Volo\**\*.cshtml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NUglify" Version="1.5.13" />
</ItemGroup>

15
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/ViewFeatures/ViewContextExtensions.cs

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public static class ViewContextExtensions
{
public static IUrlHelper GetUrlHelper(this ViewContext viewContext)
{
var urlHelperFactory = viewContext.HttpContext.RequestServices.GetRequiredService<IUrlHelperFactory>();
return urlHelperFactory.GetUrlHelper(viewContext);
}
}
}

17
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs

@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
@ -8,13 +9,23 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static void AddApplicationPartIfNotExists(this IMvcBuilder mvcBuilder, Assembly assembly)
{
if (mvcBuilder.PartManager.ApplicationParts.Any(
mvcBuilder.PartManager.ApplicationParts.AddIfNotContains(assembly);
}
public static void AddApplicationPartIfNotExists(this IMvcCoreBuilder mvcCoreBuilder, Assembly assembly)
{
mvcCoreBuilder.PartManager.ApplicationParts.AddIfNotContains(assembly);
}
public static void AddIfNotContains(this IList<ApplicationPart> applicationParts, Assembly assembly)
{
if (applicationParts.Any(
p => p is AssemblyPart assemblyPart && assemblyPart.Assembly == assembly))
{
return;
}
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(assembly));
applicationParts.Add(new AssemblyPart(assembly));
}
}
}

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

@ -10,6 +10,7 @@ using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
@ -75,14 +76,14 @@ namespace Volo.Abp.AspNetCore.Mvc
(int) HttpStatusCode.NotImplemented,
(int) HttpStatusCode.InternalServerError
};
options.SupportedResponseTypes.AddIfNotContains(statusCodes.Select(statusCode => new ApiResponseType
{
Type = typeof(RemoteServiceErrorResponse),
StatusCode = statusCode
}));
});
context.Services.PostConfigure<AbpAspNetCoreMvcOptions>(options =>
{
if (options.MinifyGeneratedScript == null)
@ -126,10 +127,10 @@ namespace Volo.Abp.AspNetCore.Mvc
return factory.Create(resourceType);
}
return factory.CreateDefaultOrNull() ??
return factory.CreateDefaultOrNull() ??
factory.Create(type);
};
})
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
Configure<MvcRazorRuntimeCompilationOptions>(options =>
@ -161,7 +162,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var application = context.Services.GetSingletonInstance<IAbpApplication>();
partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application));
partManager.ApplicationParts.Add(new AssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly));
partManager.ApplicationParts.AddIfNotContains(typeof(AbpAspNetCoreMvcModule).Assembly);
Configure<MvcOptions>(mvcOptions =>
{
@ -179,6 +180,14 @@ namespace Volo.Abp.AspNetCore.Mvc
});
}
public override void PostConfigureServices(ServiceConfigurationContext context)
{
ApplicationPartSorter.Sort(
context.Services.GetSingletonInstance<ApplicationPartManager>(),
context.Services.GetSingletonInstance<IModuleContainer>()
);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AddApplicationParts(context);
@ -220,12 +229,7 @@ namespace Volo.Abp.AspNetCore.Mvc
{
foreach (var moduleAssembly in moduleAssemblies)
{
if (partManager.ApplicationParts.OfType<AssemblyPart>().Any(p => p.Assembly == moduleAssembly))
{
continue;
}
partManager.ApplicationParts.Add(new AssemblyPart(moduleAssembly));
partManager.ApplicationParts.AddIfNotContains(moduleAssembly);
}
}
}

167
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc
{
/// <summary>
/// This class is used to align order of the MVC Application Parts with the order of
/// ABP module dependencies.
/// </summary>
public static class ApplicationPartSorter
{
public static void Sort(ApplicationPartManager partManager, IModuleContainer moduleContainer)
{
/* Performing a double Reverse() to preserve the original order for non-sorted parts
*/
var dependencyDictionary = CreateDependencyDictionary(partManager, moduleContainer);
var sortedParts = partManager
.ApplicationParts
.Reverse() //First Revers
.SortByDependencies(p => dependencyDictionary[p]);
sortedParts.Reverse(); //Reverse again
//Replace the original parts with the sorted parts
partManager.ApplicationParts.Clear();
foreach (var applicationPart in sortedParts)
{
partManager.ApplicationParts.Add(applicationPart);
}
}
private static Dictionary<ApplicationPart, List<ApplicationPart>> CreateDependencyDictionary(
ApplicationPartManager partManager, IModuleContainer moduleContainer)
{
var dependencyDictionary = new Dictionary<ApplicationPart, List<ApplicationPart>>();
foreach (var applicationPart in partManager.ApplicationParts)
{
dependencyDictionary[applicationPart] =
CreateDependencyList(applicationPart, partManager, moduleContainer);
}
return dependencyDictionary;
}
private static List<ApplicationPart> CreateDependencyList(
ApplicationPart applicationPart,
ApplicationPartManager partManager,
IModuleContainer moduleContainer)
{
var list = new List<ApplicationPart>();
if (applicationPart is AssemblyPart assemblyPart)
{
AddDependencies(list, assemblyPart, partManager, moduleContainer);
}
else if (applicationPart is CompiledRazorAssemblyPart compiledRazorAssemblyPart)
{
AddDependencies(list, compiledRazorAssemblyPart, partManager, moduleContainer);
}
return list;
}
private static void AddDependencies(
List<ApplicationPart> list,
AssemblyPart assemblyPart,
ApplicationPartManager partManager,
IModuleContainer moduleContainer)
{
var dependedAssemblyParts = GetDependedAssemblyParts(
partManager,
moduleContainer,
assemblyPart
);
list.AddRange(dependedAssemblyParts);
foreach (var dependedAssemblyPart in dependedAssemblyParts)
{
var viewsPart = GetViewsPartOrNull(partManager, dependedAssemblyPart);
if (viewsPart != null)
{
list.Add(viewsPart);
}
}
}
private static void AddDependencies(
List<ApplicationPart> list,
CompiledRazorAssemblyPart compiledRazorAssemblyPart,
ApplicationPartManager partManager,
IModuleContainer moduleContainer)
{
if (!compiledRazorAssemblyPart.Name.EndsWith(".Views"))
{
return;
}
var originalAssemblyPart = GetOriginalAssemblyPartOrNull(compiledRazorAssemblyPart, partManager);
if (originalAssemblyPart == null)
{
return;
}
list.Add(originalAssemblyPart);
}
private static AssemblyPart[] GetDependedAssemblyParts(
ApplicationPartManager partManager,
IModuleContainer moduleContainer,
AssemblyPart assemblyPart)
{
var moduleDescriptor = GetModuleDescriptorForAssemblyOrNull(moduleContainer, assemblyPart.Assembly);
if (moduleDescriptor == null)
{
return Array.Empty<AssemblyPart>();
}
var moduleDependedAssemblies = moduleDescriptor
.Dependencies
.Select(d => d.Assembly)
.ToArray();
return partManager.ApplicationParts
.OfType<AssemblyPart>()
.Where(a => a.Assembly.IsIn(moduleDependedAssemblies))
.Distinct()
.ToArray();
}
private static CompiledRazorAssemblyPart GetViewsPartOrNull(ApplicationPartManager partManager,
ApplicationPart assemblyPart)
{
var viewsAssemblyName = assemblyPart.Name + ".Views";
return partManager
.ApplicationParts
.OfType<CompiledRazorAssemblyPart>()
.FirstOrDefault(p => p.Name == viewsAssemblyName);
}
private static AssemblyPart GetOriginalAssemblyPartOrNull(
CompiledRazorAssemblyPart compiledRazorAssemblyPart,
ApplicationPartManager partManager)
{
var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views");
return partManager.ApplicationParts
.OfType<AssemblyPart>()
.FirstOrDefault(p => p.Name == originalAssemblyName);
}
private static IAbpModuleDescriptor GetModuleDescriptorForAssemblyOrNull(
IModuleContainer moduleContainer,
Assembly assembly)
{
return moduleContainer
.Modules
.FirstOrDefault(m => m.Assembly == assembly);
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs

@ -86,7 +86,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication
protected virtual string GetAppHomeUrl()
{
return "/";
return "~/";
}
}
}
}

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

@ -32,14 +32,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization
return Redirect(GetRedirectUrl(returnUrl));
}
return Redirect("/");
return Redirect("~/");
}
private string GetRedirectUrl(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return "/";
return "~/";
}
if (Url.IsLocalUrl(returnUrl))
@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization
return returnUrl;
}
return "/";
return "~/";
}
}
}

9
framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Volo.Abp.AutoMapper;
using Volo.Abp.Data;
using Volo.Abp.ObjectExtending;
@ -35,5 +36,13 @@ namespace AutoMapper
})
);
}
public static IMappingExpression<TSource, TDestination> IgnoreExtraProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IHasExtraProperties
where TSource : IHasExtraProperties
{
return mappingExpression.Ignore(x => x.ExtraProperties);
}
}
}

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

@ -15,6 +15,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj" />
<ProjectReference Include="..\Volo.Abp.ObjectMapping\Volo.Abp.ObjectMapping.csproj" />
</ItemGroup>

5
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs

@ -2,6 +2,7 @@
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Auditing;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.ObjectMapping;
@ -10,7 +11,9 @@ namespace Volo.Abp.AutoMapper
{
[DependsOn(
typeof(AbpObjectMappingModule),
typeof(AbpObjectExtendingModule))]
typeof(AbpObjectExtendingModule),
typeof(AbpAuditingModule)
)]
public class AbpAutoMapperModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)

137
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs

@ -1,6 +1,7 @@
using System;
using System.Linq.Expressions;
using AutoMapper;
using Volo.Abp.Auditing;
namespace Volo.Abp.AutoMapper
{
@ -10,5 +11,141 @@ namespace Volo.Abp.AutoMapper
{
return mappingExpression.ForMember(destinationMember, opts => opts.Ignore());
}
public static IMappingExpression<TSource, TDestination> IgnoreHasCreationTimeProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IHasCreationTime
{
return mappingExpression.Ignore(x => x.CreationTime);
}
public static IMappingExpression<TSource, TDestination> IgnoreMayHaveCreatorProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IMayHaveCreator
{
return mappingExpression.Ignore(x => x.CreatorId);
}
public static IMappingExpression<TSource, TDestination> IgnoreCreationAuditedObjectProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : ICreationAuditedObject
{
return mappingExpression
.IgnoreHasCreationTimeProperties()
.IgnoreMayHaveCreatorProperties();
}
public static IMappingExpression<TSource, TDestination> IgnoreHasModificationTimeProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IHasModificationTime
{
return mappingExpression.Ignore(x => x.LastModificationTime);
}
public static IMappingExpression<TSource, TDestination> IgnoreModificationAuditedObjectProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IModificationAuditedObject
{
return mappingExpression
.IgnoreHasModificationTimeProperties()
.Ignore(x => x.LastModifierId);
}
public static IMappingExpression<TSource, TDestination> IgnoreAuditedObjectProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IAuditedObject
{
return mappingExpression
.IgnoreCreationAuditedObjectProperties()
.IgnoreModificationAuditedObjectProperties();
}
public static IMappingExpression<TSource, TDestination> IgnoreSoftDeleteProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : ISoftDelete
{
return mappingExpression.Ignore(x => x.IsDeleted);
}
public static IMappingExpression<TSource, TDestination> IgnoreHasDeletionTimeProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IHasDeletionTime
{
return mappingExpression
.IgnoreSoftDeleteProperties()
.Ignore(x => x.DeletionTime);
}
public static IMappingExpression<TSource, TDestination> IgnoreDeletionAuditedObjectProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IDeletionAuditedObject
{
return mappingExpression
.IgnoreHasDeletionTimeProperties()
.Ignore(x => x.DeleterId);
}
public static IMappingExpression<TSource, TDestination> IgnoreFullAuditedObjectProperties<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IFullAuditedObject
{
return mappingExpression
.IgnoreAuditedObjectProperties()
.IgnoreDeletionAuditedObjectProperties();
}
public static IMappingExpression<TSource, TDestination> IgnoreMayHaveCreatorProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IMayHaveCreator<TUser>
{
return mappingExpression
.Ignore(x => x.Creator);
}
public static IMappingExpression<TSource, TDestination> IgnoreCreationAuditedObjectProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : ICreationAuditedObject<TUser>
{
return mappingExpression
.IgnoreCreationAuditedObjectProperties()
.IgnoreMayHaveCreatorProperties<TSource, TDestination, TUser>();
}
public static IMappingExpression<TSource, TDestination> IgnoreModificationAuditedObjectProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IModificationAuditedObject<TUser>
{
return mappingExpression
.IgnoreModificationAuditedObjectProperties()
.Ignore(x => x.LastModifier);
}
public static IMappingExpression<TSource, TDestination> IgnoreAuditedObjectProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IAuditedObject<TUser>
{
return mappingExpression
.IgnoreCreationAuditedObjectProperties<TSource, TDestination, TUser>()
.IgnoreModificationAuditedObjectProperties<TSource, TDestination, TUser>();
}
public static IMappingExpression<TSource, TDestination> IgnoreDeletionAuditedObjectProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IDeletionAuditedObject<TUser>
{
return mappingExpression
.IgnoreDeletionAuditedObjectProperties()
.Ignore(x => x.Deleter);
}
public static IMappingExpression<TSource, TDestination> IgnoreFullAuditedObjectProperties<TSource, TDestination, TUser>(
this IMappingExpression<TSource, TDestination> mappingExpression)
where TDestination : IFullAuditedObject<TUser>
{
return mappingExpression
.IgnoreAuditedObjectProperties<TSource, TDestination, TUser>()
.IgnoreDeletionAuditedObjectProperties<TSource, TDestination, TUser>();
}
}
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs

@ -31,7 +31,7 @@ namespace Volo.Abp.Cli.ProjectModification
protected virtual string GetInstalledNpmPackages()
{
Logger.LogInformation("Checking installed npm global packages...");
return CmdHelper.RunCmdAndGetOutput("npm list -g --depth 0");
return CmdHelper.RunCmdAndGetOutput("npm list -g --depth 0 --silent");
}
protected virtual void InstallYarn()

5
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs

@ -180,7 +180,10 @@ namespace System.Collections.Generic
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="source">A list of objects to sort</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <returns></returns>
/// <returns>
/// Returns a new list ordered by dependencies.
/// If A depends on B, then B will come before than A in the resulting list.
/// </returns>
public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
{
/* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp

74
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
@ -42,6 +43,7 @@ namespace Volo.Abp
services.AddCoreAbpServices(this, options);
Modules = LoadModules(services, options);
ConfigureServices();
}
public virtual void Shutdown()
@ -75,7 +77,7 @@ namespace Volo.Abp
}
}
private IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)
{
return services
.GetSingletonInstance<IModuleLoader>()
@ -85,5 +87,75 @@ namespace Volo.Abp
options.PlugInSources
);
}
//TODO: We can extract a new class for this
protected virtual void ConfigureServices()
{
var context = new ServiceConfigurationContext(Services);
Services.AddSingleton(context);
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = context;
}
}
//PreConfigureServices
foreach (var module in Modules.Where(m => m.Instance is IPreConfigureServices))
{
try
{
((IPreConfigureServices)module.Instance).PreConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//ConfigureServices
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
Services.AddAssembly(module.Type.Assembly);
}
}
try
{
module.Instance.ConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//PostConfigureServices
foreach (var module in Modules.Where(m => m.Instance is IPostConfigureServices))
{
try
{
((IPostConfigureServices)module.Instance).PostConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = null;
}
}
}
}
}

70
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs

@ -20,7 +20,6 @@ namespace Volo.Abp.Modularity
var modules = GetDescriptors(services, startupModuleType, plugInSources);
modules = SortByDependency(modules, startupModuleType);
ConfigureServices(modules, services);
return modules.ToArray();
}
@ -89,75 +88,6 @@ namespace Volo.Abp.Modularity
return module;
}
protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services)
{
var context = new ServiceConfigurationContext(services);
services.AddSingleton(context);
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = context;
}
}
//PreConfigureServices
foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices))
{
try
{
((IPreConfigureServices)module.Instance).PreConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//ConfigureServices
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
services.AddAssembly(module.Type.Assembly);
}
}
try
{
module.Instance.ConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//PostConfigureServices
foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices))
{
try
{
((IPostConfigureServices)module.Instance).PostConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
foreach (var module in modules)
{
if (module.Instance is AbpModule abpModule)
{
abpModule.ServiceConfigurationContext = null;
}
}
}
protected virtual void SetDependencies(List<AbpModuleDescriptor> modules, AbpModuleDescriptor module)
{
foreach (var dependedModuleType in AbpModuleHelper.FindDependedModuleTypes(module.Type))

6
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/cs.json

@ -0,0 +1,6 @@
{
"culture": "cs",
"texts": {
"MaxResultCountExceededExceptionMessage": "{0} nemůže být více než {1}! Navyšte {2}.{3} na straně serveru pro více výsledků."
}
}

5
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json

@ -18,7 +18,8 @@
"Description:Abp.Mailing.Smtp.Password": "Heslo pro uživatelské jméno spojené s přihlašovacími údaji.",
"Description:Abp.Mailing.Smtp.Domain": "Název domény nebo počítače, který ověřuje přihlašovací údaje.",
"Description:Abp.Mailing.Smtp.EnableSsl": "Zda SmtpClient používá SSL k šifrování připojení.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Zda jsou výchozí přihlašovací údaje odesílány s požadavky."
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Zda jsou výchozí přihlašovací údaje odesílány s požadavky.",
"TextTemplate:StandardEmailTemplates.Layout": "Výchozí šablona rozložení emailu",
"TextTemplate:StandardEmailTemplates.Message": "Jednoduchá šablona zprávy pro emaily"
}
}

51
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Newtonsoft.Json;
@ -14,8 +15,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying
{
public ICancellationTokenProvider CancellationTokenProvider { get; set; }
protected IDynamicProxyHttpClientFactory HttpClientFactory { get; }
protected IApiDescriptionCache Cache { get; }
private static readonly JsonSerializerSettings SharedJsonSerializerSettings = new JsonSerializerSettings
@ -23,18 +22,15 @@ namespace Volo.Abp.Http.Client.DynamicProxying
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
public ApiDescriptionFinder(
IApiDescriptionCache cache,
IDynamicProxyHttpClientFactory httpClientFactory)
public ApiDescriptionFinder(IApiDescriptionCache cache)
{
Cache = cache;
HttpClientFactory = httpClientFactory;
CancellationTokenProvider = NullCancellationTokenProvider.Instance;
}
public async Task<ActionApiDescriptionModel> FindActionAsync(string baseUrl, Type serviceType, MethodInfo method)
public async Task<ActionApiDescriptionModel> FindActionAsync(HttpClient client, string baseUrl, Type serviceType, MethodInfo method)
{
var apiDescription = await GetApiDescriptionAsync(baseUrl);
var apiDescription = await GetApiDescriptionAsync(client, baseUrl);
//TODO: Cache finding?
@ -56,8 +52,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
var found = true;
for (int i = 0; i < methodParameters.Length; i++)
{
if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i]))
{
if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i]))
{
found = false;
break;
@ -76,33 +72,30 @@ namespace Volo.Abp.Http.Client.DynamicProxying
throw new AbpException($"Could not found remote action for method: {method} on the URL: {baseUrl}");
}
public virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(string baseUrl)
public virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(HttpClient client, string baseUrl)
{
return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(baseUrl));
return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(client, baseUrl));
}
protected virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionFromServerAsync(string baseUrl)
protected virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionFromServerAsync(HttpClient client, string baseUrl)
{
using (var client = HttpClientFactory.Create())
{
var response = await client.GetAsync(
baseUrl.EnsureEndsWith('/') + "api/abp/api-definition",
CancellationTokenProvider.Token
);
var response = await client.GetAsync(
baseUrl.EnsureEndsWith('/') + "api/abp/api-definition",
CancellationTokenProvider.Token
);
if (!response.IsSuccessStatusCode)
{
throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode);
}
if (!response.IsSuccessStatusCode)
{
throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode);
}
var content = await response.Content.ReadAsStringAsync();
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(
content,
typeof(ApplicationApiDescriptionModel), SharedJsonSerializerSettings);
var result = JsonConvert.DeserializeObject(
content,
typeof(ApplicationApiDescriptionModel), SharedJsonSerializerSettings);
return (ApplicationApiDescriptionModel)result;
}
return (ApplicationApiDescriptionModel)result;
}
protected virtual bool TypeMatches(MethodParameterApiDescriptionModel actionParameter, ParameterInfo methodParameter)

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

@ -106,7 +106,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
private async Task<T> MakeRequestAndGetResultAsync<T>(IAbpMethodInvocation invocation)
{
var responseAsString = await MakeRequestAsync(invocation);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(responseAsString, typeof(T));
@ -122,7 +122,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
var client = HttpClientFactory.Create(clientConfig.RemoteServiceName);
var action = await ApiDescriptionFinder.FindActionAsync(remoteServiceConfig.BaseUrl, typeof(TService), invocation.Method);
var action = await ApiDescriptionFinder.FindActionAsync(client, remoteServiceConfig.BaseUrl, typeof(TService), invocation.Method);
var apiVersion = GetApiVersionInfo(action);
var url = remoteServiceConfig.BaseUrl.EnsureEndsWith('/') + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion);
@ -252,4 +252,4 @@ namespace Volo.Abp.Http.Client.DynamicProxying
return CancellationTokenProvider.Token;
}
}
}
}

7
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs

@ -1,4 +1,5 @@
using System;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.Http.Modeling;
@ -7,8 +8,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying
{
public interface IApiDescriptionFinder
{
Task<ActionApiDescriptionModel> FindActionAsync(string baseUrl, Type serviceType, MethodInfo invocationMethod);
Task<ActionApiDescriptionModel> FindActionAsync(HttpClient client, string baseUrl, Type serviceType, MethodInfo invocationMethod);
Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(string baseUrl);
Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(HttpClient client, string baseUrl);
}
}
}

2
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json

@ -2,6 +2,6 @@
"culture": "cs",
"texts": {
"DisplayName:Abp.Localization.DefaultLanguage": "Výchozí jazyk",
"Description:Abp.Localization.DefaultLanguage": "Váchozí jazyk aplikace."
"Description:Abp.Localization.DefaultLanguage": "Výchozí jazyk aplikace."
}
}

7
framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/cs.json

@ -0,0 +1,7 @@
{
"culture": "cs",
"texts": {
"DisplayName:Abp.Timing.Timezone": "Časové pásmo",
"Description:Abp.Timing.Timezone": "Časové pásmo aplikace"
}
}

5
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
@ -101,7 +102,7 @@ namespace Volo.Abp.UI.Navigation
Name = name;
DisplayName = displayName;
Url = url;
Url = url?.EnsureStartsWith('~');
Icon = icon;
Order = order;
CustomData = customData;
@ -133,4 +134,4 @@ namespace Volo.Abp.UI.Navigation
return $"[ApplicationMenuItem] Name = {Name}";
}
}
}
}

2
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json

@ -3,4 +3,4 @@
"texts": {
"Menu:Administration": "Administrace"
}
}
}

8
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json

@ -13,11 +13,13 @@
"DefaultErrorMessage404": "Prostředek nenalezen!",
"DefaultErrorMessage404Detail": "Vyžádaný prostředek nebyl na serveru nalezen!",
"EntityNotFoundErrorMessage": "Neexistující entita {0} s id = {1}!",
"Languages": "Jazyky",
"Error": "Chyba",
"AreYouSure": "Jste si jisti?",
"Cancel": "Zrušit",
"Yes": "Ano",
"No": "Ne",
"Ok": "Ok",
"Close": "Zavřít",
"Save": "Uložit",
"SavingWithThreeDot": "Ukládám...",
@ -25,6 +27,8 @@
"Delete": "Smazat",
"Edit": "Upravit",
"Refresh": "Obnovit",
"Language": "Jazyk",
"LoadMore": "Načíst více",
"ProcessingWithThreeDot": "Zpracovávám...",
"LoadingWithThreeDot": "Nahrávám...",
"Welcome": "Vítejte",
@ -42,7 +46,7 @@
"PagerInfo{0}{1}{2}": "Zobrazeno od {0} do {1} z celkem {2} záznamů",
"PagerInfoEmpty": "Zobrazeno od 0 do 0 z celkem 0 záznamů",
"PagerInfoFiltered": "(filtrováno ze všech _MAX_ záznamů)",
"NoDataAvailableInDatatable": "V tabulce nejsou žádná data",
"NoDataAvailableInDatatable": "Nejsou žádná data",
"PagerShowMenuEntries": "Zobrazit _MENU_ záznamů",
"DatatableActionDropdownDefaultText": "Akce",
"ChangePassword": "Změnit heslo",
@ -56,4 +60,4 @@
"GoHomePage": "Přejít na domovskou stránku",
"GoBack": "Jít zpět"
}
}
}

2
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json

@ -31,4 +31,4 @@
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "Pole není platná plně kvalifikovaná adresa http, https, nebo ftp URL.",
"ThisFieldIsInvalid.": "Pole je neplatné."
}
}
}

4
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json

@ -2,6 +2,6 @@
"culture": "cs",
"texts": {
"BirthDate": "Narozeniny",
"Value1": "Hodnota jedna"
"Value1": "Hodnota jedna"
}
}
}

177
framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions_Tests.cs

@ -0,0 +1,177 @@
using System;
using AutoMapper;
using Shouldly;
using Volo.Abp.Auditing;
using Xunit;
namespace Volo.Abp.AutoMapper
{
public class AutoMapperExpressionExtensions_Tests
{
[Fact]
public void Should_Ignore_Configured_Property()
{
var mapper = CreateMapper(
cfg => cfg
.CreateMap<SimpleClass1, SimpleClass2>()
.Ignore(x => x.Value2)
.Ignore(x => x.Value3)
);
var obj2 = mapper.Map<SimpleClass2>(
new SimpleClass1
{
Value1 = "v1",
Value2 = "v2"
}
);
obj2.Value1.ShouldBe("v1");
obj2.Value2.ShouldBeNull();
obj2.Value3.ShouldBeNull();
}
[Fact]
public void Should_Ignore_Audit_Properties()
{
var mapper = CreateMapper(
cfg => cfg
.CreateMap<SimpleClassAudited1, SimpleClassAudited2>()
.IgnoreFullAuditedObjectProperties()
);
var obj2 = mapper.Map<SimpleClassAudited2>(
new SimpleClassAudited1
{
CreationTime = DateTime.Now,
CreatorId = Guid.NewGuid(),
LastModificationTime = DateTime.Now,
LastModifierId = Guid.NewGuid(),
DeleterId = Guid.NewGuid(),
DeletionTime = DateTime.Now,
IsDeleted = true
}
);
obj2.CreationTime.ShouldBe(default);
obj2.CreatorId.ShouldBeNull();
obj2.LastModificationTime.ShouldBe(default);
obj2.LastModifierId.ShouldBeNull();
obj2.DeleterId.ShouldBeNull();
obj2.DeletionTime.ShouldBeNull();
obj2.IsDeleted.ShouldBeFalse();
}
[Fact]
public void Should_Ignore_Audit_Properties_With_User()
{
var mapper = CreateMapper(
cfg => cfg
.CreateMap<SimpleClassAuditedWithUser1, SimpleClassAuditedWithUser2>()
.IgnoreFullAuditedObjectProperties<SimpleClassAuditedWithUser1, SimpleClassAuditedWithUser2, SimpleUser>()
);
var obj2 = mapper.Map<SimpleClassAuditedWithUser2>(
new SimpleClassAuditedWithUser1
{
CreationTime = DateTime.Now,
CreatorId = Guid.NewGuid(),
LastModificationTime = DateTime.Now,
LastModifierId = Guid.NewGuid(),
DeleterId = Guid.NewGuid(),
DeletionTime = DateTime.Now,
IsDeleted = true,
Creator = new SimpleUser(),
Deleter = new SimpleUser(),
LastModifier = new SimpleUser()
}
);
obj2.CreationTime.ShouldBe(default);
obj2.CreatorId.ShouldBeNull();
obj2.LastModificationTime.ShouldBe(default);
obj2.LastModifierId.ShouldBeNull();
obj2.DeleterId.ShouldBeNull();
obj2.DeletionTime.ShouldBeNull();
obj2.IsDeleted.ShouldBeFalse();
obj2.Creator.ShouldBeNull();
obj2.Deleter.ShouldBeNull();
obj2.LastModifier.ShouldBeNull();
}
private static IMapper CreateMapper(Action<IMapperConfigurationExpression> configure)
{
var configuration = new MapperConfiguration(configure);
configuration.AssertConfigurationIsValid();
return configuration.CreateMapper();
}
public class SimpleClass1
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class SimpleClass2
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
public class SimpleClassAudited1 : IFullAuditedObject
{
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? LastModifierId { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? DeleterId { get; set; }
}
public class SimpleClassAudited2 : IFullAuditedObject
{
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? LastModifierId { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? DeleterId { get; set; }
}
public class SimpleClassAuditedWithUser1 : IFullAuditedObject<SimpleUser>
{
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? LastModifierId { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? DeleterId { get; set; }
public SimpleUser Creator { get; set; }
public SimpleUser LastModifier { get; set; }
public SimpleUser Deleter { get; set; }
}
public class SimpleClassAuditedWithUser2 : IFullAuditedObject<SimpleUser>
{
public DateTime CreationTime { get; set; }
public Guid? CreatorId { get; set; }
public DateTime? LastModificationTime { get; set; }
public Guid? LastModifierId { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletionTime { get; set; }
public Guid? DeleterId { get; set; }
public SimpleUser Creator { get; set; }
public SimpleUser LastModifier { get; set; }
public SimpleUser Deleter { get; set; }
}
public class SimpleUser
{
}
}
}

34
framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs

@ -1,5 +1,6 @@
using System.Linq;
using Shouldly;
using Volo.Abp;
using Xunit;
namespace System.Collections.Generic
@ -171,5 +172,36 @@ namespace System.Collections.Generic
list[1].ShouldBe(42);
list[2].ShouldBe(3);
}
[Fact]
public void SortByDependencies()
{
var dependencies = new Dictionary<char, char[]>
{
{'A', new[] {'B', 'G'}},
{'B', new[] {'C', 'E'}},
{'C', new[] {'D'}},
{'D', new char[0]},
{'E', new[] {'C', 'F'}},
{'F', new[] {'C'}},
{'G', new[] {'F'}}
};
for (int i = 0; i < 3; i++)
{
var list = RandomHelper
.GenerateRandomizedList(new char[] {'A', 'B', 'C', 'D', 'E', 'F', 'G'});
list = list.SortByDependencies(c => dependencies[c]);
foreach (var dependency in dependencies)
{
foreach (var dependedValue in dependency.Value)
{
list.IndexOf(dependency.Key).ShouldBeGreaterThan(list.IndexOf(dependedValue));
}
}
}
}
}
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json

@ -0,0 +1,6 @@
{
"culture": "cs",
"texts": {
"hello": "Ahoj"
}
}

2
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json

@ -1,5 +1,5 @@
{
"culture": "de",
"culture": "en",
"texts": {
"hello": "Hello"
}

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json

@ -4,4 +4,4 @@
"USA": "Spojené státy americké",
"Brazil": "Brazílie"
}
}
}

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json

@ -4,4 +4,4 @@
"ThisFieldIsRequired": "Toto pole je povinné",
"MaxLenghtErrorMessage": "Toto pole může mít nanejvýš '{0}' znaků"
}
}
}

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json

@ -8,4 +8,4 @@
"Universe": "Vesmír",
"FortyTwo": "Čtyřicet dva"
}
}
}

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json

@ -3,4 +3,4 @@
"texts": {
"SeeYou": "Měj se"
}
}
}

7
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/cs.json

@ -0,0 +1,7 @@
{
"culture": "cs",
"texts": {
"HelloText": "Ahoj {0}",
"HowAreYou": "jak se máš?"
}
}

3
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json

@ -11,6 +11,7 @@
"InvalidUserNameOrPassword": "Neplatné uživatelské jméno nebo heslo!",
"LoginIsNotAllowed": "Není vám dovoleno se přihlásit! Musíte první potvrdit email/telefonní číslo.",
"SelfRegistrationDisabledMessage": "Vlastní registrace uživatele není povolena. K vytvoření účtu kontaktujte správce.",
"LocalLoginDisabledMessage": "Místní přihlášení je pro tuto aplikaci zakázáno.",
"Login": "Přihlásit",
"Cancel": "Zrušit",
"Register": "Registrovat",
@ -41,4 +42,4 @@
"DisplayName:Abp.Account.EnableLocalLogin": "Ověření pomocí místního účtu",
"Description:Abp.Account.EnableLocalLogin": "Označuje, zda server umožní uživatelům ověřovat se pomocí místního účtu."
}
}
}

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

@ -17,10 +17,8 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.cshtml" />
<EmbeddedResource Include="Pages\**\*.css" />
<EmbeddedResource Include="Pages\**\*.js" />
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Pages\**\*.css" />
<Content Remove="Pages\**\*.js" />
<Content Remove="Properties\launchSettings.json" />

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

@ -16,8 +16,8 @@ namespace Volo.Abp.Account.Web
return Task.CompletedTask;
}
var uiResource = context.ServiceProvider.GetRequiredService<IStringLocalizer<AbpUiResource>>();
var accountResource = context.ServiceProvider.GetRequiredService<IStringLocalizer<AccountResource>>();
var uiResource = context.GetLocalizer<AbpUiResource>();
var accountResource = context.GetLocalizer<AccountResource>();
context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountResource["ManageYourProfile"], url: "/Account/Manage", icon: "fa fa-cog", order: 1000, null));
context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", uiResource["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000));
@ -25,4 +25,4 @@ namespace Volo.Abp.Account.Web
return Task.CompletedTask;
}
}
}
}

2
modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml

@ -1,4 +1,4 @@
@using Localization.Resources.AbpUi
@using Microsoft.AspNetCore.Mvc.Localization
@inject IHtmlLocalizer<AbpUiResource> L
<a href="/Account/Login">@L["Login"]</a>
<a href="~/Account/Login">@L["Login"]</a>

3
modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Localization;
@ -73,7 +74,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
protected virtual string GetAppHomeUrl()
{
return "/"; //TODO: ???
return "~/"; //TODO: ???
}
}
}

2
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

@ -59,7 +59,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
await SignInManager.SignInAsync(user, isPersistent: false);
return Redirect(ReturnUrl ?? "/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
protected virtual async Task CheckSelfRegistrationAsync()

6
modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj

@ -17,22 +17,18 @@
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Modules\**\*.cshtml" />
<EmbeddedResource Include="Modules\**\*.js" />
<EmbeddedResource Include="Modules\**\*.css" />
<EmbeddedResource Include="Pages\**\*.cshtml" />
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Pages\**\*.css" />
<Content Remove="Modules\**\*.cshtml" />
<Content Remove="Modules\**\*.js" />
<Content Remove="Modules\**\*.css" />
<Content Remove="Pages\**\*.cshtml" />
<Content Remove="Pages\**\*.js" />
<Content Remove="Pages\**\*.css" />
<Content Remove="Properties\launchSettings.json" />
<None Include="Properties\launchSettings.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\identity\src\Volo.Abp.Identity.AspNetCore\Volo.Abp.Identity.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />

9
modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln

@ -23,6 +23,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Databa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.Domain.Tests", "test\Volo.Abp.BlobStoring.Database.Domain.Tests\Volo.Abp.BlobStoring.Database.Domain.Tests.csproj", "{E60895E5-79C4-447D-88B7-85CB5BA336A4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{9E416AD3-1EC8-439A-9CFD-AAFFA89E2AEB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlobStoring.Database.Host.ConsoleApp.ConsoleApp", "host\BlobStoring.Database.Host.ConsoleApp\src\BlobStoring.Database.Host.ConsoleApp.ConsoleApp\BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj", "{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -61,6 +65,10 @@ Global
{E60895E5-79C4-447D-88B7-85CB5BA336A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.Build.0 = Release|Any CPU
{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -74,6 +82,7 @@ Global
{527F645C-C1FC-406E-8479-81386C8ECF13} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
{D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
{E60895E5-79C4-447D-88B7-85CB5BA336A4} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}
{C3C4CB94-4F73-4D04-9076-8D98D5C6B475} = {9E416AD3-1EC8-439A-9CFD-AAFFA89E2AEB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}

21
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.sln

@ -0,0 +1,21 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlobStoring.Database.Host.ConsoleApp.ConsoleApp", "src\BlobStoring.Database.Host.ConsoleApp.ConsoleApp\BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj", "{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8067AED-2B6E-4134-AAF8-9101457D709A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2} = {E8067AED-2B6E-4134-AAF8-9101457D709A}
EndGlobalSection
EndGlobal

23
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobService.cs

@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp
{
public class BlobService : IBlobService, ITransientDependency
{
private readonly IBlobContainer<ProfilePictureContainer> _container;
public BlobService(IBlobContainer<ProfilePictureContainer> container)
{
_container = container;
}
public async Task SaveFile(string fileName = "File Name", string fileContent = "File Content")
{
await _container.SaveAsync(fileName, fileContent.GetBytes(), true);
Console.WriteLine($"File: {fileName} is successfully saved");
}
}
}

32
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="appsettings.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.SqlServer\Volo.Abp.EntityFrameworkCore.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\src\Volo.Abp.BlobStoring.Database.EntityFrameworkCore\Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.2" />
</ItemGroup>
</Project>

32
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppHostedService.cs

@ -0,0 +1,32 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp
{
public class ConsoleAppConsoleAppHostedService : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = AbpApplicationFactory.Create<ConsoleAppConsoleAppModule>(options =>
{
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
}))
{
application.Initialize();
var blobService = application.ServiceProvider.GetService<BlobService>();
await blobService.SaveFile("Test File 2", "Test Content 2");
application.Shutdown();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}

56
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppModule.cs

@ -0,0 +1,56 @@
using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Database;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Modularity;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(BlobStoringDatabaseEntityFrameworkCoreModule)
)]
public class ConsoleAppConsoleAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
ConfigureEntityFramework(context);
context.Services.AddSingleton<IBlobProvider, DatabaseBlobProvider>();
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.ProviderType = typeof(DatabaseBlobProvider);
});
});
}
private void ConfigureEntityFramework(ServiceConfigurationContext context)
{
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "Server=localhost;Database=BlobStoring_Host;Trusted_Connection=True;MultipleActiveResultSets=true";
});
context.Services.AddAbpDbContext<BlobStoringHostDbContext>(options =>
{
options.AddDefaultRepositories(true);
});
Configure<AbpDbContextOptions>(x =>
{
x.UseSqlServer();
});
}
}
}

20
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContext.cs

@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore
{
public class BlobStoringHostDbContext : AbpDbContext<BlobStoringHostDbContext>
{
public BlobStoringHostDbContext(DbContextOptions<BlobStoringHostDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigureBlobStoring();
}
}
}

29
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContextFactory.cs

@ -0,0 +1,29 @@
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore
{
public class BlobStoringHostDbContextFactory : IDesignTimeDbContextFactory<BlobStoringHostDbContext>
{
public BlobStoringHostDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<BlobStoringHostDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
return new BlobStoringHostDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}

26
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbSchemaMigrator.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore
{
public class BlobStoringHostDbSchemaMigrator : IBlobStoringHostDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public BlobStoringHostDbSchemaMigrator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
await _serviceProvider
.GetRequiredService<BlobStoringHostDbContext>()
.Database
.MigrateAsync();
}
}
}

9
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/IBlobStoringHostDbSchemaMigrator.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore
{
public interface IBlobStoringHostDbSchemaMigrator
{
Task MigrateAsync();
}
}

9
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/IBlobService.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp
{
public interface IBlobService
{
Task SaveFile(string fileName = "File Name", string fileContent = "File Content");
}
}

97
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs

@ -0,0 +1,97 @@
// <auto-generated />
using System;
using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
{
[DbContext(typeof(BlobStoringHostDbContext))]
[Migration("20200531134550_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("ContainerId")
.HasColumnType("uniqueidentifier");
b.Property<byte[]>("Content")
.HasColumnType("varbinary(max)")
.HasMaxLength(2147483647);
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("TenantId", "ContainerId", "Name");
b.ToTable("AbpBlobs");
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(128)")
.HasMaxLength(128);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("AbpBlobContainers");
});
#pragma warning restore 612, 618
}
}
}

62
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs

@ -0,0 +1,62 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpBlobContainers",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
TenantId = table.Column<Guid>(nullable: true),
Name = table.Column<string>(maxLength: 128, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobContainers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpBlobs",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
ExtraProperties = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
ContainerId = table.Column<Guid>(nullable: false),
TenantId = table.Column<Guid>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: false),
Content = table.Column<byte[]>(maxLength: 2147483647, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobs", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_AbpBlobContainers_TenantId_Name",
table: "AbpBlobContainers",
columns: new[] { "TenantId", "Name" });
migrationBuilder.CreateIndex(
name: "IX_AbpBlobs_TenantId_ContainerId_Name",
table: "AbpBlobs",
columns: new[] { "TenantId", "ContainerId", "Name" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpBlobContainers");
migrationBuilder.DropTable(
name: "AbpBlobs");
}
}
}

108
modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs

@ -0,0 +1,108 @@
// <auto-generated />
using System;
using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations
{
[DbContext(typeof(BlobStoringHostDbContext))]
[Migration("20200531143839_FK_Added")]
partial class FK_Added
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "3.1.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("ContainerId")
.HasColumnType("uniqueidentifier");
b.Property<byte[]>("Content")
.HasColumnType("varbinary(max)")
.HasMaxLength(2147483647);
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(256)")
.HasMaxLength(256);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("TenantId", "ContainerId", "Name");
b.ToTable("AbpBlobs");
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(128)")
.HasMaxLength(128);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("AbpBlobContainers");
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null)
.WithMany()
.HasForeignKey("ContainerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

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

Loading…
Cancel
Save