pull/1822/head
Yunus Emre Kalkan 6 years ago
commit 0adc1c5530

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

@ -0,0 +1,3 @@
# abp.auth JavaScript API
TODO

@ -0,0 +1,24 @@
# JavaScript API
ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform some common application requirements in the client side.
## APIs
* abp.ajax
* [abp.auth](Auth.md)
* abp.currentUser
* abp.dom
* abp.event
* abp.features
* abp.localization
* abp.log
* abp.ModalManager
* abp.notify
* abp.security
* abp.setting
* abp.ui
* abp.utils
* abp.ResourceLoader
* abp.WidgetManager
* Other APIs

@ -1,3 +1,369 @@
## Authorization
# Authorization
TODO
Authorization is used to check if a user is allowed to perform some specific operations in the application.
ABP extends [ASP.NET Core Authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction) by adding **permissions** as auto [policies](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) and allowing authorization system to be usable in the **[application services](Application-Services.md)** too.
So, all the ASP.NET Core authorization features and the documentation are valid in an ABP based application. This document focuses on the features that added on top of ASP.NET Core authorization features.
## Authorize Attribute
ASP.NET Core defines the [**Authorize**](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/simple) attribute that can be used for an action, a controller or a page. ABP allows you to use the same attribute for an [application service](Application-Services.md) too.
Example:
````csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
[Authorize]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
[AllowAnonymous]
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("BookStore_Author_Create")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
}
}
````
* `Authorize` attribute forces the user to login into the application in order to use the `AuthorAppService` methods. So, `GetListAsync` method is only available to the authenticated users.
* `AllowAnonymous` suppresses the authentication. So, `GetAsync` method is available to everyone including unauthorized users.
* `[Authorize("BookStore_Author_Create")]` defines a policy (see [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies)) that is checked to authorize the current user.
"BookStore_Author_Create" is an arbitrary policy name. If you declare an attribute like that, ASP.NET Core authorization system expects a policy to be defined before.
You can, of course, implement your policies as stated in the ASP.NET Core documentation. But for simple true/false conditions like a policy was granted to a user or not, ABP defines the permission system which will be explained in the next section.
## Permission System
A permission is a simple policy that is granted or prohibited for a particular user, role or client.
### Defining Permissions
To define permissions, create a class inheriting from the `PermissionDefinitionProvider` as shown below:
````csharp
using Volo.Abp.Authorization.Permissions;
namespace Acme.BookStore.Permissions
{
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup("BookStore");
myGroup.AddPermission("BookStore_Author_Create");
}
}
}
````
> ABP automatically discovers this class. No additional configuration required!
In the `Define` method, you first need to add a **permission group** or get an existing group then add **permissions** to this group.
When you define a permission, it becomes usable in the ASP.NET Core authorization system as a **policy** name. It also becomes visible in the UI. See permissions dialog for a role:
![authorization-new-permission-ui](images/authorization-new-permission-ui.png)
* The "BookStore" group is shown as a new tab on the left side.
* "BookStore_Author_Create" on the right side is the permission name. You can grant or prohibit it for the role.
When you save the dialog, it is saved to the database and used in the authorization system.
> The screen above is available when you have installed the identity module, which is basically used for user and role management. Startup templates come with the identity module pre-installed.
#### Localizing the Permission Name
"BookStore_Author_Create" is not a good permission name for the UI. Fortunately, `AddPermission` and `AddGroup` methods can take `LocalizableString` as second parameters:
````csharp
var myGroup = context.AddGroup(
"BookStore",
LocalizableString.Create<BookStoreResource>("BookStore")
);
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create")
);
````
Then you can define texts for "BookStore" and "Permission:BookStore_Author_Create" keys in the localization file:
````json
"BookStore": "Book Store",
"Permission:BookStore_Author_Create": "Creating a new author"
````
> For more information, see the [localization document](Localization.md) on the localization system.
The localized UI will be as seen below:
![authorization-new-permission-ui-localized](images/authorization-new-permission-ui-localized.png)
#### Multi-Tenancy
ABP supports [multi-tenancy](Multi-Tenancy.md) as a first class citizen. You can define multi-tenancy side option while defining a new permission. It gets one of the three values defined below:
* **Host**: The permission is available only for the host side.
* **Tenant**: The permission is available only for the tenant side.
* **Both** (default): The permission is available both for tenant and host sides.
> If your application is not multi-tenant, you can ignore this option.
To set the multi-tenancy side option, pass to the third parameter of the `AddPermission` method:
````csharp
myGroup.AddPermission(
"BookStore_Author_Create",
LocalizableString.Create<BookStoreResource>("Permission:BookStore_Author_Create"),
multiTenancySide: MultiTenancySides.Tenant //set multi-tenancy side!
);
````
#### Child Permissions
A permission may have child permissions. It is especially useful when you want to create a hierarchical permission tree where a permission may have additional sub permissions which are available only if the parent permission has been granted.
Example definition:
````csharp
var authorManagement = myGroup.AddPermission("Author_Management");
authorManagement.AddChild("Author_Management_Create_Books");
authorManagement.AddChild("Author_Management_Edit_Books");
authorManagement.AddChild("Author_Management_Delete_Books");
````
The result on the UI is shown below (you probably want to localize permissions for your application):
![authorization-new-permission-ui-hierarcy](images/authorization-new-permission-ui-hierarcy.png)
For the example code, it is assumed that a role/user with "Author_Management" permission granted may have additional permissions. Then a typical application service that checks permissions can be defined as shown below:
````csharp
[Authorize("Author_Management")]
public class AuthorAppService : ApplicationService, IAuthorAppService
{
public Task<List<AuthorDto>> GetListAsync()
{
...
}
public Task<AuthorDto> GetAsync(Guid id)
{
...
}
[Authorize("Author_Management_Create_Books")]
public Task CreateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Edit_Books")]
public Task UpdateAsync(CreateAuthorDto input)
{
...
}
[Authorize("Author_Management_Delete_Books")]
public Task DeleteAsync(CreateAuthorDto input)
{
...
}
}
````
* `GetListAsync` and `GetAsync` will be available to users if they have `Author_Management` permission is granted.
* Other methods require additional permissions.
### Overriding a Permission by a Custom Policy
If you define and register a policy to the ASP.NET Core authorization system with the same name of a permission, your policy will override the existing permission. This is a powerful way to extend the authorization for a pre-built module that you are using in your application.
See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy.
## IAuthorizationService
ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization.
Example:
````csharp
public async Task CreateAsync(CreateAuthorDto input)
{
var result = await AuthorizationService
.AuthorizeAsync("Author_Management_Create_Books");
if (result.Succeeded == false)
{
//throw exception
throw new AbpAuthorizationException("...");
}
//continue to the normal flow...
}
````
> `AuthorizationService` is available as a property when you derive from ABP's `ApplicationService` base class. Since it is widely used in application services, `ApplicationService` pre-injects it for you. Otherwise, you can directly [inject](Dependency-Injection.md) it into your class.
Since this is a typical code block, ABP provides extension methods to simplify it.
Example:
````csharp
public async Task CreateAsync(CreateAuthorDto input)
{
await AuthorizationService.CheckAsync("Author_Management_Create_Books");
//continue to the normal flow...
}
````
`CheckAsync` extension method throws `AbpAuthorizationException` if the current user/client is not granted for the given permission. There is also `IsGrantedAsync` extension method that returns `true` or `false`.
`IAuthorizationService` has some overloads for the `AuthorizeAsync` method. These are explained in the [ASP.NET Core authorization documentation](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction).
> Tip: Prefer to use the `Authorize` attribute wherever possible, since it is declarative & simple. Use `IAuthorizationService` if you need to conditionally check a permission and run a business code based on the permission check.
### Check a Permission in JavaScript
You may need to check a policy/permission on the client side. For ASP.NET Core MVC / Razor Pages applications, you can use the `abp.auth` API. Example:
````js
abp.auth.isGranted('MyPermissionName');
````
See [abp.auth](AspNetCore/JavaScript-API/Auth.md) API documentation for details.
## Permission Management
Permission management is normally done by an admin user using the permission management modal:
![authorization-new-permission-ui-localized](images/authorization-new-permission-ui-localized.png)
If you need to manage permissions by code, inject the `IPermissionManager` and use as shown below:
````csharp
public class MyService : ITransientDependency
{
private readonly IPermissionManager _permissionManager;
public MyService(IPermissionManager permissionManager)
{
_permissionManager = permissionManager;
}
public async Task GrantPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, true);
}
public async Task ProhibitPermissionForUserAsync(Guid userId, string permissionName)
{
await _permissionManager.SetForUserAsync(userId, permissionName, false);
}
}
````
`SetForUserAsync` sets the value (true/false) for a permission of a user. There are more extension methods like `SetForRoleAsync` and `SetForClientAsync`.
`IPermissionManager` is defined by the permission management module. See the [permission management module documentation](Modules/Permission-Management.md) for more information.
## Advanced Topics
### Permission Value Providers
Permission checking system is extensible. Any class derived from `PermissionValueProvider` (or implements `IPermissionValueProvider`) can contribute to the permission check. There are three pre-defined value providers:
* `UserPermissionValueProvider` checks if the current user is granted for the given permission. It gets user id from the current claims. User claim name is defined with the `AbpClaimTypes.UserId` static property.
* `RolePermissionValueProvider` checks if any of the roles of the current user is granted for the given permission. It gets role names from the current claims. Role claims name is defined with the `AbpClaimTypes.Role` static property.
* `ClientPermissionValueProvider` checks if the current client is granted for the given permission. This is especially useful on a machine to machine interaction where there is no current user. It gets the client id from the current claims. Client claim name is defined with the `AbpClaimTypes.ClientId` static property.
You can extend the permission checking system by defining your own permission value provider.
Example:
````csharp
public class SystemAdminPermissionValueProvider : PermissionValueProvider
{
public SystemAdminPermissionValueProvider(IPermissionStore permissionStore)
: base(permissionStore)
{
}
public override string Name => "SystemAdmin";
public override async Task<PermissionGrantResult>
CheckAsync(PermissionValueCheckContext context)
{
if (context.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
{
return PermissionGrantResult.Granted;
}
return PermissionGrantResult.Undefined;
}
}
````
This provider allows for all permissions to a user with a `User_Type` claim that has `SystemAdmin` value. It is common to use current claims and `IPermissionStore` in a permission value provider.
A permission value provider should return one of the following values from the `CheckAsync` method:
* `PermissionGrantResult.Granted` is returned to grant the user for the permission. If any of the providers return `Granted`, the result will be `Granted`, if no other provider returns `Prohibited`.
* `PermissionGrantResult.Prohibited` is returned to prohibit the user for the permission. If any of the providers return `Prohibited`, the result will always be `Prohibited`. Doesn't matter what other providers return.
* `PermissionGrantResult.Undefined` is returned if this value provider could not decide about the permission value. Return this to let other providers check the permission.
Once a provider is defined, it should be added to the `PermissionOptions` as shown below:
````csharp
Configure<PermissionOptions>(options =>
{
options.ValueProviders.Add<SystemAdminPermissionValueProvider>();
});
````
### Permission Store
`IPermissionStore` is the only interface that needs to be implemented to read the value of permissions from a persistence source, generally a database system. Permission management module implements it. See the [permission management module documentation](Modules/Permission-Management.md) for more information
### AlwaysAllowAuthorizationService
`AlwaysAllowAuthorizationService` is a class that is used to bypass the authorization service. It is generally used in integration tests where you may want to disable the authorization system.
Use `IServiceCollection.AddAlwaysAllowAuthorization()` extension method to register the `AlwaysAllowAuthorizationService` to the [dependency injection](Dependency-Injection.md) system:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAlwaysAllowAuthorization();
}
````
This is already done for the startup template integration tests.
## See Also
* [Permission Management Module](Modules/Permission-Management.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](AspNetCore/JavaScript-API/Auth.md)

@ -0,0 +1,21 @@
# ABP v0.21 Has Been Released based on the ASP.NET Core 3.0
Just one hour after Microsoft released it, ABP v0.21 [has been released](https://twitter.com/abpframework/status/1176185493119258624) based on the ASP.NET Core 3.0.
v0.21 has no new feature. It just upgrades to the stable ASP.NET Core 3.0. Check [v0.20 release notes](https://github.com/abpframework/abp/releases/tag/0.20.0) for new features, enhancements and bug fixes.
## About v1.0
ABP framework is getting closer to v1.0. We intent to release it in the middle of this October. In this time, we will test and document more.
## .NET Conf 2019
Microsoft has lunched ASP.NET Core 3.0 in the .NET Conf 2019, a 3-days virtual conference. ABP's lead developer [Halil ibrahim Kalkan](https://twitter.com/hibrahimkalkan) has also talked in the conference to introduce the ABP framework. It was great to be a part of this important event.
## Techorama Netherlands 2019
[Techorama NL](https://techorama.nl/) is one of the biggest conferences in Europe. This year, Volosoft is a sponsor of the conference and will have a booth to talk to software developers about the ABP framework and software development. Our booth wall will look like shown below:
![volosoft-booth](volosoft-booth.png)
If you are in the conference, come to out booth to talk about the ABP framework. We will also have nice swags for you :)

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

@ -0,0 +1,3 @@
# Permission Management Module
TODO

@ -82,7 +82,8 @@
"text": "Validation"
},
{
"text": "Authorization"
"text": "Authorization",
"path": "Authorization.md"
},
{
"text": "Caching"

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

@ -0,0 +1,21 @@
# 基于ASP.NET Core 3.0的ABP v0.21已发布
在微软发布仅仅一个小时后, 基于ASP.NET Core 3.0的ABP v0.21也紧跟着[发布了.](https://twitter.com/abpframework/status/1176185493119258624)
v0.21没有新功能.它只是升级到稳定的ASP.NET Core 3.0. 查看[v0.20发行说明](https://github.com/abpframework/abp/releases/tag/0.20.0)以获取新功能,增强功能和错误修复.
## 关于v1.0
ABP框架越来越接近v1.0.我们打算在今年10月中旬发布1.0. 现在,我们将完善测试和文档.
## .NET Conf 2019
微软已经在为期3天的虚拟会议.NET Conf 2019发布了ASP.NET Core 3.0. ABP的首席开发人员[Halil ibrahim Kalkan](https://twitter.com/hibrahimkalkan)在会议上也发表了讲话,介绍了ABP框架.能够参加这一重要活动真是太棒了.
## Techorama荷兰2019
[Techorama NL](https://techorama.nl/)是欧洲最大的会议之一.今年,Volosoft是会议的赞助商,并将有一个展位与软件开发人员讨论ABP框架和软件开发.我们的展位墙如下图所示:
![volosoft-booth](volosoft-booth.png)
如果您也参加会议,请到展位讨论ABP框架.我们还为您准备了一些私货:)

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

@ -248,6 +248,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit", "src\Vol
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.MailKit.Tests", "test\Volo.Abp.MailKit.Tests\Volo.Abp.MailKit.Tests.csproj", "{70DD6E17-B98B-4B00-8F38-C489E291BB53}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectMapping.Tests", "test\Volo.Abp.ObjectMapping.Tests\Volo.Abp.ObjectMapping.Tests.csproj", "{667F5544-C1EB-447C-96FD-9B757F04DE2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ddd.Application.Contracts", "src\Volo.Abp.Ddd.Application.Contracts\Volo.Abp.Ddd.Application.Contracts.csproj", "{73559227-EBF0-475F-835B-1FF0CD9132AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -738,6 +742,14 @@ Global
{70DD6E17-B98B-4B00-8F38-C489E291BB53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70DD6E17-B98B-4B00-8F38-C489E291BB53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70DD6E17-B98B-4B00-8F38-C489E291BB53}.Release|Any CPU.Build.0 = Release|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{667F5544-C1EB-447C-96FD-9B757F04DE2B}.Release|Any CPU.Build.0 = Release|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73559227-EBF0-475F-835B-1FF0CD9132AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -864,6 +876,8 @@ Global
{E026A085-D881-4AE0-9F08-422AC3903BD7} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{0CAED4CC-1CFD-4092-A326-AFE4DB3A9AB4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{70DD6E17-B98B-4B00-8F38-C489E291BB53} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{667F5544-C1EB-447C-96FD-9B757F04DE2B} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{73559227-EBF0-475F-835B-1FF0CD9132AA} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -130,7 +130,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
if (!selectItems.Any(si => si.Selected))
{
var itemToBeSelected = selectItems.FirstOrDefault(si => si.Value.ToString() == selectedValue);
var itemToBeSelected = selectItems.FirstOrDefault(si => si.Value == selectedValue);
if (itemToBeSelected != null)
{

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Rendering;
@ -23,16 +23,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
while (properties.Count == 0)
{
explorer = explorer.Container;
properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName.Equals(ItemsListPropertyName)).ToList();
if (explorer.Container == null)
{
return null;
}
properties = explorer.Container.Properties.Where(p => p.Metadata.PropertyName.Equals(ItemsListPropertyName)).ToList();
}
var selectItems = (properties.First().Model as IEnumerable<SelectListItem>).ToList();
return selectItems;
}
}

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.MultiTenancy;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization;
@ -25,6 +26,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy
typeof(AbpAspNetCoreMvcUiMultiTenancyModule).Assembly
);
});
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiMultiTenancyModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Toolbars;
@ -17,6 +18,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
)]
public class AbpAspNetCoreMvcUiBasicThemeModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiBasicThemeModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<ThemingOptions>(options =>

@ -1,20 +1,21 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Brand
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Menu
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" id="main-navbar">
@(await Component.InvokeAsync<MainNavbarBrandViewComponent>())
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#main-navbar-collapse" aria-controls="main-navbar-collapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="main-navbar-collapse">
<ul class="navbar-nav mr-auto">
@(await Component.InvokeAsync<MainNavbarMenuViewComponent>())
</ul>
<span id="main-navbar-tools">
@(await Component.InvokeAsync<MainNavbarToolbarViewComponent>())
</span>
<nav class="navbar navbar-expand-md navbar-dark bg-dark shadow-sm flex-column flex-md-row mb-4" id="main-navbar" style="min-height: 4rem;">
<div class="container">
@(await Component.InvokeAsync<MainNavbarBrandViewComponent>())
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#main-navbar-collapse" aria-controls="main-navbar-collapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="main-navbar-collapse">
<ul class="navbar-nav mx-auto">
@(await Component.InvokeAsync<MainNavbarMenuViewComponent>())
</ul>
<ul class="navbar-nav">
@(await Component.InvokeAsync<MainNavbarToolbarViewComponent>())
</ul>
</div>
</div>
</nav>
</nav>

@ -37,7 +37,7 @@
}
@menuItem.DisplayName
</a>
<div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)">
<div class="dropdown-menu border-0 shadow-sm" aria-labelledby="Menu_@(menuItem.Name)">
@foreach (var childMenuItem in menuItem.Items)
{
@await Html.PartialAsync("~/Themes/Basic/Components/Menu/_MenuItem.cshtml", childMenuItem)

@ -33,7 +33,7 @@ else
@Model.DisplayName
</span>
</a>
<div class="dropdown-menu">
<div class="dropdown-menu border-0 shadow-sm">
@foreach (var childMenuItem in Model.Items)
{
@await Html.PartialAsync("~/Themes/Basic/Components/Menu/_MenuItem.cshtml", childMenuItem)

@ -2,5 +2,5 @@
@model Toolbar
@foreach (var toolbarItem in Model.Items.OrderBy(i => i.Order))
{
<span>@(await Component.InvokeAsync(toolbarItem.ComponentType))</span>
<li class="nav-item">@(await Component.InvokeAsync(toolbarItem.ComponentType))</li>
}

@ -1,11 +1,11 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.Toolbar.LanguageSwitch
@model LanguageSwitchViewComponentModel
<div class="dropdown d-inline">
<a class="btn btn-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Model.CurrentLanguage.DisplayName
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<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)">@language.DisplayName</a>

@ -7,14 +7,14 @@
@inject ICurrentTenant CurrentTenant
@inject IHtmlLocalizer<AbpUiResource> L
@model ApplicationMenu
<div class="dropdown d-inline">
<a class="btn btn-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@CurrentUser.UserName
</a>
@if (Model.Items.Any())
{
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
<div class="dropdown-menu dropdown-menu-right border-0 shadow-sm" aria-labelledby="dropdownMenuLink">
@foreach (var menuItem in Model.Items)
{
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";

@ -45,32 +45,42 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Account)
</head>
<body class="abp-account-layout">
<body class="abp-account-layout bg-light">
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Account)
@(await Component.InvokeAsync<MainNavbarViewComponent>())
<div class="@containerClass">
<abp-row>
<abp-column size-md="_4" offset-md="_4">
@if (MultiTenancyOptions.Value.IsEnabled &&
(TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true))
{
<div class="tenant-switch-box" style="background-color: #eee; margin-bottom: 20px; color: #000; padding: 10px; text-align: center;">
<span style="color: #666;">@MultiTenancyStringLocalizer["Tenant"]: </span>
<strong>
@if (CurrentTenant.Id == null)
{
<i>@MultiTenancyStringLocalizer["NotSelected"]</i>
}
else
{
@(CurrentTenant.Name ?? CurrentTenant.Id.Value.ToString())
}
</strong>
(<a id="AbpTenantSwitchLink" style="color: #333; cursor: pointer">@MultiTenancyStringLocalizer["Switch"]</a>)
</div>
}
<abp-column size-md="_5" class="mx-auto">
<div class="card shadow-sm rounded mb-3">
<div class="card-body px-5">
<div class="row">
<div class="col">
<span style="font-size: .8em;" class="text-uppercase text-muted">@MultiTenancyStringLocalizer["Tenant"]</span><br />
<h6 class="m-0 d-inline-block">
@if (CurrentTenant.Id == null)
{
<span>
@MultiTenancyStringLocalizer["NotSelected"]
</span>
}
else
{
<strong>@(CurrentTenant.Name ?? CurrentTenant.Id.Value.ToString())</strong>
}
</h6>
</div>
<div class="col-auto">
@if (MultiTenancyOptions.Value.IsEnabled &&
(TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true))
{
<a id="AbpTenantSwitchLink" href="javascript:;" class="btn btn-sm mt-3 btn-outline-primary">@MultiTenancyStringLocalizer["Switch"]</a>
}
</div>
</div>
</div>
</div>
@(await Component.InvokeAsync<PageAlertsViewComponent>())
@RenderBody()
</abp-column>

@ -49,14 +49,14 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Application)
</head>
<body class="abp-application-layout">
<body class="abp-application-layout bg-light">
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Application)
@(await Component.InvokeAsync<MainNavbarViewComponent>())
<div class="@containerClass">
@(await Component.InvokeAsync<PageAlertsViewComponent>())
<div id="AbpContentToolbar" class="text-right mb-3">
<div id="AbpContentToolbar">
@RenderSection("content_toolbar", false)
</div>
@RenderBody()

@ -1,36 +1,59 @@
body {
padding-top: 5rem;
}
body.abp-empty-layout {
padding-top: 0;
}
#main-navbar-tools a.dropdown-toggle {
text-decoration: none;
color: #fff;
}
/* Main Menu */
.navbar .dropdown-submenu {
position: relative;
}
.navbar .dropdown-submenu a {
padding: 0.25rem 1.4rem;
.navbar .dropdown-menu {
margin: 0;
padding: 0;
}
.navbar .dropdown-menu a {
font-size: .9em;
padding: 10px 15px;
display: block;
min-width: 210px;
text-align: left;
border-radius: 0.25rem;
min-height: 44px;
}
.navbar .dropdown-submenu a::after {
transform: rotate(-90deg);
position: absolute;
right: 16px;
top: 18px;
}
.navbar .dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
}
.card-header .btn {
padding: 2px 6px;
}
.card-header h5 {
margin: 0;
}
.container > .card {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
}
.navbar .dropdown-submenu a::after {
transform: rotate(-90deg);
position: absolute;
right: 16px;
top: 18px;
}
@media screen and (min-width: 768px) {
.navbar .dropdown:hover > .dropdown-menu {
display: block;
}
.navbar .dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
margin-left: .1rem;
margin-right: .1rem;
.navbar .dropdown-submenu:hover > .dropdown-menu {
display: block;
}
}
.input-validation-error {
border-color: #dc3545;
}
.field-validation-error {
font-size: 0.8em;
}

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
@ -15,6 +16,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared
)]
public class AbpAspNetCoreMvcUiThemeSharedModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiThemeSharedModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>

@ -2,7 +2,7 @@ using System;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Http;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Error

@ -17,6 +17,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiWidgetsModule).Assembly);
});
AutoAddWidgets(context.Services);
}

@ -1,4 +1,5 @@
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation;
using Volo.Abp.VirtualFileSystem;
@ -8,6 +9,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI
[DependsOn(typeof(AbpUiNavigationModule))]
public class AbpAspNetCoreMvcUiModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAspNetCoreMvcUiModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>

@ -24,7 +24,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
{
public IServiceProvider ServiceProvider { get; set; }
protected readonly object ServiceProviderLock = new object();
protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference);
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
@ -32,7 +36,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
{
if (reference == null)
{
reference = ServiceProvider.GetRequiredService<TService>();
reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
}
}
}
@ -48,7 +52,27 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager;
public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper);
protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper
{
get
{
if (_objectMapper != null)
{
return _objectMapper;
}
if (ObjectMapperContext == null)
{
return LazyGetRequiredService(ref _objectMapper);
}
return LazyGetRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext),
ref _objectMapper
);
}
}
private IObjectMapper _objectMapper;
public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator);

@ -0,0 +1,20 @@
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpMvcBuilderExtensions
{
public static void AddApplicationPartIfNotExists(this IMvcBuilder mvcBuilder, Assembly assembly)
{
if (mvcBuilder.PartManager.ApplicationParts.Any(
p => p is AssemblyPart assemblyPart && assemblyPart.Assembly == assembly))
{
return;
}
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(assembly));
}
}
}

@ -26,6 +26,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0-preview8.19405.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
</ItemGroup>
</Project>

@ -15,6 +15,7 @@ using System.Reflection;
using Volo.Abp.ApiVersioning;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Json;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.DependencyInjection;
@ -65,7 +66,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var mvcCoreBuilder = context.Services.AddMvcCore();
context.Services.ExecutePreConfiguredActions(mvcCoreBuilder);
var abpMvcDataAnnotationsLocalizationOptions = context.Services.ExecutePreConfiguredActions(new AbpMvcDataAnnotationsLocalizationOptions());
context.Services
@ -76,10 +77,14 @@ namespace Volo.Abp.AspNetCore.Mvc
);
var mvcBuilder = context.Services.AddMvc()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver =
new AbpMvcJsonContractResolver(context.Services);
})
.AddRazorRuntimeCompilation()
.AddDataAnnotationsLocalization(options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
{
var resourceType = abpMvcDataAnnotationsLocalizationOptions.AssemblyResources.GetOrDefault(type.Assembly);
@ -87,7 +92,7 @@ namespace Volo.Abp.AspNetCore.Mvc
};
})
.AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it.
context.Services.ExecutePreConfiguredActions(mvcBuilder);
//TODO: AddViewLocalization by default..?
@ -105,16 +110,12 @@ 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));
Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(context.Services);
});
//Configure<MvcJsonOptions>(jsonOptions => @3.0.0!
//{
// jsonOptions.SerializerSettings.ContractResolver = new AbpMvcJsonContractResolver(context.Services);
//});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

@ -23,7 +23,11 @@ namespace Volo.Abp.AspNetCore.Mvc
{
public IServiceProvider ServiceProvider { get; set; }
protected readonly object ServiceProviderLock = new object();
protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference);
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
@ -31,7 +35,7 @@ namespace Volo.Abp.AspNetCore.Mvc
{
if (reference == null)
{
reference = ServiceProvider.GetRequiredService<TService>();
reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
}
}
}
@ -42,7 +46,27 @@ namespace Volo.Abp.AspNetCore.Mvc
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager;
public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper);
protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper
{
get
{
if (_objectMapper != null)
{
return _objectMapper;
}
if (ObjectMapperContext == null)
{
return LazyGetRequiredService(ref _objectMapper);
}
return LazyGetRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext),
ref _objectMapper
);
}
}
private IObjectMapper _objectMapper;
public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator);

@ -1,15 +1,55 @@
using Microsoft.AspNetCore.Mvc;
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.AspNetCore.Mvc
{
public abstract class AbpViewComponent : ViewComponent
{
public IObjectMapper ObjectMapper { get; set; }
public IServiceProvider ServiceProvider { get; set; }
protected readonly object ServiceProviderLock = new object();
protected AbpViewComponent()
protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference);
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
lock (ServiceProviderLock)
{
if (reference == null)
{
reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
}
}
}
return reference;
}
protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper
{
get
{
if (_objectMapper != null)
{
return _objectMapper;
}
if (ObjectMapperContext == null)
{
return LazyGetRequiredService(ref _objectMapper);
}
return LazyGetRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext),
ref _objectMapper
);
}
}
private IObjectMapper _objectMapper;
}
}

@ -220,11 +220,26 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
foreach (var selector in action.Selectors)
{
var httpMethod = selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault();
var httpMethod = selector.ActionConstraints
.OfType<HttpMethodActionConstraint>()
.FirstOrDefault()?
.HttpMethods?
.FirstOrDefault();
if (httpMethod == null)
{
httpMethod = SelectHttpMethod(action, configuration);
}
if (selector.AttributeRouteModel == null)
{
selector.AttributeRouteModel = CreateAbpServiceAttributeRouteModel(rootPath, controllerName, action, httpMethod, configuration);
}
if (!selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Any())
{
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] {httpMethod}));
}
}
}
@ -336,7 +351,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected virtual bool IsEmptySelector(SelectorModel selector)
{
return selector.AttributeRouteModel == null && selector.ActionConstraints.IsNullOrEmpty();
return selector.AttributeRouteModel == null
&& selector.ActionConstraints.IsNullOrEmpty()
&& selector.EndpointMetadata.IsNullOrEmpty();
}
protected virtual bool ImplementsRemoteServiceInterface(Type controllerType)

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.Json;

@ -4,7 +4,7 @@ using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Tracing;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;

@ -1,63 +0,0 @@
using System;
namespace Microsoft.AspNetCore.InProcess
{
/// <summary>
/// https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/host-and-deploy/aspnet-core-module/samples_snapshot/2.x/CurrentDirectoryHelpers.cs
/// TODO: Remove CurrentDirectoryHelpers.cs when upgrade to ASP.NET Core 3.0.
/// </summary>
public class CurrentDirectoryHelpers
{
internal const string AspNetCoreModuleDll = "aspnetcorev2_inprocess.dll";
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[System.Runtime.InteropServices.DllImport(AspNetCoreModuleDll)]
private static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
private struct IISConfigurationData
{
public IntPtr pNativeApplication;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
public string pwzFullApplicationPath;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
public string pwzVirtualApplicationPath;
public bool fWindowsAuthEnabled;
public bool fBasicAuthEnabled;
public bool fAnonymousAuthEnable;
}
public static void SetCurrentDirectory()
{
try
{
// Check if physical path was provided by ANCM
var sitePhysicalPath = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH");
if (string.IsNullOrEmpty(sitePhysicalPath))
{
// Skip if not running ANCM InProcess
if (GetModuleHandle(AspNetCoreModuleDll) == IntPtr.Zero)
{
return;
}
IISConfigurationData configurationData = default(IISConfigurationData);
if (http_get_application_properties(ref configurationData) != 0)
{
return;
}
sitePhysicalPath = configurationData.pwzFullApplicationPath;
}
Environment.CurrentDirectory = sitePhysicalPath;
}
catch
{
// ignore
}
}
}
}

@ -4,12 +4,13 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class AbpExceptionHandlingMiddleware : IMiddleware, ITransientDependency
{

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -15,7 +15,7 @@ using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Validation;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConverter, ITransientDependency
{
@ -42,9 +42,9 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
var errorInfo = CreateErrorInfoWithoutCode(exception);
if (exception is IHasErrorCode)
if (exception is IHasErrorCode hasErrorCodeException)
{
errorInfo.Code = (exception as IHasErrorCode).Code;
errorInfo.Code = hasErrorCodeException.Code;
}
return errorInfo;

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
@ -9,7 +8,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Validation;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultHttpExceptionStatusCodeFinder : IHttpExceptionStatusCodeFinder, ITransientDependency
{

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Net;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class ExceptionHttpStatusCodeOptions
{

@ -1,7 +1,7 @@
using System;
using Volo.Abp.Http;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
/// <summary>
/// This interface can be implemented to convert an <see cref="Exception"/> object to an <see cref="RemoteServiceErrorInfo"/> object.

@ -2,7 +2,7 @@
using System.Net;
using Microsoft.AspNetCore.Http;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public interface IHttpExceptionStatusCodeFinder
{

@ -1,5 +1,4 @@
using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Authorization;
using Volo.Abp.Authorization.Permissions;
@ -8,13 +7,6 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpAuthorizationServiceCollectionExtensions
{
//TODO: Remove this and use AddAlwaysAllowAuthorization
[Obsolete("Use AddAlwaysAllowAuthorization instead")]
public static IServiceCollection AddAlwaysAllowPermissionChecker(this IServiceCollection services)
{
return services.Replace(ServiceDescriptor.Singleton<IPermissionChecker, AlwaysAllowPermissionChecker>());
}
public static IServiceCollection AddAlwaysAllowAuthorization(this IServiceCollection services)
{
services.Replace(ServiceDescriptor.Singleton<IAuthorizationService, AlwaysAllowAuthorizationService>());

@ -0,0 +1,23 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.AutoMapper;
using Volo.Abp.ObjectMapping;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpAutoMapperServiceCollectionExtensions
{
public static IServiceCollection AddAutoMapperObjectMapper(this IServiceCollection services)
{
return services.Replace(
ServiceDescriptor.Transient<IAutoObjectMappingProvider, AutoMapperAutoObjectMappingProvider>()
);
}
public static IServiceCollection AddAutoMapperObjectMapper<TContext>(this IServiceCollection services)
{
return services.Replace(
ServiceDescriptor.Transient<IAutoObjectMappingProvider<TContext>, AutoMapperAutoObjectMappingProvider<TContext>>()
);
}
}
}

@ -12,6 +12,8 @@ namespace Volo.Abp.AutoMapper
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper();
var mapperAccessor = new MapperAccessor();
context.Services.AddSingleton<IMapperAccessor>(_ => mapperAccessor);
context.Services.AddSingleton<MapperAccessor>(_ => mapperAccessor);

@ -0,0 +1,32 @@
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.AutoMapper
{
public class AutoMapperAutoObjectMappingProvider<TContext> : AutoMapperAutoObjectMappingProvider, IAutoObjectMappingProvider<TContext>
{
public AutoMapperAutoObjectMappingProvider(IMapperAccessor mapperAccessor)
: base(mapperAccessor)
{
}
}
public class AutoMapperAutoObjectMappingProvider : IAutoObjectMappingProvider
{
public IMapperAccessor MapperAccessor { get; }
public AutoMapperAutoObjectMappingProvider(IMapperAccessor mapperAccessor)
{
MapperAccessor = mapperAccessor;
}
public virtual TDestination Map<TSource, TDestination>(object source)
{
return MapperAccessor.Mapper.Map<TDestination>(source);
}
public virtual TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
{
return MapperAccessor.Mapper.Map(source, destination);
}
}
}

@ -1,29 +0,0 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ObjectMapping;
namespace Volo.Abp.AutoMapper
{
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
public class AutoMapperObjectMapper : DefaultObjectMapper
{
protected IMapperAccessor MapperAccessor { get; }
public AutoMapperObjectMapper(IMapperAccessor mapperAccessor, IServiceProvider serviceProvider)
: base(serviceProvider)
{
MapperAccessor = mapperAccessor;
}
protected override TDestination AutoMap<TSource, TDestination>(object source)
{
return MapperAccessor.Mapper.Map<TDestination>(source);
}
protected override TDestination AutoMap<TSource, TDestination>(TSource source, TDestination destination)
{
return MapperAccessor.Mapper.Map(source, destination);
}
}
}

@ -0,0 +1,23 @@
using AutoMapper;
using Volo.Abp.AutoMapper;
namespace Volo.Abp.ObjectMapping
{
public static class AbpAutoMapperObjectMapperExtensions
{
public static IMapper GetMapper(this IObjectMapper objectMapper)
{
return objectMapper.AutoObjectMappingProvider.GetMapper();
}
public static IMapper GetMapper(this IAutoObjectMappingProvider autoObjectMappingProvider)
{
if (autoObjectMappingProvider is AutoMapperAutoObjectMappingProvider autoMapperAutoObjectMappingProvider)
{
return autoMapperAutoObjectMappingProvider.MapperAccessor.Mapper;
}
throw new AbpException($"Given object is not an instance of {typeof(AutoMapperAutoObjectMappingProvider).AssemblyQualifiedName}. The type of the given object it {autoObjectMappingProvider.GetType().AssemblyQualifiedName}");
}
}
}

@ -169,8 +169,16 @@ namespace Volo.Abp.Cli
}
}
private static bool IsGlobalTool(string toolPath)
{
var globalPaths = new[] { @"%USERPROFILE%\.dotnet\tools\", "%HOME%/.dotnet/tools/", };
return globalPaths.Select(Environment.ExpandEnvironmentVariables).Contains(toolPath);
}
private void LogNewVersionInfo(UpdateChannel updateChannel, SemanticVersion latestVersion, string toolPath)
{
var toolPathArg = IsGlobalTool(toolPath) ? "-g" : $"--tool-path {toolPath}";
Logger.LogWarning($"ABP CLI has a newer {updateChannel.ToString().ToLowerInvariant()} version {latestVersion}, please update to get the latest features and fixes.");
Logger.LogWarning(string.Empty);
Logger.LogWarning("Update Command: ");
@ -179,18 +187,18 @@ namespace Volo.Abp.Cli
switch (updateChannel)
{
case UpdateChannel.Stable:
Logger.LogWarning("dotnet tool update -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool update {toolPathArg} Volo.Abp.Cli");
break;
case UpdateChannel.Prerelease:
Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --version {latestVersion}");
Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --version {latestVersion}");
break;
case UpdateChannel.Nightly:
case UpdateChannel.Development:
Logger.LogWarning("dotnet tool uninstall -g Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install -g Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}");
Logger.LogWarning($"dotnet tool uninstall {toolPathArg} Volo.Abp.Cli");
Logger.LogWarning($"dotnet tool install {toolPathArg} Volo.Abp.Cli --add-source https://www.myget.org/F/abp-nightly/api/v3/index.json --version {latestVersion}");
break;
default:
throw new ArgumentOutOfRangeException(nameof(updateChannel), updateChannel, null);

@ -64,6 +64,12 @@ namespace Volo.Abp.Cli.Commands
Logger.LogInformation("UI Framework: " + uiFramework);
}
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long);
if (gitHubLocalRepositoryPath != null)
{
Logger.LogInformation("GitHub Local Repository Path: " + gitHubLocalRepositoryPath);
}
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long);
if (outputFolder != null)
{
@ -90,6 +96,7 @@ namespace Volo.Abp.Cli.Commands
version,
databaseProvider,
uiFramework,
gitHubLocalRepositoryPath,
commandLineArgs.Options
)
);
@ -149,6 +156,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine("--tiered (if supported by the template)");
sb.AppendLine("--no-ui (if supported by the template)");
sb.AppendLine("--separate-identity-server (if supported by the template)");
sb.AppendLine("--local-framework-ref --abp-path <your-local-abp-repo-path> (keeps local references to projects instead of replacing with NuGet package references)");
sb.AppendLine("");
sb.AppendLine("Examples:");
sb.AppendLine("");
@ -160,6 +168,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp new Acme.BookStore -d mongodb -o d:\\my-project");
sb.AppendLine(" abp new Acme.BookStore -t module");
sb.AppendLine(" abp new Acme.BookStore -t module no-ui");
sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\"");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
@ -219,6 +228,11 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "output-folder";
}
public static class GitHubLocalRepositoryPath
{
public const string Long = "abp-path";
}
public static class Version
{
public const string Short = "v";

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
using Volo.Abp.Cli.ProjectBuilding.Files;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public class ProjectReferenceReplaceStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
if (context.BuildArgs.ExtraProperties.ContainsKey("local-framework-ref"))
{
var localAbpRepoPath = context.BuildArgs.AbpGitHubLocalRepositoryPath;
if (string.IsNullOrWhiteSpace(localAbpRepoPath))
{
return;
}
new ProjectReferenceReplacer.LocalProjectPathReferenceReplacer(
context.Files,
"MyCompanyName.MyProjectName",
localAbpRepoPath
).Run();
}
else
{
var nugetPackageVersion = context.TemplateFile.Version;
if (IsBranchName(nugetPackageVersion))
{
nugetPackageVersion = context.TemplateFile.LatestVersion;
}
new ProjectReferenceReplacer.NugetReferenceReplacer(
context.Files,
"MyCompanyName.MyProjectName",
nugetPackageVersion
).Run();
}
}
private bool IsBranchName(string versionOrBranchName)
{
Check.NotNullOrWhiteSpace(versionOrBranchName, nameof(versionOrBranchName));
if (char.IsDigit(versionOrBranchName[0]))
{
return false;
}
if (versionOrBranchName[0].IsIn('v', 'V') &&
versionOrBranchName.Length > 1 &&
char.IsDigit(versionOrBranchName[1]))
{
return false;
}
return true;
}
private abstract class ProjectReferenceReplacer
{
private readonly List<FileEntry> _entries;
private readonly string _companyAndProjectNamePlaceHolder;
protected ProjectReferenceReplacer(
List<FileEntry> entries,
string companyAndProjectNamePlaceHolder)
{
_entries = entries;
_companyAndProjectNamePlaceHolder = companyAndProjectNamePlaceHolder;
}
public void Run()
{
foreach (var fileEntry in _entries)
{
if (fileEntry.Name.EndsWith(".csproj"))
{
fileEntry.SetContent(ProcessFileContent(fileEntry.Content));
}
}
}
private string ProcessFileContent(string content)
{
Check.NotNull(content, nameof(content));
var doc = new XmlDocument() { PreserveWhitespace = true };
doc.Load(GenerateStreamFromString(content));
return ProcessReferenceNodes(doc, content);
}
private string ProcessReferenceNodes(XmlDocument doc, string content)
{
Check.NotNull(content, nameof(content));
var nodes = doc.SelectNodes("/Project/ItemGroup/ProjectReference[@Include]");
foreach (XmlNode oldNode in nodes)
{
var oldNodeIncludeValue = oldNode.Attributes["Include"].Value;
// ReSharper disable once PossibleNullReferenceException : Can not be null because nodes are selected with include attribute filter in previous method
if (oldNodeIncludeValue.Contains($"{_companyAndProjectNamePlaceHolder}"))
{
continue;
}
XmlNode newNode = null;
newNode = GetNewReferenceNode(doc, oldNodeIncludeValue);
oldNode.ParentNode.ReplaceChild(newNode, oldNode);
}
return doc.OuterXml;
}
protected abstract XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue);
private static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
public class NugetReferenceReplacer : ProjectReferenceReplacer
{
private readonly string _nugetPackageVersion;
public NugetReferenceReplacer(List<FileEntry> entries, string companyAndProjectNamePlaceHolder, string nugetPackageVersion)
: base(entries, companyAndProjectNamePlaceHolder)
{
_nugetPackageVersion = nugetPackageVersion;
}
protected override XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue)
{
var newNode = doc.CreateElement("PackageReference");
var includeAttr = doc.CreateAttribute("Include");
includeAttr.Value = ConvertToNugetReference(oldNodeIncludeValue);
newNode.Attributes.Append(includeAttr);
var versionAttr = doc.CreateAttribute("Version");
versionAttr.Value = _nugetPackageVersion;
newNode.Attributes.Append(versionAttr);
return newNode;
}
private string ConvertToNugetReference(string oldValue)
{
var newValue = Regex.Match(oldValue, @"\\((?!.+?\\).+?)\.csproj", RegexOptions.CultureInvariant | RegexOptions.Compiled);
if (newValue.Success && newValue.Groups.Count == 2)
{
return newValue.Groups[1].Value;
}
return oldValue;
}
}
public class LocalProjectPathReferenceReplacer : ProjectReferenceReplacer
{
private readonly string _gitHubLocalRepositoryPath;
public LocalProjectPathReferenceReplacer(List<FileEntry> entries, string companyAndProjectNamePlaceHolder, string gitHubLocalRepositoryPath)
: base(entries, companyAndProjectNamePlaceHolder)
{
_gitHubLocalRepositoryPath = gitHubLocalRepositoryPath;
}
protected override XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue)
{
var newNode = doc.CreateElement("ProjectReference");
var includeAttr = doc.CreateAttribute("Include");
includeAttr.Value = SetGithubPath(oldNodeIncludeValue);
newNode.Attributes.Append(includeAttr);
return newNode;
}
private string SetGithubPath(string includeValue)
{
while (includeValue.StartsWith("..\\"))
{
includeValue = includeValue.TrimStart('.');
includeValue = includeValue.TrimStart('\\');
}
includeValue = _gitHubLocalRepositoryPath.EnsureEndsWith('\\') + includeValue;
return includeValue;
}
}
}
}
}

@ -1,4 +1,5 @@
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
using System;
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
namespace Volo.Abp.Cli.ProjectBuilding.Building
@ -13,11 +14,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
pipeline.Steps.AddRange(context.Template.GetCustomSteps(context));
if (!context.BuildArgs.ExtraProperties.ContainsKey("local-framework-ref"))
{
pipeline.Steps.Add(new NugetReferenceReplaceStep());
}
pipeline.Steps.Add(new ProjectReferenceReplaceStep());
pipeline.Steps.Add(new TemplateCodeDeleteStep());
pipeline.Steps.Add(new SolutionRenameStep());

@ -19,6 +19,9 @@ namespace Volo.Abp.Cli.ProjectBuilding
public UiFramework UiFramework { get; set; }
[CanBeNull]
public string AbpGitHubLocalRepositoryPath { get; set; }
[NotNull]
public Dictionary<string, string> ExtraProperties { get; set; }
@ -28,6 +31,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
[CanBeNull] string version = null,
DatabaseProvider databaseProvider = DatabaseProvider.NotSpecified,
UiFramework uiFramework = UiFramework.NotSpecified,
[CanBeNull] string abpGitHubLocalRepositoryPath = null,
Dictionary<string, string> extraProperties = null)
{
SolutionName = Check.NotNull(solutionName, nameof(solutionName));
@ -35,6 +39,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
Version = version;
DatabaseProvider = databaseProvider;
UiFramework = uiFramework;
AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath;
ExtraProperties = extraProperties ?? new Dictionary<string, string>();
}
}

@ -29,8 +29,7 @@ namespace Volo.Abp.Cli.ProjectModification
var csFiles = new DirectoryInfo(csprojFileDirectory)
.GetFiles("*.cs", SearchOption.AllDirectories)
.Where(f => !f.DirectoryName.StartsWith(binFile))
.Where(f => !f.DirectoryName.StartsWith(objFile))
.Where(f => f.DirectoryName != null && (!f.DirectoryName.StartsWith(binFile) || !f.DirectoryName.StartsWith(objFile)))
.Select(f => f.FullName)
.ToList();

@ -24,7 +24,7 @@ namespace Volo.Abp.Threading
public static bool IsTaskOrTaskOfT([NotNull] this Type type)
{
return type == typeof(Task) || type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>);
return type == typeof(Task) || (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>));
}
/// <summary>

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Ddd.Application.Contracts</AssemblyName>
<PackageId>Volo.Abp.Ddd.Application.Contracts</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,12 @@
using Volo.Abp.Auditing;
using Volo.Abp.Modularity;
namespace Volo.Abp.Application
{
[DependsOn(
typeof(AbpAuditingModule)
)]
public class AbpDddApplicationContractsModule : AbpModule
{
}
}

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Application.Contracts\Volo.Abp.Ddd.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Features\Volo.Abp.Features.csproj" />
<ProjectReference Include="..\Volo.Abp.Http.Abstractions\Volo.Abp.Http.Abstractions.csproj" />

@ -16,6 +16,7 @@ namespace Volo.Abp.Application
{
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpDddApplicationContractsModule),
typeof(AbpSecurityModule),
typeof(AbpObjectMappingModule),
typeof(AbpValidationModule),
@ -30,6 +31,7 @@ namespace Volo.Abp.Application
{
Configure<ApiDescriptionModelOptions>(options =>
{
//TODO: Should we move related items to their own projects?
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));

@ -34,7 +34,11 @@ namespace Volo.Abp.Application.Services
{
public IServiceProvider ServiceProvider { get; set; }
protected readonly object ServiceProviderLock = new object();
protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference);
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
@ -42,7 +46,7 @@ namespace Volo.Abp.Application.Services
{
if (reference == null)
{
reference = ServiceProvider.GetRequiredService<TService>();
reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
}
}
}
@ -57,7 +61,27 @@ namespace Volo.Abp.Application.Services
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager;
public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper);
protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper
{
get
{
if (_objectMapper != null)
{
return _objectMapper;
}
if (ObjectMapperContext == null)
{
return LazyGetRequiredService(ref _objectMapper);
}
return LazyGetRequiredService(
typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext),
ref _objectMapper
);
}
}
private IObjectMapper _objectMapper;
public IGuidGenerator GuidGenerator { get; set; }

@ -188,8 +188,7 @@ namespace Volo.Abp.Application.Services
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetListInput input)
{
//Try to sort query if available
var sortInput = input as ISortedResultRequest;
if (sortInput != null)
if (input is ISortedResultRequest sortInput)
{
if (!sortInput.Sorting.IsNullOrWhiteSpace())
{
@ -215,15 +214,13 @@ namespace Volo.Abp.Application.Services
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetListInput input)
{
//Try to use paging if available
var pagedInput = input as IPagedResultRequest;
if (pagedInput != null)
if (input is IPagedResultRequest pagedInput)
{
return query.PageBy(pagedInput);
}
//Try to limit query result if available
var limitedInput = input as ILimitedResultRequest;
if (limitedInput != null)
if (input is ILimitedResultRequest limitedInput)
{
return query.Take(limitedInput.MaxResultCount);
}

@ -1,5 +1,6 @@
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.EventBus.Distributed;
@ -9,14 +10,14 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed
{
public class EntityToEtoMapper : IEntityToEtoMapper, ITransientDependency
{
protected IObjectMapper ObjectMapper { get; }
protected IHybridServiceScopeFactory HybridServiceScopeFactory { get; }
protected DistributedEventBusOptions Options { get; }
public EntityToEtoMapper(
IOptions<DistributedEventBusOptions> options,
IObjectMapper objectMapper)
IHybridServiceScopeFactory hybridServiceScopeFactory)
{
ObjectMapper = objectMapper;
HybridServiceScopeFactory = hybridServiceScopeFactory;
Options = options.Value;
}
@ -31,15 +32,23 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed
}
var entityType = ProxyHelper.UnProxy(entity).GetType();
var etoType = Options.EtoMappings.GetOrDefault(entityType);
if (etoType == null)
var etoMappingItem = Options.EtoMappings.GetOrDefault(entityType);
if (etoMappingItem == null)
{
var keys = entity.GetKeys().JoinAsString(",");
return new EntityEto(entityType.FullName, keys);
}
//TODO: Also add KeysAsString property to resulting json for compatibility with the EntityEto!
return ObjectMapper.Map(entityType, etoType, entityObj);
using (var scope = HybridServiceScopeFactory.CreateScope())
{
var objectMapperType = etoMappingItem.ObjectMappingContextType == null
? typeof(IObjectMapper)
: typeof(IObjectMapper<>).MakeGenericType(etoMappingItem.ObjectMappingContextType);
var objectMapper = (IObjectMapper) scope.ServiceProvider.GetRequiredService(objectMapperType);
return objectMapper.Map(entityType, etoMappingItem.EtoType, entityObj);
}
}
}
}

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
</ItemGroup>
</Project>

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.0-preview9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
</ItemGroup>
</Project>

@ -30,9 +30,9 @@ namespace Volo.Abp.EntityFrameworkCore
{
protected virtual Guid? CurrentTenantId => CurrentTenant?.Id;
protected virtual bool IsMultiTenantFilterEnabled => DataFilter.IsEnabled<IMultiTenant>();
protected virtual bool IsMultiTenantFilterEnabled => DataFilter?.IsEnabled<IMultiTenant>() ?? false;
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter.IsEnabled<ISoftDelete>();
protected virtual bool IsSoftDeleteFilterEnabled => DataFilter?.IsEnabled<ISoftDelete>() ?? false;
public ICurrentTenant CurrentTenant { get; set; }
@ -316,17 +316,17 @@ namespace Volo.Abp.EntityFrameworkCore
protected virtual void SetCreationAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetCreationProperties(entry.Entity);
AuditPropertySetter?.SetCreationProperties(entry.Entity);
}
protected virtual void SetModificationAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetModificationProperties(entry.Entity);
AuditPropertySetter?.SetModificationProperties(entry.Entity);
}
protected virtual void SetDeletionAuditProperties(EntityEntry entry)
{
AuditPropertySetter.SetDeletionProperties(entry.Entity);
AuditPropertySetter?.SetDeletionProperties(entry.Entity);
}
protected virtual void ConfigureBaseProperties<TEntity>(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType)

@ -3,11 +3,11 @@ using System.Collections.Generic;
namespace Volo.Abp.EventBus.Distributed
{
public class EtoMappingDictionary : Dictionary<Type, Type>
public class EtoMappingDictionary : Dictionary<Type, EtoMappingDictionaryItem>
{
public void Add<TEntity, TEntityEto>()
public void Add<TEntity, TEntityEto>(Type objectMappingContextType = null)
{
this[typeof(TEntity)] = typeof(TEntityEto);
this[typeof(TEntity)] = new EtoMappingDictionaryItem(typeof(TEntityEto), objectMappingContextType);
}
}
}

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.EventBus.Distributed
{
public class EtoMappingDictionaryItem
{
public Type EtoType { get; }
public Type ObjectMappingContextType { get; }
public EtoMappingDictionaryItem(Type etoType, Type objectMappingContextType = null)
{
EtoType = etoType;
ObjectMappingContextType = objectMappingContextType;
}
}
}

@ -2,7 +2,6 @@
using System.Security.Cryptography;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Guids
{
@ -32,7 +31,7 @@ namespace Volo.Abp.Guids
{
// We start with 16 bytes of cryptographically strong random data.
var randomBytes = new byte[10];
RandomNumberGenerator.Locking(r => r.GetBytes(randomBytes));
RandomNumberGenerator.GetBytes(randomBytes);
// An alternate method: use a normally-created GUID to get our initial
// random data:

@ -224,12 +224,16 @@ namespace Volo.Abp.Http.Client.DynamicProxying
requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString());
}
//Culture
//TODO: Is that the way we want? Couldn't send the culture (not ui culture)
var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name;
if (!currentCulture.IsNullOrEmpty())
{
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture));
}
//X-Requested-With
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest");
}
private string GetConfiguredApiVersion()

@ -19,5 +19,13 @@ namespace Volo.Abp.ObjectMapping
);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient(
typeof(IObjectMapper<>),
typeof(DefaultObjectMapper<>)
);
}
}
}

@ -4,16 +4,33 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.ObjectMapping
{
//TODO: It can be slow to always check if service is available. Test it and optimize if necessary.
public class DefaultObjectMapper<TContext> : DefaultObjectMapper, IObjectMapper<TContext>
{
public DefaultObjectMapper(
IServiceProvider serviceProvider,
IAutoObjectMappingProvider<TContext> autoObjectMappingProvider
) : base(
serviceProvider,
autoObjectMappingProvider)
{
}
}
public class DefaultObjectMapper : IObjectMapper, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public IAutoObjectMappingProvider AutoObjectMappingProvider { get; }
protected IServiceProvider ServiceProvider { get; }
public DefaultObjectMapper(IServiceProvider serviceProvider)
public DefaultObjectMapper(
IServiceProvider serviceProvider,
IAutoObjectMappingProvider autoObjectMappingProvider)
{
_serviceProvider = serviceProvider;
AutoObjectMappingProvider = autoObjectMappingProvider;
ServiceProvider = serviceProvider;
}
//TODO: It can be slow to always check if service is available. Test it and optimize if necessary.
public virtual TDestination Map<TSource, TDestination>(TSource source)
{
@ -22,7 +39,7 @@ namespace Volo.Abp.ObjectMapping
return default;
}
using (var scope = _serviceProvider.CreateScope())
using (var scope = ServiceProvider.CreateScope())
{
var specificMapper = scope.ServiceProvider.GetService<IObjectMapper<TSource, TDestination>>();
if (specificMapper != null)
@ -61,7 +78,7 @@ namespace Volo.Abp.ObjectMapping
return default;
}
using (var scope = _serviceProvider.CreateScope())
using (var scope = ServiceProvider.CreateScope())
{
var specificMapper = scope.ServiceProvider.GetService<IObjectMapper<TSource, TDestination>>();
if (specificMapper != null)
@ -87,12 +104,12 @@ namespace Volo.Abp.ObjectMapping
protected virtual TDestination AutoMap<TSource, TDestination>(object source)
{
throw new NotImplementedException($"Can not map from given object ({source}) to {typeof(TDestination).AssemblyQualifiedName}.");
return AutoObjectMappingProvider.Map<TSource, TDestination>(source);
}
protected virtual TDestination AutoMap<TSource, TDestination>(TSource source, TDestination destination)
{
throw new NotImplementedException($"Can no map from {typeof(TSource).AssemblyQualifiedName} to {typeof(TDestination).AssemblyQualifiedName}.");
return AutoObjectMappingProvider.Map<TSource, TDestination>(source, destination);
}
}
}

@ -0,0 +1,14 @@
namespace Volo.Abp.ObjectMapping
{
public interface IAutoObjectMappingProvider
{
TDestination Map<TSource, TDestination>(object source);
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
}
public interface IAutoObjectMappingProvider<TContext> : IAutoObjectMappingProvider
{
}
}

@ -5,6 +5,11 @@
/// </summary>
public interface IObjectMapper
{
/// <summary>
/// Gets the underlying <see cref="IAutoObjectMappingProvider"/> object that is used for auto object mapping.
/// </summary>
IAutoObjectMappingProvider AutoObjectMappingProvider { get; }
/// <summary>
/// Converts an object to another. Creates a new object of <see cref="TDestination"/>.
/// </summary>
@ -24,6 +29,14 @@
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
}
/// <summary>
/// Defines a simple interface to automatically map objects for a specific context.
/// </summary>
public interface IObjectMapper<TContext> : IObjectMapper
{
}
/// <summary>
/// Maps an object to another.
/// Implement this interface to override object to object mapping for specific types.

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

Loading…
Cancel
Save