ASP.NET Core(一)

ASP.NET Core框架中 依赖注入 LINQ语句 配置系统 日志

ASP.NET core

异步

  • Task

  • async

  • await

委托

  • 关键字 delegate

  • 指向方法的类型

  • .NET中内置了两个泛型委托 Action(无返回值)Func(有返回值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//无返回值
namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
Action a1 = F1;
a1();

Action<int> a_int = F_int;
a_int(520);

}
static void F1()
{
Console.WriteLine("hello world!");
}
static void F_int(int a)
{
Console.WriteLine(a);
}
}

//有返回值,与上述类似,但是Func的参数列表中最后一个记录的是返回值
Func<int> F1 = function;
F1();
int function(){
return 520;
}

Lambda 表达式

1
2
3
4
5
6
7
8
9
namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
Action<int, int> a1 = (int i, int j) => { Console.WriteLine("hello world!"); };
a1(1, 2);
}
}

yeild

yield关键字用于简化实现迭代器的方法。迭代器是一种特殊的方法,它可以逐步返回集合中的元素,而不是一次性返回整个集合。使用yield可以让方法在每次调用时返回一个元素,并在需要时保持其当前状态,以便下次调用时继续执行。

LINQ

几乎里面的所有的扩展方法都是针对 IEnumerable接口的

所有能返回集合的都是返回IEnumerable

处于System.Linq命名空间中

常用方法

  • Where() 筛选出符合条件的数据

  • Single() 有且只有一条数据时返回

  • SingleOrDefault()

  • First():至少有一条时返回第一条

  • FirstOrDefault()

  • Order() 正序排序

  • OrderByDescending() 倒序排序

  • OrderBy() 按照自定义要求排序

  • ThenBy()

  • Skip() Take()

聚合函数

  • Max() Min() Average() Sum() Count()

分组函数

  • GroupBy() 根据选择的属性进行分组

返回值IEnumerable<IGrouping<T,Y>>

投影

按照某种规则将一组数据映射为另一组数据

  • Select

集合转换

  • ToList()

  • ToArray()

控制反转

1.服务定位器(ServiceLocator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp1;
class MainClass
{
public interface ITestService
{
public string? Name { get; set; }
public void SayHi();
}
public class TestServiceImpl : ITestService
{
public string? Name { get; set; }
public void SayHi()
{
Console.WriteLine($"Hi!My name is {Name}");
}
}
public static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();//创建服务容器
services.AddTransient<TestServiceImpl>();//注册服务
using (ServiceProvider sp = services.BuildServiceProvider())//创建服务定位器
{//使用了服务定位器 获取存放在容器中所需要的服务
TestServiceImpl t = sp.GetService<TestServiceImpl>();
t.Name = "WangMC";
t.SayHi();
}
}

}

2.依赖注入(Dependency Injection)

.NET控制反转组件取名为DependencyInjection,但其中包含服务定位器

使用前要安装Microsoft.Extensions.DependencyInjection

  • 服务(service):要使用的对象

  • 注册服务

  • 服务容器:存放和管理注册好的服务

  • 查询服务:创建对象及关联对象

  • 对象的生命周期:Transient(瞬态) Scoped(范围) Singleton(单例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp1;
//控制器发出测试请求,开始调用日志单元和存储单元
//存储单元需要调用配置信息连接存储设备,最后将测试的信息存储到设备中

class MainClass
{
public static void Main(string[] args)
{
var service = new ServiceCollection();

service.AddScoped<Controller>();
service.AddScoped<IStorage,StorageImp1>();
service.AddScoped<ILog,LogImp1>();
service.AddScoped<IConfig,ConfigImp1>();

using (var sp = service.BuildServiceProvider())
{
var controller = sp.GetService<Controller>();
controller.Test();
}
}

class Controller
{
private readonly ILog log;
private readonly IStorage storage;

public Controller(ILog log, IStorage storage)
{
this.storage = storage;
this.log = log;
}

public void Test()
{
this.log.Log("开始上传");
this.storage.Save("依赖注入测试", "1.txt");
this.log.Log("上传完毕");
}

}
interface ILog
{
public void Log(string msg);
}

class LogImp1 : ILog
{
public void Log(string msg)
{
Console.WriteLine($"日志{msg}");
}
}

interface IConfig
{
public string GetValue(string name);
}

class ConfigImp1 : IConfig
{
public string GetValue(string name)
{
return "\"hello_world\"";
}
}

interface IStorage
{
public void Save(string content, string name);
}

class StorageImp1 : IStorage
{
private readonly IConfig config;

public StorageImp1(IConfig config)
{
this.config = config;
}
public void Save(string content, string name)
{
string service = config.GetValue("service");
Console.WriteLine($"向服务器{service}上传文件名为{name},其内容为{content}");
}
}
}

配置系统

配置读取顺序

配置中心服务器---->本地环境变量------>本地配置文件 (后者可将前者覆盖)

Json文件配置

1
2
3
4
5
{
"name": "WangMC",
"age": "18",
"hobby": { "sport": "swimming", "music": "rock" }
}

属性设置为 如果较新则复制

法一: NuGet安装Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Channels;

namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
var configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
var configRoot = configBuilder.Build();

string name = configRoot["name"];
string hobby_sport = configRoot.GetSection("hobby")["sport"];

Console.WriteLine($"Name: {name}, Age: {age}, Hobby_sport: {hobby_sport}");
}
}

法二: 绑定一个类 来进行配置的读取

NuGet安装Microsoft.Extensions.Configuration.Binder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Channels;

namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
var configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("config.json", optional: true, reloadOnChange: true);
var configRoot = configBuilder.Build();

var config = configRoot.Get<Config>();
string name = config.name;
int age = config.age;
Hobby hobby = config.hobby;
string sprot = config.hobby.sport;
}
class Config
{
public string name { get; set; }
public int age { get; set; }
public Hobby hobby { get; set; }
}
class Hobby
{
public string sport { get; set; }
public string music { get; set; }
}
}

选项方式读取

  1. NuGet安装Microsoft.Extensions.Configuration Microsoft.Extensions.Configuration.Json包

Microsoft.Extensions.Configuration.Options Microsoft.Extensions.Configuration.Binder

  1. 读取配置时, DI要声明IOptions IOptionsMonitor IOptionsSnapshot等类型

  • IOptions不会读取到新的值

  • ==IOptionsSnapshot==与IOptionsMonitor 相比,IOptionsSnapshot读取到的值会在同一个范围中保持一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddScoped<TestController>();

var configBuild = new ConfigurationBuilder();
configBuild.AddJsonFile("config.json",optional:true,reloadOnChange:true);

var configRoot = configBuild.Build();
services.AddOptions().Configure<Config>(e => configRoot.Bind(e));

using (var sp = services.BuildServiceProvider())
{
int i = 4;
while (i!=0)
{
var info = sp.GetRequiredService<TestController>();
info.Test();
i--;
}
}


}
class TestController
{
private readonly IOptionsSnapshot<Config> optConfig;

public TestController(IOptionsSnapshot<Config> optConfig)
{
this.optConfig=optConfig;
}

public void Test()
{
Console.WriteLine($"姓名:{optConfig.Value.name}");
Console.WriteLine($"年龄:{optConfig.Value.age}");
Console.WriteLine($"兴趣爱好:{optConfig.Value.hobby.sport}");
Console.WriteLine($"兴趣爱好:{optConfig.Value.hobby.music}");
}
}
class Config
{
public string name { get; set; }
public int age { get; set; }
public Hobby hobby { get; set; }
}
class Hobby
{
public string sport { get; set; }
public string music { get; set; }
}
}

日志

级别: Trace<Debug<Information<Warning<Error<Critical

1. 输出到控制台

安装Microsoft.Extensions.Logging Microsoft.Extensions.Logging.Console

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console

namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
var services = new ServiceCollection();

services.AddLogging(logBuilder => {
logBuilder.AddConsole();
logBuilder.SetMinimumLevel(LogLevel.Trace);
});
services.AddScoped<Demo>();

using (var sp = services.BuildServiceProvider())
{
var demo = sp.GetRequiredService<Demo>();
demo.Test();
}
}
class Demo
{
private readonly ILogger<Demo> logger;
public Demo(ILogger<Demo> logger)
{
this.logger = logger;
}
public void Test()
{
logger.LogWarning("this is a warning");
logger.LogError("this is a Error");
try
{
File.ReadAllText("A fake document");
logger.LogDebug("Read Success!!");
}
catch(Exception ex)
{
logger.LogDebug(ex,"Read Failed!!");
}
}
}

}

2. 文本日志

使用NLog包

日志过滤和分类

1
2
3
<target xsi:type="File" name="logfile" fileName="c:\temp\console-example.log"
archiveAboveSize="10000" maxArchiveFiles="3" maxArchiveDays="30"
layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
  • xsi:type="File":指定了目标的类型为File,这意味着日志消息将被写入到文件中。

  • name="logfile":为目标命名,这个名字在整个NLog配置中必须是唯一的,并且可以在日志规则中引用。

  • fileName="c:\temp\console-example.log":指定了日志文件的完整路径。日志消息将被写入到这个文件中。

  • archiveAboveSize="10000":这是一个可选参数,用于指定日志文件的大小阈值。当日志文件达到或超过这个大小时,NLog会将当前日志文件归档,并创建一个新的日志文件继续记录。单位是字节,这里设置为10000字节(大约10KB)。

  • maxArchiveFiles="3":这是一个可选参数,用于指定最大归档文件的数量。当达到这个数量时,NLog将开始删除最旧的归档文件,以便为新归档文件腾出空间。这里设置为3,意味着最多保留3个归档文件。

  • maxArchiveDays="30":其中 X 是一个整数,表示归档文件将被保留的天数。当归档文件创建后经过 X 天,NLog 将自动删除这些归档文件

  • layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}":定义了日志消息的布局(layout)。布局是一个模板,它指定了日志消息的格式。以下是布局中各个元素的含义:

    • ${longdate}:插入日志事件的时间戳。
    • ${level}:插入日志事件的级别(例如:Info、Debug、Error等)。
    • ${message}:插入日志事件的文本消息。
    • ${all-event-properties}:插入所有事件属性的内容。
    • ${exception:format=tostring}:如果日志事件中包含异常信息,则将其转换为字符串并插入到日志消息中。

3.结构化日志

使用Serilog包 安装Serilog.AspNetCore包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Formatting.Json;

namespace ConsoleApp1;
class MainClass
{
public static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();

services.AddLogging(logBuilder =>
{
Log.Logger = new LoggerConfiguration().
MinimumLevel.Debug().
Enrich.FromLogContext().
WriteTo.Console(new JsonFormatter()).
CreateLogger();
services.AddSerilog();
});
//services.AddSerilog();
services.AddScoped<Demo>();
services.AddScoped<Demo1>();

using (var sp = services.BuildServiceProvider())
{
var demo = sp.GetRequiredService<Demo>();
var demo1 = sp.GetRequiredService<Demo1>();

demo1.Test();
demo.Test();

}
}
class Demo
{
private readonly ILogger<Demo> logger;
public Demo(ILogger<Demo> logger)
{
this.logger = logger;
}
public void Test()
{
logger.LogWarning("this is a warning");
logger.LogError("this is a Error");
logger.LogDebug("this is a debug");

var Information = new { name = "WangMC", Age = 18 };
string Hobby = "running";
logger.LogDebug("Hello this is my informaiton {@Information},my hobby is {Hobby}", Information, Hobby);
}
}


class Demo1
{
private readonly ILogger<Demo1> logger;
public Demo1(ILogger<Demo1> logger)
{
this.logger = logger;
}
public void Test()
{
logger.LogWarning("这是一个警告");
logger.LogError("这是一个错误");
logger.LogDebug("这是一个debug");
}
}

}

4.集中化日志

适合集群化部署 有N多服务器

Exceptionless 提供了现成的云服务 也可以本地部署(self hosting)