21
10

C#泛型接口的协变和逆变

0
归档:2022年10月分类:C#和.NET

一、协变和逆变是什么?

先从字面上理解 协变(Covariance)、逆变(Contravariance)。

co- 是英文中表示“协同”、“合作”的前缀。协变 的字面意思就是 “与变化的方向相同”。
contra- 是英文中表示“相反”的前缀,逆变 的字面意思就是是 “与变化方向相反”。

官方:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。

那么问题来了,这里的“变化方向”指的是什么?

C# 中对于对象(即对象引用),仅存在一种隐式类型转换,即 子类型的对象引用到父类型的对象引用的转换。这里的变化指的就是这种 子->父 的类型转换。

协变与逆变虽然从名字上看是两个完全相反的转换,但其实只是“子类型引用到父类型引用”这一过程在函数中使用的“两个不同阶段”而已,接下来将详细说明这点。

二、为什么需要协变和逆变

三、协变例子

四、逆变例子

五、.NET自带的协变和逆变委托和泛型

20
10

C#委托之Action和Func的用法

0
归档:2022年10月分类:C#和.NET

我们在使用委托的过程中,除了为每个参数和返回类型定义一个委托之外,也就是说为每一个方法(作为参数的方法)定义一个委托,我们还可以使用Action和Func委托。

泛型Action委托表示引用一个void返回类型的方法。Action委托类存在不同的变体,可以传递至多16种不同的参数类型,没有泛型参数的Action类可以调用没有参数的方法。例如:Action调用带一个参数的方法,Action<in T1,in T2>调用带两个参数的方法等

Func的用法和Action用法类似,但是Func表示引用一个带返回类型的方法,Func也存在不同的变体,至多可以传递16个参数类型和1个返回类型,例如:Func<in T1,out Resout>表示带一个参数的方法,Func<in T1,in T2,out Resout>表示调用带两个参数的方法。

总结 1:Action用于没有返回值的方法(参数可以根据自己情况进行传递);2:Func恰恰相反用于有返回值的方法(同样参数根据自己情况情况);3:记住无返回就用action,有返回就用Func

10
10

[转]Azure Container App 应用介绍

0
归档:2022年10月分类:C#和.NET

容器技术正日益成为打包、部署应用程序的第一选择。Azure提供了许多使用容器的选项。例如,我们可以使用Azure容器注册表来存储和管理Docker Images。Azure Container Instance或Azure应用服务可用于运行隔离容器。对于需要完整容器编排、自动缩放和服务发现的更复杂的场景,Azure Kubernetes服务是一个很好的选择。问题是,Azure Kubernetes虽然是托管的,但 用起来还是有些困难,开发人员必须得学习掌握一些运维知识。

2021年,Azure提出了新服务 Azure Container App。该服务旨在减少构建 AKS 应用程序所需的知识和配置量,从而可能降低解决方案成本并加快上市时间。

1,什么是 Azure Container App ??

Azure Container App 提供位于 AKS 服务之上的无服务器托管服务,允许您部署多个容器而无需处理底层基础结构。事实上,Azure Container App 甚至不向用户公开 Kubernetes API。

当我们在 Azure Container App中部署或更改容器时,该服务将自动创建应用程序的快照-----修订版,并在一个 pod 中运行它的容器。就像在 Azure Kubernetes 中一样,这些容器共享相同的应用程序生命周期、网络和磁盘。它们可以相互交流。此外,由于它与基于 Kubernetes 的事件驱动自动缩放的集成,该服务将根据 HTTP 并发请求数等指标自动增加/减少(不支持垂直缩放)与修订相关的 Pod 数量和内存使用。作为节省一些费用上开支,我们还可以将最小副本数设置为 0。如果应用程序没有请求,该服务会将活动 pod 的数量缩减为 0,我们无需支付任何费用。

同时多个 Azure Container App 也可以部署到单个环境中。通过这样做,它们将被置于同一个虚拟网络下并与外界隔离。为了提供监视功能,每个环境都有自己的 Log Analytics 工作区,该工作区与其中的 Azure Container App共享。对于熟悉 Kubernetes 的人来说,我们可以将 Azure Container App Environment 视为 Kubernetes Namespace,将 Azure Container App Revision 视为 Kubernetes Deployment。

最后就是,Azure Container App 将可用选项减少为一个简单的入口切换,一旦启用,它就会提供 HTTPS 入口和完全限定域名 (FQDN)。

2、修改

当我们在 Azure Container App 中部署或更改容器时,该服务会自动获取 Azure Container Instance 的不可变快照。然后它在一个单独的 pod 上部署一个新版本。但是,并非所有更改都会触发此行为。在 Azure Container App 中,我们可以区分两种类型的更改:

  1)修订范围更改。

  2)应用范围的变化。

当我们创建新版本时,旧版本的流量会完全转移到新版本。我们可以使用流量拆分规则来定义它们之间的流量平衡方式。这些包括将一定比例的流量引导到特定的修订版。

3、和 Dapr 集成

Azure Container App 提供与开源项目 Dapr(分布式应用程序运行时)的内置兼容性。启用后,Dapr 应用程序将与我们修订版一起部署,通过 HTTP 或 gRPC 提供对 Dapr API 提供的功能的访问。

三,结尾
  相对于操作有些复杂的 Azure Kubernetes 和昂贵的使用的定价,Azure Container App 这项服务变得越来越流行。目前,该服务作为公共预览版提供,因此我们已经可以开始使用它了。

作者:Allen

09
9

依赖注入简单易懂的详解

0
归档:2022年9月分类:C#和.NET

之前记录的依赖注入太复杂,今天认真看了《C#高级编程》里面对依赖注入的解释,明显就简单很多。

1、什么是依赖注入?为什么需要它?

更快的开发周期需要单元测试和更好的可更新性。更改一些代码,不应该导致意外位罝出现错误。创建更模块化的、减少依赖项的应用程序,有助于防止这种错误。

依赖注入(Dependency Injection,DI)允许从类的外部注入依赖项,因此注入依赖项的类只需要知道一个协定(通常是C#接口)。这个类可以独立于其对象的创建。

依赖注入更便于进行单元测试。在单元测试中,只需要测试特定的类,需要的依赖项可以替换为包含测试数据的特殊模拟类。

还可以使用不同的实现区分生产模式和开发模式。例如,在生产过程中,可能需要访问SAP服务器,或者可能需要对所有开发人员都无法访问的特定活动目录进行身份验证。在开发的每个调试会话期间,都不希望等待成功的身份验证,也不需要SAP服务器开发用户界面。在这里,可以给相同的协定使用不同的实现来模拟身份验证,可以使用测试数据而不是访问SAP服务器。

也可以在不同的平台上使用不同的实现。例如,可以创建一个.NET标准库,在其中为UWP、WPF和Xamarin应用程序实现所有公共功能,并可以根据需要重定向到特定于平台的代码。

依赖注入还允许用自定义特性替换标准功能。ASP.NETCore和EntityFrameworkCore主要基于依赖注入。这些技术使用数百个协定一例如,来找到控制器,将HTTP请求映射到控制器,将接收到的数据转换为参数,将数据库表映射到实体类型等。使用不同的实现,可以轻松地替换自定义功能。

DI是敏捷软件开发和持续软件交付实践的核心模式。

依赖注入不需要依赖注入容器,但该容器有助于管理依赖项。依赖注入容器管理的服务列表越来越长,就可以看到它的优点。ASP.NETCore和EntityFrameworkCore使用Microsoft.Exteosions.DependencyInjection作为容器来管理所有依赖项,以此管理数百个服务。

尽管依赖注入和依赖注入容器在非常小的应用程序中会增加复杂性,但是一旦应用程序变得更大,需要多个服务,依赖注入就会降低复杂性,并促进非紧密绑定的实现。

2、没有依赖注入

下面的示例没有使用依赖注入;稍后将更改它,以使用依赖注入。所用的服务实现在类GreetingService中定义。这个类定义了返回字符串的Greet方法:

public class GreetingService
{
  public string Greet(stringname)=>$"Hello,{name}";
}

类HomeController使用这个服务。在Hello方法中,实例化了GreetingService,并且调用Greet方法:

public class HomeController
{
   public string Hello(string name)
   {
    var service = new GreetingService();
    return service.Greet(name);
   }
}

下面看看Program类的Main()方法。其中实例化了HomeController,调用Hello方法,将结果写入控制台:

static void Main()
{
  var controller=new HomeController();
  string result=controller.Hello("Stephanie");
  Console.WriteLine(result);
}

程序运行时,把Hello,Stephanie写入控制台。这有什么问题吗?

HomeController和GreetingService是紧密稱合的。要用不同的实现取代HomeController中的GreetingService并不容易。这个GreetingService是一个返回字符串的简单服务。在正常的应用程序中,场景通常更复杂。例如,GreetingService可能使用HTTP请求访问API服务,或者使用EntityFramewoik访问数据库。可能要更改在一个地方使用的服务,而不是査找使用服务的所有位置。

另外,为HomeController创建单元测试时,也会测试GreetingService。在单元测试中,希望仅测试单个类的方法的功能,而不需要使用其他依赖项。在HomeController中,不能很容易地为单元测试替换GreetingService。从技术上讲,为单元测试替换GreetingService方法的内部实现是可能的。使用Microsoft Fakes框架,可以通过替换GreetingSeivice类的特定方法和属性,来更改方法的实现。这个变更是在单元测试中定义的,并且只有在单元测试运行时才会发生:通过另一个方法来“伪造”原来的方法。其实这有更好的方法:使用依赖注入。

下一节将介绍如何更改此实现,以使用依赖注入。

3、使用依赖注入实现

下面使HomeController独立于GreetingService的实现。为此,可以创建接口IGreetingService,它定义了HomeController所需的功能:

public interface IGreetingService
{
  string Greet(stringname);
}

GreetingService现在实现了接口IGreetingService:

public class GreetingService:IGreetingService
{
   public string Greet(stringname)=>$"Hello,{name}";
}

HomeController现在只需要对一个对象的引用,该对象实现了接口IGreetingService。它用HomeController的构造函数注入,分配给私有字段,通过方法Hello来使用:

public class HomeController
{
    private readonly IGreetingService _greetingService;

    public HomeController(IGreetingService greetingService)

    {
        _greetingService=greetingService??
            throw new ArgumentMullException(nameof(greetingService));
    }
    public string Hello(stringname) => _greetingService.Greet(name);

}

在这个实现中,HomeController利用了控制反转的设计原理。HomeController没有像以前那样实例化GreetingService。相反,定义由HomeController使用的具体类的控件在外部给出;换句话说,控制是反转的。

注意:控制反转也被称为好莱坞原則:不要给我们打电话;我们会给你打电话。

控制反转也减少了对不同技术的依赖,创建出更通用的代码。例如,可以在.NET标准库中为WPF、UWP和Xamarin应用程序一同使用相同的视图糢型和服务协定。对于WPF、UWP和Xamarin,有些服务需要不同的实现。这种服务的实现可以来自于托管应用程序,而协定是在.NET标准库中定义和使用的。阅读第34章可以获得关于视图模型的更多信息。
类HomeController并没有依赖IGreetingService接口的具体实现。

HomeController可以使用实现了接口IGreetingService的任何类。这个类只需要实现这个接口的所有成员。现在,需要从外部注入依赖项,将具体的实现传递给HdloControUer类的构造函数。在样例代码中,使用构造函数注入的依赖注入模式实现了控制反转的设计原则。它称为构造函数注入,因为接口是在构造函数中注入的。需要注入依赖项来创建一个HomeController实例。

下面修改Main()方法,将IGreetingService的具体实现传递给HomeControIler。这里,注入了依赖项:

static void Main()
{
    var controller=new HomeController(new GreetingService());
    string result=controller.Hello("Matthias");
    Console.WriteLine(result);
}

为HomeController的Hello方法创建一个单元测试,可以注入不同的实现。例如,执行IGreetingService的MockGreetingService。

示例应用程序目前非常小。唯一需要注入的是一个实现协定的具体类。这个类在实例化的同时实例化了HomeController。在实际应用程序中,需要处理许多接口和实现,还需要共享实例。这样做的一个简单方法是使用依赖注入容器来管理所有依赖项。应用程序还可以使用Microsoft.Extensions.Dependencylnjection容器。或者使用第三方的依赖注入容器,比如Autofac。

05
9

https请求报错block:mixed-content

0
归档:2022年9月分类:C#和.NET

开发过程中,可能会遇到在https里面请求内容的时候报混合调用的错误,一般是因为网站启用了https,但是老的网页链接和请求中还有使用http的情况。接口会错block: mixed-content,图片则会报错:Mixed block。解决的办法有两种。

第一种, 页面的head中加入下面代码(将调用的http请求升级成https请求并调用):

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

这是最方便快捷的,也是很有效的办法。

第二种, 如果是接口,可以通过本地后端(将本地后端当成service中间层),从后端再去调用其他服务器的http请求。

23
8

如何在.NET Core3.1使用log4net

0
归档:2022年8月分类:C#和.NET

1、添加log4net程序集

通过nuget添加,可以直接在程序集引用里搜索nuget包安装,也可以通过命令行安装,这里使用命令行:PM> Install-Package log4net

2、添加log4net.config配置文件

创建一个log4net.config文件,保存在项目的根目录:

<log4net>
  <root>
    <level value="ALL" />
    <appender-ref ref="console" />
    <appender-ref ref="file" />
  </root>

  <!--File Appender-->
  <appender name="file" type="log4net.Appender.RollingFileAppender">
    <file value="main.log" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="5" />
    <maximumFileSize value="25MB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
    </layout>
  </appender>

  <!--Console appender-->
  <appender name="console" type="log4net.Appender.ManagedColoredConsoleAppender">
    <mapping>
      <level value="INFO" />
      <forecolor value="Green" />
    </mapping>
    <mapping>
      <level value="WARN" />
      <forecolor value="Yellow" />
    </mapping>
    <mapping>
      <level value="ERROR" />
      <forecolor value="Red" />
    </mapping>
    <mapping>
      <level value="DEBUG" />
      <forecolor value="Blue" />
    </mapping>
    <layout type="log4net.Layout.PatternLayout">
      <conversionpattern value="%date [%thread] %-5level - %message%newline" />
    </layout>
  </appender>
</log4net>

3、加载log4net的配置的并创建相关实例

这里有两种办法,第一种最简单,这种情况适用于控制台应用程序,简单明了,直接用加载到程序集中:

using log4net;
using log4net.Config;
using System;
using System.IO;
using System.Reflection;

namespace NetCore3Log4Net.Tutorial.ConsoleApp
{
    class Program
    {
        private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        static void Main(string[] args)
        {
            // 加载配置的 configuration
            var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
            XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));

            Console.WriteLine("Hello world!");

            // 打印日志
            log.Info("Hello logging world!");
            log.Error("Error!");
            log.Warn("Warn!");

            Console.ReadLine();
        }
    }
}

第二种办法是通过依赖注入的方式加载log4net,这种方法适合用在MVC网站项目中使用,首先要注册Log4Net服务:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup()

            .ConfigureLogging((hostingContext, builder) =>
            {
                //该方法需要引入Microsoft.Extensions.Logging名称空间
                builder.AddFilter("System", LogLevel.Error); //过滤掉系统默认的一些日志
                builder.AddFilter("Microsoft", LogLevel.Error);//过滤掉系统默认的一些日志

                //添加Log4Net
                var path = Path.Combine(Directory.GetCurrentDirectory(), "Log4net.config");
                //不带参数:表示log4net.config的配置文件就在应用程序根目录下,也可以指定配置文件的路径
                //需要添加nuget包:Microsoft.Extensions.Logging.Log4Net.AspNetCore
                builder.AddLog4Net(path);
            });
        });

其次,要在Startup.cs中注入Log4Net

// 注意在CreateDefaultBuilder中,添加Autofac服务工厂
/// 
/// 自定义容器服务注册
/// 
/// 
public void ConfigureContainer(ContainerBuilder builder)
{
    // 业务逻辑层 所在程序集命名空间
    Assembly service = Assembly.Load("WebAPI.Services");
    // 接口层 所在程序集命名空间
    Assembly repository = Assembly.Load("WebAPI.IService");
    // 自动注入
    builder.RegisterAssemblyTypes(service, repository).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces();

    // 注入Log4Net
    builder.RegisterType().As().SingleInstance();
}
15
8

亲子沟通提示:重视亲子沟通

0
归档:2022年8月分类:点滴生活

一、耐心、认真地去“听”你的孩子讲话

有时当妈妈一边炒菜一边盛饭时,孩子在想跟妈妈说些什么时,妈妈有时是不经心地听着,有时会打断她,反而请她帮忙摆桌吃饭,但从未停下来,专心去“听”她想说的话,除非她说的话,真是什么天大的好消息(不过,这种机率低于百分之一)。

这里不是教您一定要停下手边所做的,但你可给孩子一个“确定”的时间,比如:“好,等我把饭菜摆好。”或是“再给我一分钟。"当然,最好是立即去“听”孩子要说的。

因为,你一次,二次,三次地忽略了他们后,他们便会慢慢地失去了"告诉你”的兴趣。另外,在“听”时,最好做目光接触,要孩子知道你是直的愿意听。很多时候,孩子不过想说你觉得不太要紧的事,但对他们而言,却是重要的。如果你没有经常拒绝他们,当他们真遇到困难时,自然就会地来找你谈了。

二、有些话不能说,以免伤害到孩子的感情

家长不要说一些孩子最不想听的话,即使在盛怒的时候,也要思考话语的轻重,以免伤害孩子的感情。

另外,孩子的世界与想法是和大人们不同的,当孩子很认真地告诉你什么时,你如果取笑孩子,或者有轻蔑或不相信的态度,也会伤害孩子,这样孩子以后也就不再愿意跟你谈了。

三、有意识地与孩子进行沟通,主动分享自己的感受

多与孩子说说自己的感受和以往的经历,让孩子走进自己,向孩子表达自己的期待和需求。

当然,做父母的,不需要把心中的担心向孩子表明,去增加孩子担忧的心理负担,但有时不妨主动地与孩子分享自己的心情、感受,及对事情的看法,孩子当然也就比较容易向你诉说他的心情、感受与看法了。

四、重视沟通的方法,别问一些太空洞的问题

譬如“今天如何?”“学校好吗?”也别常用,“为什么?”因为孩子会不自觉地想保护自己。以询问孩子一些有趣的、愉快的事情作为聊天的开场白。愉快的开始,能营造良好的沟通氛围,促进亲子沟通。

了解孩子的兴趣,了解现在孩子与同龄人中流行的东西,包括儿歌、动画片,故事书等,与孩子一起做他感兴趣的事,进行积极沟通,让孩子感受到父母对自己的关心,他会更愿意与家长沟通。

五、了解孩子的想法,理解孩子要表达的意思

由于孩子的表达能力条理性较差,因此孩子有时不能清晰表达自己。家长要耐心等孩子把话说完,把“零散”的东西拼凑起来,准确理解孩子要表达的意思。

大部分时间,孩子只是想从他信任的成人中,确定自己的想法与做法。所以,家长可在了解情况后,做个回答或支持。

08
7

ABP框架能力清单

0
归档:2022年7月分类:C#和.NET

ABP框架提供了一个稳定的架构用于构建企业级软件解决方案,它遵循了.NET和ASP.NETCore平台之上的最佳实践。它内置了基础架构、生产级别的模块、主题、工具、指南和文档,并尽可能地自动化开发细节和解法我们的重复性工作。

在接下来的几个小节中,我将从架构层面介绍ABP是如何完成所有这些工作。

ABP架构

ABP是一个特殊的架构,换句话说,它是一个有个性化的框架。先解释一下什么是没有个性的框架,什么是有个性的框架。

正如我在搭建架构部分所述,为搭建解决方案的基础设施需要大量决策工作;比如系统架构、开发模型、技术、模式、工具和库。

没有个性的框架,例如ASP.NETCore,这些决定大多由您决定。例如,您可以通过将UI层与数据访问层分离来创建分层解决方案,或者您可以通过直接从UI页面/视图访问数据库来创建单层解决方案。您可以使用任何库,只要它与ASP.NETCore兼容,并且您可以应用任何架构模式。无个性使ASP.NETCore在不同的场景中变得灵活和可用。但是,所有的这些都需要我们自己去做决策。

我并不是说ASP.NETCore完全没有自己的个性想法。假定您正在构建基于HTTP规范的Web应用程序或API。它清楚地定义了应该如何开发UI和API层。它还提供了一些低级的基础设施组件,例如依赖项注入、缓存和日志记录。但是,它并没有说明您的业务代码是应该要如何组建,应该使用哪些架构模式。

换句话说,ABP框架是一个有个性倾向的框架。它相信软件开发方法本质上可以更好,因此可以引导开发人员在解决方案中使用更佳的架构、模式、工具和库。
尽管ABP框架足够灵活,可以使用不同的工具和库来改变您的架构决策,但当您遵循它的实践原则时,您将获得最大的价值。请别担心,因为它为通用架构提供了良好的、行业认可的解决方案。他的架构规则将节省您的时间,提高您的生产力,并使您专注于您的业务代码而不是基础设施问题。

在接下来,我将介绍ABP所支持的四种基本架构。

领域驱动设计

ABP的主要目标是根据整洁代码原则提供一个模型来构建易维护的解决方案。它提供了一个基于DDD模式和实践的分层架构。它提供了一个分层的启动模板、基础架构以及架构应用指南。

由于ABP是一个软件框架,它专注于DDD的技术实现。本书的第3部分,实现领域驱动设计,解释了使用ABP框架构建基于DDD的最佳实践。

模块化

在软件开发中,模块化是一种拆分系统成独立模块的技术。最终目标是降低复杂性,提高可重用性,使不同的团队能够在不相互影响的情况下能并行处理不同的功能集。

ABP构建模块化有两个主要挑战:

第一个挑战是模块隔离。尽管ASP.NETCore有一些特性(例如Razor组件库)来支持模块化。但是,它仍然非常有限,因为它是一个底层通用的框架,并且仅对UI和API部分支持。另一方面,ABP框架提供了一个一致的模型和基础设施来构建完全隔离的、可重用的应用模块及数据库、领域、应用和UI层。

第二个挑战是模块之间如何通信,使之成为一个统一的应用程序。ABP为模块化系统提供常见的模型,例如在模块之间共享数据库,在模块之间通过事件或API进行通信,以及模块安装。

ABP提供了许多可在任何应用程序中使用的预构建模块。包括身份验证模块,它提供用户、角色和权限管理,同时也提供登录和注册页面。他们基本上都是可重用和自定义的。此外,ABP提供了模块启动模板,帮助您构建可重用的应用程序。这方面的一个例子可以在[第15章]使用模块化中详细介绍。

模块化非常适合管理复杂的大型单体系统。但是,ABP也可以帮助您创建微服务解决方案。

微服务

微服务和分布式架构是构建可扩展系统的公认方法。它允许不同的团队开发不同的服务并独立地对服务进行单独部署和扩展。

但是,构建微服务系统在团队开发、部署、微服务间通信、数据一致性、监控等方面存在一些重要挑战。

微服务系统是一种将不同的学科、方法、技术和工具结合在一起来解决独特问题的解决方案。每个微服务系统都有其要求和限制。每个团队都有一定程度的专业知识、知识和技能。

ABP框架从一开始就被设计为与微服务兼容。它在具有事务支持的微服务之间提供了一个用于异步通信的分布式事件总线。它还提供C#客户端代理来轻松调用远程服务的RESTAPI。

所有预构建的ABP应用模块都经过设计,以便您可以将它们转换为微服务。ABP也提供了详细指南来解释如何创建微服务兼容模块。这样,您可以从模块化单体开始,然后将其转换为微服务解决方案。

ABP团队准备了一个使用ABP框架构建的开源微服务参考方案。它演示了如何使用API网关、微服务间通信、分布式事件、分布式缓存、多个数据库提供程序和多个UI应用程序。它还包括在容器上运行解决方案的Kubernetes和Helm配置。详情参阅。

SaaS/多租户

软件即服务(SaaS)是一种广泛使用的架构模式。以下是多租户系统的典型特征:

在租户之间共享硬件和软件资源。每个租户都有用户、角色和权限。在租户之间隔离数据库、缓存和其他资源。可以启用/禁用每个租户的应用功能。可以为每个租户自定义应用配置。

ABP框架涵盖了所有这些要求甚至更多。它可以帮助您优雅地构建多租户系统,而您几乎感受不到多租户的存在。

到目前为止,我已经介绍了四种ABP基本架构模式。此外,ABP还提供了启动模板来轻松创建新的解决方案。

启动模板

使用ASP.NETCore自带的模板创建新解决方案时,只能获得单个项目,没有分层和依赖关系。您通常会花费大量时间来设置解决方案架构,包括安装工具和做配置基本。

ABP框架提供了一个架构完善、分层清晰、预配置和生产就绪的启动解决方案模板。以下截图显示了当您运行ABP框架创建的启动模板时的初始UI:

下面谈谈这个启动模板:解决方案已经做好逻辑分层。一些预构建模块,例如Account和Identity模块。已经实现了基本的登录、注册、用户和角色管理以及其他一些标准功能。预先配置好的单元测试和集成测试项目。一些管理数据库迁移以及使用HTTPAPI实用工具。

ABP的启动模板带有UI框架和数据库提供者的多个选项。你可以从Angular、Blazor或MVC(RazorPages)选择一个作为UI框架,或者使用EntityFrameworkCore或MongoDB作为数据库提供者。

ABP基础设施

ABP基于您已经了熟悉的工具和库。它没有引入新的对象关系映射器(ORM),而是使用EntityFrameworkCore。同样,它使用Serilog、AutoMapper、IdentityServer和Bootstrap,而不是自己创建类似的功能。它提供了一个解决方案集成了这些工具,并实现了常见的业务应用需求。

ABP框架按照约定简化了异常处理、验证、授权、缓存、审计日志和数据库事务管理,并允许您在需要时进行精细控制。

ABP与IdentityServer很好地集成,基于cookie和令牌的身份验证以及单点登录。它还提供了一个详细的、基于权限的授权系统来帮助您控制应用的权限。

同时也提供了后台作业、BLOB存储、文本模板、审计日志和本地化等常见组件。

在UI部分,ABP提供了完整的UI主题系统,帮助您开发无主题的模块化应用,并轻松为应用程序安装主题。它还在UI方面提供了大量功能和帮助程序,以消除重复代码。

社区

当您在公司中搭建解决方案架构时,除了开发人员没有人会去研究它。然而,ABP拥有一个庞大而活跃的社区。他们使用相同的架构和基础设施,应用类似的最佳实践,并以类似的方式开发他们的应用程序。当您遇到基础架构问题或想要获得解决业务问题的想法或建议时,这具有很大的优势。由于ABP开发人员正在应用相同或相似的模式,因此在另一个解决方案中也更容易理解他人的代码。

ABP框架自2016年以来一直存在并不断发展。截至2021年底,它在GitHub上拥有7,000多颗星、220多位贡献者、22,000多个提交、5,700个已关闭问题,以及在NuGet上超过4,000,000次下载,超过110多个专业和次要版本。我的意思是,它是一个成熟的、被接受的、值得信赖的开源项目。

来自ABP核心团队和社区的贡献者,有着不断持续输出的文章,视频教程和ABP社区网站可供大家学习。

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。