@ -0,0 +1,14 @@ |
|||||
|
node_modules |
||||
|
npm-debug.log |
||||
|
Dockerfile* |
||||
|
docker-compose* |
||||
|
.dockerignore |
||||
|
.git |
||||
|
.gitignore |
||||
|
.env |
||||
|
*/bin |
||||
|
*/obj |
||||
|
README.md |
||||
|
LICENSE |
||||
|
.vscode |
||||
|
.vs |
||||
@ -0,0 +1,28 @@ |
|||||
|
using Serilog.Core; |
||||
|
using Serilog.Events; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Tracing; |
||||
|
|
||||
|
namespace Volo.AbpWebSite |
||||
|
{ |
||||
|
//This is for trial for now
|
||||
|
public class CorrelationIdLogEventEnricher : ILogEventEnricher, ITransientDependency |
||||
|
{ |
||||
|
private readonly ICorrelationIdProvider _correlationIdProvider; |
||||
|
|
||||
|
public CorrelationIdLogEventEnricher(ICorrelationIdProvider correlationIdProvider) |
||||
|
{ |
||||
|
_correlationIdProvider = correlationIdProvider; |
||||
|
} |
||||
|
|
||||
|
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) |
||||
|
{ |
||||
|
logEvent.AddOrUpdateProperty( |
||||
|
new LogEventProperty( |
||||
|
"CorrelationId", |
||||
|
new ScalarValue("CorrId:" + _correlationIdProvider.Get()) |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,50 +0,0 @@ |
|||||
# COMMON PATHS |
|
||||
|
|
||||
$buildFolder = (Get-Item -Path "./" -Verbose).FullName |
|
||||
$slnFolder = Join-Path $buildFolder "../" |
|
||||
$outputFolder = Join-Path $buildFolder "outputs" |
|
||||
$abpDeskFolder = Join-Path $slnFolder "src/AbpDesk" |
|
||||
$abpDeskWebFolder = Join-Path $abpDeskFolder "AbpDesk.Web.Mvc" |
|
||||
|
|
||||
## CLEAR ###################################################################### |
|
||||
|
|
||||
Remove-Item $outputFolder -Force -Recurse |
|
||||
New-Item -Path $outputFolder -ItemType Directory |
|
||||
|
|
||||
## RESTORE NUGET PACKAGES ##################################################### |
|
||||
|
|
||||
Set-Location $slnFolder |
|
||||
dotnet restore |
|
||||
|
|
||||
## PUBLISH ASPDESK WEB ######################################################## |
|
||||
|
|
||||
Set-Location $abpDeskWebFolder |
|
||||
dotnet publish --output (Join-Path $outputFolder "AbpDesk/Web") |
|
||||
|
|
||||
New-Item -Path (Join-Path $outputFolder "AbpDesk/Web/PlugIns") -ItemType Directory |
|
||||
Copy-Item (Join-Path $abpDeskFolder "Web_PlugIns/*") (Join-Path $outputFolder "AbpDesk/Web/PlugIns/") |
|
||||
|
|
||||
## PUBLISH IDENTITY HTTP API HOST ############################################# |
|
||||
|
|
||||
Set-Location (Join-Path $slnFolder "src/Volo.Abp.Identity.HttpApi.Host") |
|
||||
dotnet publish --output (Join-Path $outputFolder "AbpIdentity/HttpApiHost") |
|
||||
|
|
||||
## CREATE DOCKER IMAGES ####################################################### |
|
||||
|
|
||||
Set-Location (Join-Path $outputFolder "AbpDesk/Web") |
|
||||
|
|
||||
docker rmi abpdesk/web -f |
|
||||
docker build -t abpdesk/web . |
|
||||
|
|
||||
Set-Location (Join-Path $outputFolder "AbpIdentity/HttpApiHost") |
|
||||
|
|
||||
docker rmi abpidentity/httpapihost -f |
|
||||
docker build -t abpidentity/httpapihost . |
|
||||
|
|
||||
## DOCKER COMPOSE FILES ####################################################### |
|
||||
|
|
||||
Copy-Item (Join-Path $slnFolder "docker/*.*") $outputFolder |
|
||||
|
|
||||
## FINALIZE ################################################################### |
|
||||
|
|
||||
Set-Location $outputFolder |
|
||||
@ -1,28 +0,0 @@ |
|||||
version: '2' |
|
||||
|
|
||||
services: |
|
||||
|
|
||||
mongodb: |
|
||||
image: tutum/mongodb |
|
||||
environment: |
|
||||
- AUTH=no |
|
||||
ports: |
|
||||
- "27017:27017" |
|
||||
- "28017:28017" |
|
||||
|
|
||||
abpidentity_httpapihost: |
|
||||
image: abpidentity/httpapihost |
|
||||
environment: |
|
||||
- ASPNETCORE_ENVIRONMENT=Staging |
|
||||
|
|
||||
abpdesk_web: |
|
||||
image: abpdesk/web |
|
||||
environment: |
|
||||
- ASPNETCORE_ENVIRONMENT=Staging |
|
||||
|
|
||||
load_balancer: |
|
||||
image: haproxy:1.7.1 |
|
||||
volumes: |
|
||||
- "./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg" |
|
||||
ports: |
|
||||
- "9005:8080" |
|
||||
@ -1 +0,0 @@ |
|||||
docker-compose down -v --rmi local |
|
||||
@ -1,18 +0,0 @@ |
|||||
global |
|
||||
maxconn 4096 |
|
||||
|
|
||||
defaults |
|
||||
mode http |
|
||||
timeout connect 5s |
|
||||
timeout client 50s |
|
||||
timeout server 50s |
|
||||
|
|
||||
listen http-in |
|
||||
bind *:8080 |
|
||||
|
|
||||
server web-1 outputs_abpdesk_web_1:80 |
|
||||
server web-2 outputs_abpdesk_web_2:80 |
|
||||
|
|
||||
stats enable |
|
||||
stats uri /haproxy |
|
||||
stats refresh 1s |
|
||||
@ -1,8 +0,0 @@ |
|||||
docker rm $(docker ps -aq) |
|
||||
docker-compose up -d mongodb |
|
||||
docker-compose up -d abpidentity_httpapihost |
|
||||
docker-compose up -d abpdesk_web |
|
||||
sleep 2 |
|
||||
docker-compose scale abpdesk_web=2 |
|
||||
sleep 2 |
|
||||
docker-compose up -d load_balancer |
|
||||
@ -0,0 +1,3 @@ |
|||||
|
# Audit Logging |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,3 @@ |
|||||
|
# Event Bus |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,30 @@ |
|||||
|
# Microservice Architecture |
||||
|
|
||||
|
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."* |
||||
|
|
||||
|
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices) |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
One of the major goals of the ABP framework is to provide a convenient infrastructure to create microservice solutions. To make this possible, |
||||
|
|
||||
|
* Provides a [module system](Module-Development-Basics.md) that allows you to split your application into modules where each module may have its own database, entities, services, APIs, UI components/pages... etc. |
||||
|
* Offers an [architectural model](Best-Practices/Module-Architecture.md) to develop your modules to be compatible to microservice development and deployment. |
||||
|
* Provides [best practices guide](Best-Practices/Index.md) to develop your module standards-compliance. |
||||
|
* Provides base infrastructure to implement [Domain Driven Design](Domain-Driven-Design.md) in your microservices. |
||||
|
* Provide services to [automatically create REST-style APIs](AspNetCore/Auto-API-Controllers.md) from your application services. |
||||
|
* Provide services to [automatically create C# API clients](AspNetCore/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application. |
||||
|
* Provides a [distributed event bus](Event-Bus.md) to communicate your services. |
||||
|
* Provides many other services to make your daily development easier. |
||||
|
|
||||
|
## Microservice for New Applications |
||||
|
|
||||
|
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture. |
||||
|
|
||||
|
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts. |
||||
|
|
||||
|
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration. |
||||
|
|
||||
|
## Microservice Demo Solution |
||||
|
|
||||
|
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework. |
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,3 @@ |
|||||
|
# ABP Documentation |
||||
|
|
||||
|
待添加 |
||||
@ -0,0 +1,3 @@ |
|||||
|
## Dynamic Proxying / Interceptors |
||||
|
|
||||
|
待添加 |
||||
@ -0,0 +1,3 @@ |
|||||
|
# Event Bus |
||||
|
|
||||
|
TODO |
||||
@ -0,0 +1,3 @@ |
|||||
|
## Guid 生成 |
||||
|
|
||||
|
待添加 |
||||
@ -0,0 +1,30 @@ |
|||||
|
# 微服务架构 |
||||
|
|
||||
|
*"作为**面向服务架构**(SOA)的一个变体,微服务是一种将应用程序分解成**松散耦合服务**的新型架构风格. 通过**细粒度**的服务和**轻量级**的协议,微服务提供了更多的**模块化**,使应用程序更容易理解,开发,测试,并且更容易抵抗架构侵蚀. 它使小型团队能够**开发,部署和扩展**各自的服务,实现开发的**并行化**.它还允许通过**连续重构**形成单个服务的架构. 基于微服务架构可以实现**持续交付和部署**."* |
||||
|
|
||||
|
— [维基百科](https://zh.wikipedia.org/wiki/Microservices) |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
ABP框架的主要目标之一就是提供**便捷的基础设施来创建微服务解决方案**. 我们做了以下工作: |
||||
|
|
||||
|
* 提供[模块系统](Module-Development-Basics.md),允许将应用程序拆分为模块,其中每个模块可以拥有自己的数据库,实体,服务,API,UI组件/页面....等. |
||||
|
* 提供[架构模型](Best-Practices/Module-Architecture.md)来开发模块,与微服务开发和部署兼容. |
||||
|
* 提供[最佳实践指南](Best-Practices/Index.md)制定模块开发标准. |
||||
|
* 提供基础设施来实现微服务中的[领域驱动设计](Domain-Driven-Design.md). |
||||
|
* 提供从应用程序服务[自动生成REST风格的API](AspNetCore/Auto-API-Controllers.md)的服务. |
||||
|
* 提供[自动创建C#API客户端](AspNetCore/Dynamic-CSharp-API-Clients.md)服务,以便从其他服务/应用程序使用你服务. |
||||
|
* 提供[分布式事件总线](Event-Bus.md)用于服务通信. |
||||
|
* 提供更多其他服务,使日常开发更加简便. |
||||
|
|
||||
|
## 在新应用程序中使用微服务 |
||||
|
|
||||
|
开始一个新解决方案建议**始终从单体开始**, 保持模块化,在单体成为问题时将其拆分为微服务.这使初期进度会很快,特别是如果你的团队人数不多,并且不想处理微服务架构带来的各种挑战. |
||||
|
|
||||
|
然而开发一个良好的模块化应用程序不是那么简单,因为很难像微服务那样**保持模块之间的隔离** (参阅 [Stefan Tilkov的文章](https://martinfowler.com/articles/dont-start-monolith.html)). 微服务架构会自然的让你开发隔离的服务,但是在模块化的单体应用程序中,模块很容易彼此紧密耦合并设计出**弱模块边界**和API约定. |
||||
|
|
||||
|
ABP可以帮助你,它提供了与**与微服务兼容的严格模块架构** 在这个架构中你的模块被分割成多个层/项目,在自己的VS解决方案中进行开发,该解决方案完成独立于其它模块. 这种方式开发的模块是一种天然的微服务,但是它可以很容易的插入到单体应用程序中. 请参阅**微服务优先的模块设计**的[模块开发最佳实践指南](Best-Practices/Index.md). 所有[标准的ABP模块](https://github.com/abpframework/abp/tree/master/modules)都是基于本指南开发的. 因此你可以将这些模块嵌入到单体解决方案中使用它们,也可以单独部署通过远程API调用. 它们可以共享一个数据库,也可以通过简单配置使用自己的数据库. |
||||
|
|
||||
|
## 微服务解决方案示例 |
||||
|
|
||||
|
[微服务解决方案示例](Samples/Microservice-Demo.md)演示了基于ABP框架的完整的微服务的解决方案. |
||||
@ -0,0 +1,40 @@ |
|||||
|
# 微服务解决方案示例 |
||||
|
|
||||
|
*"作为**面向服务架构**(SOA)的一个变体,微服务是一种将应用程序分解成**松散耦合服务**的新型架构风格. 通过**细粒度**的服务和**轻量级**的协议,微服务提供了更多的**模块化**,使应用程序更容易理解,开发,测试,并且更容易抵抗架构侵蚀. 它使小型团队能够**开发,部署和扩展**各自的服务,实现开发的**并行化**.它还允许通过**连续重构**形成单个服务的架构. 基于微服务架构可以实现**持续交付和部署**."* |
||||
|
|
||||
|
— [维基百科](https://zh.wikipedia.org/wiki/Microservices) |
||||
|
|
||||
|
## 介绍 |
||||
|
|
||||
|
ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服务解决方案](../Microservice-Architecture.md). |
||||
|
|
||||
|
此示例演示了一个简单而完整的微服务解决方案; |
||||
|
|
||||
|
* 拥有多个可独立可单独部署的**微服务**. |
||||
|
* 多个**Web应用程序**, 每一个都使用不同的API网关. |
||||
|
* 使用[Ocelot](https://github.com/ThreeMammals/Ocelot)库开发了多个**网关** / BFFs ([用于前端的后端](https://docs.microsoft.com/zh-cn/azure/architecture/patterns/backends-for-frontends)). |
||||
|
* 包含使用[IdentityServer](https://identityserver.io/)框架开发的 **身份认证服务**. 它也是一个带有UI的SSO(单点登陆)应用程序. |
||||
|
* 有**多个数据库**. 一些微服务有自己的数据库,也有一些服务/应用程序共享同一个数据库(以演示不同的用例). |
||||
|
* 有不同类型的数据库: **SQL Server** (与 **Entity Framework Core** ORM) 和 **MongoDB**. |
||||
|
* 有一个**控制台应用程序**使用身份验证展示使用服务最简单的方法. |
||||
|
* 使用[Redis](https://redis.io/)做**分布式缓存**. |
||||
|
* 使用[RabbitMQ](https://www.rabbitmq.com/)做服务间的**消息**传递. |
||||
|
* 使用[Kubernates](https://kubernetes.io/)**部署**和运行所有的服务和应用程序. |
||||
|
|
||||
|
下图显示了该系统: |
||||
|
|
||||
|
 |
||||
|
|
||||
|
### 源码 |
||||
|
|
||||
|
你可以从[GitHub仓库](https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo)获取源码. |
||||
|
|
||||
|
### 状态 |
||||
|
|
||||
|
此示例仍处于开发阶段,尚未完成. |
||||
|
|
||||
|
## 微服务 |
||||
|
|
||||
|
### 身份认证服务 |
||||
|
|
||||
|
... |
||||
@ -0,0 +1,3 @@ |
|||||
|
## Unit of Work |
||||
|
|
||||
|
待添加 |
||||
@ -0,0 +1,3 @@ |
|||||
|
## Validation |
||||
|
|
||||
|
待添加 |
||||
|
After Width: | Height: | Size: 69 KiB |
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Client |
||||
|
{ |
||||
|
public static class CachedApplicationConfigurationClientExtensions |
||||
|
{ |
||||
|
public static ApplicationConfigurationDto Get(this ICachedApplicationConfigurationClient client) |
||||
|
{ |
||||
|
return AsyncHelper.RunSync(client.GetAsync); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Client |
||||
|
{ |
||||
|
public class RemoteLocalizationContributor : ILocalizationResourceContributor |
||||
|
{ |
||||
|
private LocalizationResource _resource; |
||||
|
private ICachedApplicationConfigurationClient _applicationConfigurationClient; |
||||
|
private ILogger<RemoteLocalizationContributor> _logger; |
||||
|
|
||||
|
public void Initialize(LocalizationResourceInitializationContext context) |
||||
|
{ |
||||
|
_resource = context.Resource; |
||||
|
_applicationConfigurationClient = context.ServiceProvider.GetRequiredService<ICachedApplicationConfigurationClient>(); |
||||
|
_logger = context.ServiceProvider.GetService<ILogger<RemoteLocalizationContributor>>() |
||||
|
?? NullLogger<RemoteLocalizationContributor>.Instance; |
||||
|
} |
||||
|
|
||||
|
public LocalizedString GetOrNull(string cultureName, string name) |
||||
|
{ |
||||
|
var resource = GetResourceOrNull(); |
||||
|
if (resource == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var value = resource.GetOrDefault(name); |
||||
|
if (value == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return new LocalizedString(name, value); |
||||
|
} |
||||
|
|
||||
|
public void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary) |
||||
|
{ |
||||
|
var resource = GetResourceOrNull(); |
||||
|
if (resource == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var keyValue in resource) |
||||
|
{ |
||||
|
dictionary[keyValue.Key] = new LocalizedString(keyValue.Key, keyValue.Value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Dictionary<string, string> GetResourceOrNull() |
||||
|
{ |
||||
|
var resource = _applicationConfigurationClient |
||||
|
.Get() |
||||
|
.Localization.Values |
||||
|
.GetOrDefault(_resource.ResourceName); |
||||
|
|
||||
|
if (resource == null) |
||||
|
{ |
||||
|
_logger.LogWarning($"Could not find the localization resource {_resource.ResourceName} on the remote server!"); |
||||
|
} |
||||
|
|
||||
|
return resource; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Settings; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Client |
||||
|
{ |
||||
|
public class RemoteSettingProvider : ISettingProvider, ITransientDependency |
||||
|
{ |
||||
|
protected ICachedApplicationConfigurationClient ConfigurationClient { get; } |
||||
|
|
||||
|
public RemoteSettingProvider(ICachedApplicationConfigurationClient configurationClient) |
||||
|
{ |
||||
|
ConfigurationClient = configurationClient; |
||||
|
} |
||||
|
|
||||
|
public async Task<string> GetOrNullAsync(string name) |
||||
|
{ |
||||
|
var configuration = await ConfigurationClient.GetAsync(); |
||||
|
return configuration.Setting.Values.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
public async Task<List<SettingValue>> GetAllAsync() |
||||
|
{ |
||||
|
var configuration = await ConfigurationClient.GetAsync(); |
||||
|
return configuration |
||||
|
.Setting.Values |
||||
|
.Select(s => new SettingValue(s.Key, s.Value)) |
||||
|
.ToList(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class ApplicationSettingConfigurationDto |
||||
|
{ |
||||
|
public Dictionary<string, string> Values { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions |
||||
|
{ |
||||
|
public static class ModelExplorerExtensions |
||||
|
{ |
||||
|
public static T GetAttribute<T>(this ModelExplorer property) where T : Attribute |
||||
|
{ |
||||
|
return property?.Metadata?.ContainerType?.GetTypeInfo()?.GetProperty(property.Metadata.PropertyName)?.GetCustomAttribute<T>(); |
||||
|
} |
||||
|
|
||||
|
public static int GetDisplayOrder(this ModelExplorer explorer) |
||||
|
{ |
||||
|
return GetAttribute<DisplayOrder>(explorer)?.Number ?? DisplayOrder.Default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using Microsoft.AspNetCore.Razor.TagHelpers; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions |
||||
|
{ |
||||
|
public static class TagHelperAttributeExtensions |
||||
|
{ |
||||
|
public static string ToHtmlAttributeAsString(this TagHelperAttribute attribute) |
||||
|
{ |
||||
|
return attribute.Name + "=\"" + attribute.Value + "\""; |
||||
|
} |
||||
|
|
||||
|
public static string ToHtmlAttributesAsString(this List<TagHelperAttribute> attributes) |
||||
|
{ |
||||
|
var attributesAsString = ""; |
||||
|
|
||||
|
foreach (var attribute in attributes) |
||||
|
{ |
||||
|
attributesAsString += attribute.ToHtmlAttributeAsString() + " "; |
||||
|
} |
||||
|
|
||||
|
return attributesAsString; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Microsoft.AspNetCore.Razor.TagHelpers; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions |
||||
|
{ |
||||
|
public static class TagHelperContextExtensions |
||||
|
{ |
||||
|
public static T GetValue<T>(this TagHelperContext context, string key) |
||||
|
{ |
||||
|
if (!context.Items.ContainsKey(key)) |
||||
|
{ |
||||
|
return default(T); |
||||
|
} |
||||
|
|
||||
|
return (T)context.Items[key]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Razor.TagHelpers; |
||||
|
using System.Text.Encodings.Web; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions |
||||
|
{ |
||||
|
public static class TagHelperExtensions |
||||
|
{ |
||||
|
public static TagHelperOutput ProcessAndGetOutput(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) |
||||
|
{ |
||||
|
var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run<TagHelperContent>(() => new DefaultTagHelperContent())) |
||||
|
{ |
||||
|
TagMode = tagMode |
||||
|
}; |
||||
|
|
||||
|
var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); |
||||
|
|
||||
|
tagHelper.Init(context); |
||||
|
|
||||
|
if (runAsync) |
||||
|
{ |
||||
|
AsyncHelper.RunSync(() => tagHelper.ProcessAsync(innerContext, innerOutput)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tagHelper.Process(innerContext, innerOutput); |
||||
|
} |
||||
|
|
||||
|
return innerOutput; |
||||
|
} |
||||
|
|
||||
|
public static string Render(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, HtmlEncoder htmlEncoder, string tagName = "div", TagMode tagMode = TagMode.SelfClosing, bool runAsync = false) |
||||
|
{ |
||||
|
var innerOutput = tagHelper.ProcessAndGetOutput(attributeList, context, tagName, tagMode, runAsync); |
||||
|
|
||||
|
return innerOutput.Render(htmlEncoder); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Microsoft.AspNetCore.Razor.TagHelpers; |
||||
|
using System.IO; |
||||
|
using System.Text.Encodings.Web; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions |
||||
|
{ |
||||
|
public static class TagHelperOutputExtensions |
||||
|
{ |
||||
|
public static string Render(this TagHelperOutput output, HtmlEncoder htmlEncoder) |
||||
|
{ |
||||
|
using (var writer = new StringWriter()) |
||||
|
{ |
||||
|
output.WriteTo(writer, htmlEncoder); |
||||
|
return writer.ToString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,11 +0,0 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Label |
|
||||
{ |
|
||||
public class AbpLabelTagHelper : AbpTagHelper<AbpLabelTagHelper, AbpLabelTagHelperService> |
|
||||
{ |
|
||||
public AbpLabelTagHelper(AbpLabelTagHelperService tagHelperService) |
|
||||
: base(tagHelperService) |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,12 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Razor.TagHelpers; |
|
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Label |
|
||||
{ |
|
||||
public class AbpLabelTagHelperService : AbpTagHelperService<AbpLabelTagHelper> |
|
||||
{ |
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output) |
|
||||
{ |
|
||||
//TODO: fill
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,14 +1,17 @@ |
|||||
@using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Components.TenantSwitch |
@using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Components.TenantSwitch |
||||
@model TenantSwitchViewComponent.TenantSwitchViewModel |
@model TenantSwitchViewComponent.TenantSwitchViewModel |
||||
<li class="nav-item"> |
@if (!Model.CurrentUser.IsAuthenticated) |
||||
<a abp-button="Link" id="TenantSwitchToolbarLink" href="#"> |
{ |
||||
@if (Model.Tenant == null) |
<li class="nav-item"> |
||||
{ |
<a abp-button="Link" id="TenantSwitchToolbarLink" href="#"> |
||||
<text>@@host</text> |
@if (Model.Tenant == null) |
||||
} |
{ |
||||
else |
<text>@@host</text> |
||||
{ |
} |
||||
<text>@@@Model.Tenant.Name</text> |
else |
||||
} |
{ |
||||
</a> |
<text>@@@Model.Tenant.Name</text> |
||||
</li> |
} |
||||
|
</a> |
||||
|
</li> |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using Microsoft.AspNetCore.Routing; |
||||
|
|
||||
|
namespace Microsoft.AspNetCore.Builder |
||||
|
{ |
||||
|
public static class AbpAspNetCoreMvcApplicationBuilderExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Adds MVC to the <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" /> request execution pipeline
|
||||
|
/// with the following default routes:
|
||||
|
///
|
||||
|
/// - a default route named 'defaultWithArea' and the following template: '{area}/{controller=Home}/{action=Index}/{id?}'.
|
||||
|
/// - a default route named 'default' and the following template: '{controller=Home}/{action=Index}/{id?}'.
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">The <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" />.</param>
|
||||
|
/// <param name="additionalConfigurationAction">Additional action to configure routes</param>
|
||||
|
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
|
public static IApplicationBuilder UseMvcWithDefaultRouteAndArea( |
||||
|
this IApplicationBuilder app, |
||||
|
Action<IRouteBuilder> additionalConfigurationAction = null) |
||||
|
{ |
||||
|
return app.UseMvc(routes => |
||||
|
{ |
||||
|
routes.MapRoute( |
||||
|
name: "defaultWithArea", |
||||
|
template: "{area}/{controller=Home}/{action=Index}/{id?}"); |
||||
|
|
||||
|
routes.MapRoute( |
||||
|
name: "default", |
||||
|
template: "{controller=Home}/{action=Index}/{id?}"); |
||||
|
|
||||
|
additionalConfigurationAction?.Invoke(routes); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||