.NET开发:第三方框架与组件笔记

文章发布时间:

最后更新时间:

文章总字数:
3.2k

预计阅读时间:
13 分钟

摘要

本文为自用.NET相关第三方框架与组件笔记。
知识点内容根据各个教程收集整理。
所有教程版权归各自作者所有。
这里只做临时记录,对于笔记相对完整的框架/组件,将单独分离出来形成独立文档。

JQuery

示例

$(this).hide():隐藏当前的 HTML 元素。

$("#test").hide():隐藏 id="test" 的元素。

$("p").hide():隐藏所有<p>元素。

$(".test").hide():隐藏所有 class="test" 的元素。

语法

jQuery 语法是为 HTML 元素的选取编制的,可以对元素执行某些操作。

基础语法是:$(selector).action()

  • 美元符号定义 jQuery
  • 选择符(selector)“查询”和“查找” HTML 元素
  • jQuery 的 action() 执行对元素的操作

文档就绪函数

1
2
3
$(document).ready(function(){
// jQuery functions go here
});

这是为了防止文档在完全加载(就绪)之前运行 jQuery 代码。
如果在文档没有完全加载之前就运行函数,操作可能失败。下面是两个具体的例子:

  • 试图隐藏一个不存在的元素
  • 获得未完全加载的图像的大小

选择器

元素选择器

jQuery 使用 CSS 选择器来选取 HTML 元素。
$("p") 选取 <p> 元素。
$("p.intro") 选取所有 class="intro" 的 <p> 元素。
$("p#demo") 选取所有 id="demo" 的 <p> 元素。

属性选择器

jQuery 使用 XPath 表达式来选择带有给定属性的元素。
$("[href]") 选取所有带有href属性的元素。
$("[href='#']") 选取所有带有 href 值等于 "#" 的元素。
$("[href!='#']") 选取所有带有 href 值不等于 "#" 的元素。
$("[href$='.jpg']") 选取所有 href 值以 ".jpg" 结尾的元素。

以上详见jQuery 教程

jQuery ajax - ajax() 方法

见jQuery ajax - ajax() 方法

SeriLog

Serilog是 .net 下的新兴的日志框架

示例

包Serilog是Log核心库,Serilog.Sinks.Console是Log的控制台输出库,这个也是日志框架的一贯策略,一个核心库加多个输出库组合使用,这样可以保持良好的扩展性。

1
2
3
4
5
6
7
using (var log = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger())
{
log.Information("Hello, Serilog!");
log.Warning("Goodbye, Serilog.");
}

LoggerConfiguration

这里用了一个LoggerConfiguration对象,它主要用于创建和设置Log对象,类似于Nlog里面的LogManager类。这里主要用它两个方法:

WriteTo:WriteTo属性用来设置日志的输出,Serilog将其称为Sink(水槽),还是比较形象的。
CreateLogger:用于创建一个ILogger类型的Logger对象.

ILogger

ILogger对象用于记录日志,和其他日志框架差不多。Serilog日志级别分为如下5级

Verbose,
Debug,
Information,
Warning,
Error,
Fatal,

全局Logger对象

在实际的使用过程中,往往并不是每次使用都去创建一个ILogger,一种方式是通过依赖注入的方式创建一个全局的Logger。不过这种方式需要引入DI框架。在小程序中使用不算方便。

另一种方式是直接使用静态的Log类,它也携带了写入日志的方法,用起来非常方便。

1
2
3
Log.Warning("warning");
Log.Error("err");
Log.Fatal("fatal");

不过Log类之前,首先必须给它关联一个ILogger。

1
2
3
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();

SqlSugar

基本查询

详见基本查询

事务用法

1、单库事务
单库事务是针一个db操作执行的事务,无论是 ISqlSugarClient和 SqlSugarClient 用法都一样

1
2
3
4
5
6
7
8
9
10
11
12
try
{
db.Ado.BeginTran();
db.Insertable(new Order() { .....}).ExecuteCommand();
db.Insertable(new Order() { .....}).ExecuteCommand();
db.Ado.CommitTran();
}
catch (Exception ex)
{
db.Ado.RollbackTran();
throw ex;
}

如果一个db就一个库,那么你也可以用多租户事务节约代码,因为2者在一个库的情况下作用一样

1
2
3
4
db.BeginTran();//去掉了.ado
db.CommitTran();//去掉了.ado
db.RollbackTran();//去掉了.ado
//ISqlSugarClient 接口使用多租户事务 看文档2.2

来源详见事务用法

UnityContainer

IoC容器

注册实现

1
2
3
4
5
6
7
8
public static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<ITest, Test1>();

ITest testInstance1 = unityContainer.Resolve<ITest>();
testInstance1.SayHi();
}

注册多个不同名称实现

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void Main(string[] args)
{
UnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<ITest, Test1>("type1");
unityContainer.RegisterType<ITest, Test2>("type2");

ITest testInstance1 = unityContainer.Resolve<ITest>("type1");
ITest testInstance2 = unityContainer.Resolve<ITest>("type2");

testInstance1.SayHi();
testInstance2.SayHi();
}

生命周期

TransientLifetimeManager - 为每次请求生成新的类型对象实例。 (默认行为)
ContainerControlledLifetimeManager - 实现 Singleton 对象实例。 当容器被 Disposed 后,对象实例也被 Disposed。
HierarchicalifetimeManager - 实现 Singleton 对象实例。但子容器并不共享父容器实例,而是创建针对字容器的 Singleton 对象实例。当容器被 Disposed 后,对象实例也被 Disposed。
ExternallyControlledLifetimeManager - 实现 Singleton 对象实例,但容器仅持有该对象的弱引用(WeakReference),所以该对象的生存期由外部引用控制。
PerThreadLifetimeManager - 为每个线程生成 Singleton 的对象实例,通过 ThreadStatic 实现。
PerResolveLifetimeManager - 实现与 TransientLifetimeManager 类似的行为,为每次请求生成新的类型对象实例。不同之处在于对象实例在 BuildUp 过程中是可被重用的。

Prism Core : Event Aggregator

  1. 事件聚合器
    Prism 提供了一种机制,可以实现应用程序中松散耦合组件之间的通信。这种机制基于事件聚合器服务,允许发布者和订阅者通过事件进行通信,并且彼此之间仍然没有直接引用。
    事件聚合器提供多播发布/订阅功能。这意味着可以有多个发布者引发相同的事件,并且可以有多个订阅者监听相同的事件。
    通过事件聚合器服务可以使用IEventAggregator接口获取到事件聚合器。事件聚合器负责定位或构建事件,并在系统中保存事件的集合。首次访问一个事件时,如果该事件尚未构建,则构建。

  2. 事件类型
    PubSubEvent是Prism中对积累EventBase的唯一实现。此类维护订阅者列表并向订阅者发送事件。PubSubEvent是一个泛型类,在使用时需要指定具体类型以进行特化。
    一般是通过继承该类,特化一个自定义事件类型

  3. 发布
    发布者通过事件聚合器服务获取到EventAggregator,并调用Publish方法来触发事件。

  4. 订阅
    订阅者通过事件聚合器服务获取到EventAggregator,并调用Subscribe方法进行注册。之后,注册的事件被触发是,通过参数指定委托进行相应。

来源详见【Prism 8】事件聚合器(Event Aggregator)

步骤

  1. 继承PubSubEvent<T>创建自定义事件类型
  2. 创建事件发送者
  3. 创建事件接收者,并订阅指定的事件

示例

首先定义 PubSubEvent:

1
public class TestEvent:PubSubEvent<string>{}

发布方和订阅方都通过 EventAggregator 获得 PubSubEvent。

1
2
3
4
5
6
7
8
public class MainPageViewModel
{
IEventAggregator _eventAggregator;
public MainPageViewModel(IEventAggregator ea)
{
_eventAggregator = ea;
}
}

发送方需要通过 GetEvent 拿到 PubSubEvent,使用Publish()发布消息:

1
_eventAggregator.GetEvent<TestEvent>().Publish("=_=");

订阅方使用这些消息,可以通过 Subscribe() 订阅事件:

1
2
3
4
5
6
7
8
9
10
11
12
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
ea.GetEvent<TestEvent>().Subscribe(PrintLn);
}

void PrintLn(string s)
{
Console.WriteLine(s); //这里写触发后实现逻辑
}
}

来源详见[Windows] Prism 8.0 入门(上):Prism.Core

SignalR

.NET Core平台对于WebSocket的封装

示例

后端:实现Hub

1
2
3
4
5
6
7
8
9
10
11
using Microsoft.AspNetCore.SignalR;

namespace TestHub{
class MyHub:Hub{
public Task SendPublicMsg(string msg){
string connId = this.Context.ConnctionId;//获取连接ID
string msgToSend = $"{connId} {DateTime.Now}:{msg}";//组装发回去的信息
return this.Clients.All.SendAsync("PublicMsgReceived",msgToSend);//把收到的信息加点东西再发还回去
}
}
}

配置:

SrPic01

配置中心(Hub)

在Program.cs中调用AddSignalR注册中心(Hub)所需的 SignalR 服务

1
2
3
4
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddSignalR();//调用AddSignalR

使用MapHub配置端点(Endpoint):

1
2
3
4
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");//配置端点

app.Run();

创建和使用中心

创建一个继承Hub的类,并添加若干public方法使得客户端可以使用

1
2
3
4
5
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}

向客户端发送消息

向客户端发还信息需要Client对象:

  • 使用Clients.All向所有客户端发送消息
  • 使用Clients.Caller向Caller发还消息
  • 使用Group("...")向特定组发送消息
1
2
3
4
5
6
7
8
9
10
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);//所有客户端

public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);//调用者

public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);//SignalR Users组

//都使用字符串"ReceiveMessage"调用客户端方法

或避免字符串误拼写使用强类型中心Hub:

1
2
3
4
5
6
7
8
9
10
11
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);

public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);

public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}

客户端方法需要提前提取:

1
2
3
4
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}

强类型指定可以让编译器参与检查避免字符串误拼写
使用强类型 Hub<T> 会禁止使用 SendAsync。

从客户端请求结果

非强类型

在中心(Hub)方法中调用Client(...) 或 CallerClients 属性:

1
2
3
4
5
6
7
8
9
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}

第二种方法是在IHubContext<T>的实例上调用Client(...):

1
2
3
4
5
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}

强类型

强类型中心可以通过接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IClient
{
Task<string> GetMessage();
}

public class ChatHub : Hub<IClient>
{
public async Task<string> WaitForMessage(string connectionId)
{
string message = await Clients.Client(connectionId).GetMessage();
return message;
}
}

客户端

客户端使用On()返回结果

.NET参考:

1
2
3
4
5
6
hubConnection.On("GetMessage", async () =>
{
Console.WriteLine("Enter message:");
var message = await Console.In.ReadLineAsync();
return message;
});

来源详见使用适用于 ASP.NET Core 的 SignalR 中的中心

Autofac

Autofac是第三方IOC容器,功能强大,比asp.netcore内置容器强大得多,支持属性注入和方法注入,支持AOP。

通过容器创建对象实例:

1
2
3
4
ContainerBuilder containerBuilder = new ContainerBuilder();//创建一个容器Builder
containerBuilder.RegisterType<Honer>();//注册一个普通类
IContainer container = containerBuilder.Build();//build一下,得到一个容器
Honer phone = container.Resolve<Honer>()//可以基于容器来获取对象的实例

注册类型

普通类

通过 containerBuilder.RegisterType<>() 进行注册

1
2
3
4
ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Honer>();
IContainer container = containerBuilder.Build();
Honer phone = container.Resolve<Honer>();

抽象以及实现

通过 containerBuilder.RegisterType<>().As<>()

1
2
3
4
ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<Honer>().As<IPhone>();
IContainer container = containerBuilder.Build();
IPhone phone = container.Resolve<IPhone>();

注册程序集

RegisterAssemblyTypes(程序集数组),程序集必须是public的
AsImplementedInterfaces():表示注册的类型,以接口的方式注册
PropertiesAutowired():支持属性注入
Where:满足条件类型注册

1
2
3
4
5
6
7
8
9
10
var basePath = AppContext.BaseDirectory;
var dll = Path.Combine(basePath, "MyAutofac.dll");
ContainerBuilder containerBuilder = new ContainerBuilder();
var assemblysServices = Assembly.LoadFrom(dll);
containerBuilder.RegisterAssemblyTypes(assemblysServices)
.Where(t => !t.Name.EndsWith("XXX"))
.AsImplementedInterfaces()
.PropertiesAutowired();
IContainer container = containerBuilder.Build();
ITeacher teacher = container.Resolve<ITeacher>();

注入方式

构造函数注入

默认选择参数最多的构造函数

1
containerBuilder.RegisterType<Teacher>().As<ITeacher>();

全部属性注入

使用PropertiesAutowired(),对象所有属性全部注入

1
containerBuilder.RegisterType<Teacher>().As<ITeacher>().PropertiesAutowired();

标记特性的属性注入

使用PropertiesAutowired,和特性选择器CustomPropertySelector

1
2
3
4
5
6
7
8
9
10
11
containerBuilder.RegisterType<Teacher>().As<ITeacher>().PropertiesAutowired(new CustomPropertySelector());

//自定义特性选择器
public class CustomPropertySelector : IPropertySelector
{
public bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
var flag = propertyInfo.CustomAttributes.Any(it => it.AttributeType == typeof(SelectPropAttribute));
return flag;
}
}

方法注入

使用OnActivated,指定调用方法

1
2
3
4
5
containerBuilder.RegisterType<Teacher>().As<ITeacher>()
.OnActivated(p =>
{
p.Instance.SetStudent1(p.Context.Resolve<IStudent>());
});

生命周期

生命周期 名称 说明
InstancePerDependency 瞬时生命周期 每次获取都是全新的实例,默认生命周期
SingleInstance 单例生命周期 同一个进程内都是同一个实例
InstancePerLifetimeScope 作用域生命周期 同一个作用域内都是同一个实例
InstancePerMatchingLifetimeScope 作用域范围生命周期 在作用域范围里面,同一个作用域下面的对象是同一个;在作用域范围外无法创建实例
InstancePerRequest 一次请求一个对象 只能在Web中使用,在控制台中使用会报错

使用:

1
containerBuilder.RegisterType<Honer>().As<IPhone>().InstancePerDependency();