.net6 学习

.net6学习


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

一、异步方法

  1. 异步方法的返回值一般是Task< T >,T是真正的返回值类型.。惯例:异步方法名字一般以Async结尾
  2. 即使方法没有返回值,也最好把返回值的类型声明为非泛型的Task
  3. 调用异步方法时,一般在方法前加上await关,这样拿到的返回值就是泛型指定的T类型
  4. 异步方法的传染性:一个方法中如果有await调用,则这个方法必须修饰为Async
async Task<T> T1Async(){
}

async Task<string> T2Async(){
	await T1Async();
}

如果同样的功能,既有同步方法,也有异步方法,那么首先使用异步方法。
对于不支持的异步方法怎么办? Wait()(无返回值);Result(有返回值)。风险:死锁

static void main(string[] args){
	Task<string> t=File.ReadAllTextAsync(@"e:\temp\a\1.txt");
	string s= t.Result;	
	File.WriteAllTextAsync(@"e:\temp\a\1.txt","aaaaaaaaaaaaaaaaaaa").Wait();
}

lambda表达式调用异步方法

static void main(string[] args){
	ThreadPool.QueueUserWorkItem(async (obj)=>{	
		while(true){
			await File.WriteAllTextAsync(@"e:\temp\a\1.txt","aaaaaaaaaaaaaaaaaaa");
		}
	})
	
}

不写Async、await

public static Task<double> Calc2Async(int n)
{
	return Task.Run(()=>{
	Console.WriteLine("CalcAsync:"+Thread.CurrentThread.ManagedThreadId());
	double result=0;
	Random radom=new Random();
	for(var i=0;i<n*n;i++)
	{
		result+=rand.NextDouble();
	}
	return Task.FromResult(result);
})
}

为什么有的异步方法没有Async

static Task<string> ReadAsync(int num)
{
	if(num==0)
	{
		return File.ReadAllTextAsync(@"e:\temp\a\1.txt");
	}
	else if(numm==2)
	{
		return File.ReadAllTextAsync(@"e:\temp\a\2.txt");
	}
	else
	{
		throw new ArgumentException();
	}
}

优点:只是普通的方法调用,运行效率更高

Async方法缺点

  1. 异步方法会生成一个类,运行效率没有普通方法高
  2. 可能会占用非常多的线程

不要使用Thread.Sleep()

如果想在异步方法中暂停一段时间,不要用Thread.Sleep(),因为它会阻塞调用线程,而要用await Task.Delay();

CancellationToken:用于获得提前终止执行的信号

三种情况使用方法

static async Task Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    cts.CancelAfter(1000);
    //await DownloadAsync2("https://www.baidu.com", 100,cts.Token);
    await DownloadAsync3("https://www.baidu.com", 100,cts.Token);

    
}

static async Task DownloadAsync2(string url,int n,CancellationToken cancellationToken)
{
    using (HttpClient httpClient = new HttpClient())
    {
        for (int i = 0; i < n; i++)
        {
            string html = await httpClient.GetStringAsync(url);
            Console.WriteLine(html);
            //抛出异常方式一
            /*手动处理
            if (cancellationToken.IsCancellationRequested)
            {
            	//自己决定怎么处理
                Console.WriteLine("cancel request");
                break;
            }
            */
            //抛出异常方式二:只是发现异常抛出
            cancellationToken.ThrowIfCancellationRequested();
        }
    }
}

static async Task DownloadAsync3(string url, int n, CancellationToken cancellationToken)
{
    using (HttpClient httpClient = new HttpClient())
    {
        for (int i = 0; i < n; i++)
        {	//由GetAsync()方法来处理异常请求
            var resp=await httpClient.GetAsync(url,cancellationToken);
            string html=await resp.Content.ReadAsStringAsync();
            Console.WriteLine(html);
        }
    }
}

WhenAll

Task类的重要方法:

  1. Task WhenAny(IEnumerable tasks)等,任何一个Task完成,Task就完成
  2. Task<TResult[]> WhenAll(paramasTask[] tasks)等,所有的Task完成,Task才完成。用于等待多个任务执行结束,但是不在乎它们的执行顺序。
  3. FromResult()创建普通数值的Task对象
Task<string> t1=File.ReadAllTextAsync("d:\1.txt");
Task<string> t2=File.ReadAllTextAsync("d:\1.txt");
Task<string> t3=File.ReadAllTextAsync("d:\1.txt");

string[] strs=await Task.WhenAll(t1,t2,t3);

异步与yield

yield return:在迭代中一个一个返回待处理的值

//并非一起返回,而是先返回第一个return的数据,处理完之后再返回下一个return
static IEnumerable<string> Test()
{		
	yield return "hello";
	yield return "Rao";
	yield return "Ao";
}

注意事项:在旧版C#中,async方法中不能用yield。从C#8.0开始,把返回值声明为IAsyncEnumerable(不要带Task),然后遍历的时候用await foreach()即可

//并非一起返回,而是先返回第一个return的数据,处理完之后再返回下一个return
static async Task Main(string[] args)
{
	await foreach(var s in Test())
	{
		
	}
}
static async IEnumerable<string> Test()
{		
	yield return "hello";
	yield return "Rao";
	yield return "Ao";
}

二、LINQ

Linq的简单使用

static void Main(string[] args)
{
    int[] nums = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    IEnumerable<int> result = nums.Where(b => b > 6);
}

手写where方法

static void Main(string[] args)
{
    int[] nums = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    //IEnumerable<int> result = nums.Where(b => b > 6);
    IEnumerable<int> result = MyWhere2(nums, b => b > 6);
    foreach (int i in result)
    {
        Console.WriteLine(i);
    }
}
//手写where方法
static IEnumerable<int> MyWhere1(IEnumerable<int> items, Func<int, bool> f)
{
    List<int> result=new List<int>();
    foreach (int item in items)
    {
        if (f(item) == true) 
        { 
            result.Add(item);    
        }
    }
    return result;
}
//使用yield
static IEnumerable<int> MyWhere2(IEnumerable<int> items, Func<int, bool> f)
{
    List<int> result = new List<int>();
    foreach (int item in items)
    {
        if (f(item) == true)
        {
            yield return item;
        }
    }
    return result;
}

常用扩展方法

static void Main(string[] args)
{
    List<Employee> list = new List<Employee>();
    list.Add(new Employee { Id=1,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=2,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=3,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=4,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=5,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=6,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=7,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=8,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=9,Name="jerry",Age=28,Gender=true,Salary=5000});
    list.Add(new Employee { Id=10,Name="jerry",Age=28,Gender=true,Salary=5000});
}

Where()方法

//Where()方法
    IEnumerable<Employee> items=list.Where(e => e.Age > 20);
    foreach(var i in items)
    {
        Console.WriteLine(i.Id);
    }

list()方法

//获取数据条数
    Console.WriteLine(list.Count());
    //判断条件的Count方法
    Console.WriteLine(list.Count(e => e.Age > 20));
    Console.WriteLine(list.Count(e => e.Salary > 8000 && e.Age > 20));
    //是否有一条数据
    Console.WriteLine(list.Any());
    //是否有一条数据的Salary大于30000
    Console.WriteLine(list.Any(e=>e.Salary>30000));

获取一条数据(是否带参数的两种写法)
Single:有且只有一条满足要求的数据
SingleOrDefault:最多只有一条满足要求的数据
First:至少有一条,返回第一条
FirstOrDefault:返回第一条或者默认值

//Single:有且只有一条满足要求的数据
IEnumerable<Employee> item1 = list.Where(e => e.Name == "jack");
Employee e1 = item1.Single();
//另一种方法
//Employee e2 = list.Where(e => e.Name == "jack").Single();
Employee e2 = list.Single(e => e.Name == "jack");
//SingleOrDefault:最多只有一条满足要求的数据
Employee e3 = list.SingleOrDefault(e => e.Name == "jack");
//First:至少有一条,返回第一条
Employee e4 = list.First(e => e.Name == "jerry");
//FirstOrDefault:返回第一条或者默认值
Employee e4 = list.FirstOrDefault(e => e.Name == "jerry");

排序:
Order():对数据正序排序
OrderByDescending():倒序排序

//Order():对数据正序排序
IEnumerable<Employee> item2 = list.OrderBy(e => e.Id);
//OrderByDescending():倒序排序
IEnumerable<Employee> item3 = list.OrderByDescending(e => e.Id);
//随机排序
var item4 = list.OrderByDescending(e => Guid.NewGuid());
Random rand = new Random();
var item5 = list.OrderByDescending(e => rand.Next());
//先按照年龄排序,相同年龄的再按照工资排序
var item6 = list.OrderBy(e => e.Age).ThenBy(e => e.Salary);
//限制结果集,获取部分数据
//跳过三条数据,再取两条数据
var item7=list.Skip(3).Take(2);

分组:
GroupBy()方法参数是分组条件表达式,返回值为IGrouping<TKey,TSource>类型的泛型IEnumerable,也就是 每一组以一个IGrouping对象的形式返回

//GroupBy
//按照年龄分组
IEnumerable<IGrouping<int, Employee>> item8 = list.GroupBy(e => e.Age);
foreach(IGrouping<int, Employee> group in item8)
{
    Console.WriteLine(group.Key);
    foreach(Employee e in group)
    {
        Console.WriteLine(e);
    }
}

投影:把集合中的每一项转换为另一个类型

IEnumerable<int> ages=list.Select(e => e.Age);

IEnumerable<string> names=list.Select(e => e.Gender?"男":"女");

集合转换

var item9 = list.Where(e => e.Salary > 6000);
List<Employee> list1=item9.ToList();
Employee[] array1 = item9.ToArray();

查询语法

//查询语法
var item10 = list.Where(e=>e.Salary>3000).OrderBy(e=>e.Age)
    .Select(e=>new { e.Age, e.Name, XB = e.Gender ? "男" : "女" });
var item11 = from e in list
             where e.Salary > 5000
             select new { e.Age, e.Name, XB = e.Gender ? "男" : "女" };

Linq解决面试问题

//计算平均值
string s = "52,52,657,9,826,252,65";
string[] strs=s.Split(',');
IEnumerable<int> nums=strs.Select(e => Convert.ToInt32(e));
//double avg=nums.Average();
double avg=s.Split(',').Select(e=> Convert.ToInt32(e)).Average();

//统计一个字符串中每个字母出现的频率(忽略大小写),然后按照从高到低的顺序输出频率高于2次的单词和其出现频率
string s1 = "hello world,soamvpawmdmvalmkempw,c,pc,peq";
s.Where(c => char.IsLetter(c)).Select(c => char.ToLower(c))
    .GroupBy(c => c).Select(g => new { g.Key, Count = g.Count() })
    .OrderByDescending(g => g.Count).Where(g => g.Count > 2);

三、依赖注入

依赖注入的几个概念
服务:对象
注册服务
服务容器:负责管理注册的服务
查询服务:创建对象及关联对象
对象生命周期:Transient(瞬态)、Scoped(范围)、Singleton(单例)

internal class Program
{
    static void Main(string[] args)
    {
        //传统方法
        TestServiceImpl1 service = new TestServiceImpl1();
        service.Name = "Test";
        //service.SayHi();
        //依赖注入
        //创建服务集合
        ServiceCollection service2 = new ServiceCollection();
        //添加服务
        service2.AddTransient<TestServiceImpl1>();
        service2.AddTransient<TestServiceImpl2>();
        //ServiceProvider==服务定位器
        using(ServiceProvider sp = service2.BuildServiceProvider())
        {
            //向服务定位器Get一个服务
            TestServiceImpl1 t1 = sp.GetService<TestServiceImpl1>();
            TestServiceImpl2 t2 = sp.GetService<TestServiceImpl2>();
            t1.Name = "Test1";
            t1.SayHi();
            t2.Name = "Test2";
            t2.SayHi();
        }
        Console.ReadLine();
    }
}
public interface ITestService
{
    public string Name { get; set; }

    public void SayHi();
}

public class TestServiceImpl1 : ITestService
{
    public string Name { get; set; }
    public void SayHi()
    {
        Console.WriteLine("TestServiceImpl1");
    }
}

public class TestServiceImpl2 : ITestService
{
    public string Name { get; set; }
    public void SayHi()
    {
        Console.WriteLine("TestServiceImpl2");
    }
}

服务的生命周期

Transient(瞬态)
通过GetService出来的是两个对象

service2.AddTransient<TestServiceImpl1>();
service2.AddTransient<TestServiceImpl2>();
using(ServiceProvider sp = service2.BuildServiceProvider())
{
    //向服务定位器Get一个服务
    TestServiceImpl1 t1 = sp.GetService<TestServiceImpl1>();
    TestServiceImpl1 t2 = sp.GetService<TestServiceImpl1>();
    Console.WriteLine(object.ReferenceEquals(t1, t2));//False
}

Singleton(单例)
通过GetService出来的是同一个对象

service2.AddSingleton<TestServiceImpl1>();
service2.AddSingleton<TestServiceImpl2>();
using(ServiceProvider sp = service2.BuildServiceProvider())
{
    //向服务定位器Get一个服务
    TestServiceImpl1 t1 = sp.GetService<TestServiceImpl1>();
    TestServiceImpl1 t2 = sp.GetService<TestServiceImpl1>();
    Console.WriteLine(object.ReferenceEquals(t1, t2));//true
}

Scoped(范围)

service2.AddScoped<TestServiceImpl1>();
service2.AddScoped<TestServiceImpl2>();
//ServiceProvider==服务定位器
using(ServiceProvider sp = service2.BuildServiceProvider())
{
    TestServiceImpl1 tt1;
    //using的范围就是scope1的范围
    using (IServiceScope scope1 = sp.CreateScope())
    {
        //在scope中获取Scope相关的对象,scope1.ServiceProvider
        TestServiceImpl1 t1 = scope1.ServiceProvider.GetService<TestServiceImpl1>();
        TestServiceImpl1 t2 = scope1.ServiceProvider.GetService<TestServiceImpl1>();
        Console.WriteLine(object.ReferenceEquals(t1, t2));//true,两个创建的是同一个scope
        tt1 = t1;
    }

    using (IServiceScope scope2 = sp.CreateScope())
    {
        //在scope中获取Scope相关的对象,scope1.ServiceProvider
        TestServiceImpl1 t3 = scope2.ServiceProvider.GetService<TestServiceImpl1>();
        TestServiceImpl1 t4 = scope2.ServiceProvider.GetService<TestServiceImpl1>();
        Console.WriteLine(object.ReferenceEquals(t3, t4));//true,两个创建的是同一个scope
        Console.WriteLine(object.ReferenceEquals(tt1, t4));//False,两个创建的不是同一个scope
    }
}

服务类型与实现类型不一致的方法

ServiceCollection services = new ServiceCollection();
//<TService,TServiceImpl>
//TService:服务的类型 接口
//TServiceImpl:实现的类型 实现类
services.AddScoped<ITestService,TestServiceImpl1>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
    ITestService ts1 = sp.GetService<ITestService>();
    ts1.Name = "Test";
    ts1.SayHi();
}

IServiceProvider的服务定位器方法
T GetService():如果获取不到对象,则返回null
object GetService(Type serviceType)
T GetRequiredService():如果获取不到对象,则抛异常
object GetRequiredService(Type ServiceType)
IEnumerable GetService():适用于可能有很多满足条件的服务
IEnumerable GetService(Type ServiceType)

四、EF Core

EFcore基本使用
EFcore实体配置

五、Web API

ASP.NET Core客户端响应缓存

1.RFC7324是HTTP协议中对缓存进行控制的规范,其中重要的是cache-control这个响应报文头。服务器如果返回contro:max-age-60,则表示服务器指示浏览器端“可以缓存这个响应内容60秒”

在这里插入图片描述

2.我们只要给需要进行缓存控制的控制器的操作方法添加ResponseCacheAttribute,ASP.NET Core会自动添加cache-control报文头

[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
    [ResponseCache(Duration = 20)]
    [HttpGet]
    public DateTime Now()
    {
        return DateTime.Now;
    }
}

ASP.NET Core服务器响应缓存

1.客户端
当有客户端响应缓存后,再次向服务器请求相同的地址,会直接从缓存中读取,但不同的浏览器间的缓存并不共享。
2.服务端
服务端的缓存是由服务器进行缓存,所有浏览器访问同一地址,公用同一个缓存

Response Caching Middleware

1、如果ASP.NETCore中安装了“响应缓存中间件”,那么ASP.NET
Core不仅会继续根据[Responsecache]设置来生成cache-control响应报文头来设置客户端缓存,而且服务器端也会按照[Responsecache]的设置来对响应进行服务器端缓存。
2、“响应缓存中间件”的好处:对于来自不同客户端的相同请求或者不支持客户端缓存的客户端,能降低服务器端的压力。
3、用法:app.MapControllers()之前加上app.UseResponsecaching().请确保app.UseCors()写到app.UseResponsecaching()之前

app.UseAuthorization();
//启用服务器端响应缓存
app.UseResponseCaching();
app.MapControllers();

服务器端响应缓存的缺点

1.恶意请求会造成服务器压力过大当浏览器设置禁用缓存,客户端和服务端缓存都不会缓存,这时如果频繁执行耗时操作,就会服务器压力过大

2.服务端缓存限制
1.只有状态码为 200的Get和Head才会被缓存
2.报文头不能有Authorization、Set-Cookie

3.解决办法
使用内存缓存和分布式缓存

ASP.NET Core中的内存缓存

一、缓存数据类型
缓存放到应用程序的内存中。内存缓存中保存的是一系列的键值对。就像Dictionary类型一样。

二、缓存特性
内存缓存保存在当前运行的网站程序的内存中是和进程相关的。因为在web服务器中,多个网站是运行在不同的进程中的,因此不同进程的缓存不会相互影响。而且网站重启后,内存中的缓存会全部清除。

三、用法
1.启用AddMemoryCache()

builder.Services.AddMemoryCache();

2.注入IMemoryCache接口:方法TryGetValue,Remove,Set,GetOrCreat,GetOrCreatAsync

private readonly IMemoryCache memoryCache;

public TestController(IMemoryCache memoryCache)
{
	this.memoryCache = memoryCache;
}
[HttpGet]
public async ActionResult<Book?> GetBookById(long id)
{
	/*
	Book? result = MyDbContext.GetById(id);
	if(result = null){
		return NotFound($"找不到id={id}的书")
	}
	else{
		return result;
	}*/
	//查询缓存中的数据,如果没有数据,则回调,进行数据库查询,返回给调用者并保存到缓存
	Book? b= await memoryCache.GetOrCreateAsync("Book" + id,async(e)=>{
		return awaitMyDbContext.GetByIdAsync(id);
	})
	if(b==null){
		return NotFound($"找不到id={id}的书");
	}
	else{
		return b;
	}
}

缓存的过期时间策略

缓存的绝对过期时间

1.从缓存保存的那一刻开始多长时间内缓存有效,超过这个时间,缓存失效
2.GetOrCreateAsync()方法的回调方法中有一个ICacheEntry类型的参数,通过对ICacheEntry对当前的缓存项做设置
3.AbsoluteExpirationRelativeToNow 用来设定缓存项的绝对过期时间

[HttpGet]
public async ActionResult<Book?> GetBookById(long id)
{
	/*
	Book? result = MyDbContext.GetById(id);
	if(result = null){
		return NotFound($"找不到id={id}的书")
	}
	else{
		return result;
	}*/
	//查询缓存中的数据,如果没有数据,则回调,进行数据库查询,返回给调用者并保存到缓存
	Book? b= await memoryCache.GetOrCreateAsync("Book" + id,async(e)=>{
		e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);//缓存有效期十秒
		return await MyDbContext.GetByIdAsync(id);
	})
	if(b==null){
		return NotFound($"找不到id={id}的书");
	}
	else{
		return b;
	}
}

缓存的滑动过期时间

1.只要在缓存没过期的时候请求一次,缓存自动续约一段时间
2.ICacheEntry的SlidingExpiration属性用来设定缓存项的滑动过期时间

[HttpGet]
public async ActionResult<Book?> GetBookById(long id)
{
	/*
	Book? result = MyDbContext.GetById(id);
	if(result = null){
		return NotFound($"找不到id={id}的书")
	}
	else{
		return result;
	}*/
	//查询缓存中的数据,如果没有数据,则回调,进行数据库查询,返回给调用者并保存到缓存
	Book? b= await memoryCache.GetOrCreateAsync("Book" + id,async(e)=>{
		e.SlidingExpiration = TimeSpan.FromSeconds(10);//缓存有效期十秒
		return awaitMyDbContext.GetByIdAsync(id);
	})
	if(b==null){
		return NotFound($"找不到id={id}的书");
	}
	else{
		return b;
	}
}

内存缓存的是与非

1.无论用哪种过期时间策略,程序中都会存在缓存数据不一致的情况。
2.可以通过其他机制获取数据源改变的消息,再通过代码调用IMemoryCache的Set方法更新缓存

缓存穿透

分布式缓存

ASP.NET Core默认添加的配置提供者

1.加载现有的IConfiguration
2.加载项目根目录下的appsetting.json
3.加载项目根目录下的appsetting.{Environment}.json
4.当程序运行在开发环境下,程序会加载“用户机密”配置
5.加载环境变量中的配置
6.加载命令行中的配置

ASP.NET Core 配置系统

多层项目中EFCore的使用

Filter

1.Exception Filter

public class MyExceptionFilter : IAsyncExceptionFilter
{
    private readonly IWebHostEnvironment webHostEnvironment;

    public MyExceptionFilter(IWebHostEnvironment webHostEnvironment)
    {
        this.webHostEnvironment = webHostEnvironment;
    }

    public Task OnExceptionAsync(ExceptionContext context)
    {
    	//context.Exception代表异常信息对象
    	//如果context.ExceptionHandled赋值为true,则其他ExceptionFilter不会被执行
    	//context.Result的值会被输出给客户端
        string msg;
        if (webHostEnvironment.IsDevelopment())
        {
            msg = context.Exception.ToString();
        }
        else
        {
            msg = "error";
        }
        ObjectResult result = new ObjectResult(new {code = 500,message = msg});
        context.Result = result;
        context.ExceptionHandled = true;
        context.Result = result;
        return Task.CompletedTask;
    }
}

2.Action Filter

IAsyncActionFilter接口
多个ActionFilter的链式执行

public class MyActionFilter1 : IAsyncActionFilter
{
    /**
     * context:
     * next:一个参数为空,返回值为Task<ActionExecutedContext>类型的委托,代表要执行的下一个ActionFilter
     **/
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        Console.WriteLine("MyActionFilter1 before");
        ActionExecutedContext result = await next();
        if (result.Exception != null)
        {
            Console.WriteLine("MyActionFilter1 after ERROR");
        }
        else
        {
            Console.WriteLine("MyActionFilter1 after success");
        }
    }
}

自动启用事务的ActionFilter

1.数据库事务:要么全部成功、要么全部失败
2.自动化:启动、提交以及回滚事务
3.当一段使用EFCore进行数据库操作的代码放到TransactionScope声明的范围中的时候,这段代码就会自动被标记为“支持事务”
4.TransactionScope实现了IDisposable接口,如果一个TransactionScope的对象没有调用Complete()就执行了Dispose()方法,则事务会被回滚,否则事务就会被提交
5.TransactionScope还支持嵌套式事务
6 .NET Core中的TransactionScope不像.NET FX一样由MSDTCL()分布式事务提升的问题。请使用最终一致性事务

 public class DemoController : ControllerBase
 {
     private MyDbContext ctx;
     [HttpPost]
     public string Test1()
     {
         using (TransactionScope tx = new TransactionScope())
         {
             ctx.Books.Add(new Book { Name = "sss", Price = 1 });
             ctx.SaveChanges();//一个事务
             ctx.Persons.Add(new Book { Name = "sss", Age = 1 });
             ctx.SaveChanges();//一个事务
             tx.Complete();
             return "ok";
         }
     }

     [HttpPost]
     public async Task<string> Test1()
     {
         using (TransactionScope tx = new TransactionScope(
             TransactionScopeAsyncFlowOption.Enabled))
         {
             ctx.Books.Add(new Book { Name = "sss", Price = 1 });
             await ctx.SaveChangesAsync();//一个事务
             ctx.Persons.Add(new Book { Name = "sss", Age = 1 });
             await ctx.SaveChangesAsync();//一个事务
             tx.Complete();
             return "ok";
         }
     }
 }

asp.net core ActionFilter 进行 所有接口 访问限速

中间件

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    //app.MapGet("/", () => "Hello World!");
    app.Map("/test", async (pipeBuilder) =>
    {
        pipeBuilder.Use(async (context, next) => {
            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync("1 start<br/>");
            await next.Invoke();
            await context.Response.WriteAsync("1 end<br/>");
        });
        pipeBuilder.Use(async (context, next) => {
            await context.Response.WriteAsync("2 start<br/>");
            await next.Invoke();
            await context.Response.WriteAsync("2 end<br/>");
        });
        pipeBuilder.Run(async context =>
        {
            await context.Response.WriteAsync("Run<br/>");
        });
    });
    app.Run();
}

简单的自定义中间件

1.如果中间件的代码比较复杂,或者我们需要复用使用一个中间件的话,我们最好把中间件的代码放到单独的中间件类中
2.中间件类是一个普通的.NET类,它不需要继承任何父类或者实现接口,但是这个类需要有一个构造方法,构造方法中至少要有一个RequestDelegate类型的参数,这个参数用来指向下一个中间件。这个类还需要定义一个名字为Invoke的或者InvokeAsync的方法,方法至少有一个HttpContext类型的参数,方法的返回值必须是Task类型。中间件类的构造方法和Invoke方法还可以定义其他参数,其他参数的值会通过依赖注入自动赋值

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    //app.MapGet("/", () => "Hello World!");
    app.Map("/test", async (pipeBuilder) =>
    {
        pipeBuilder.UseMiddleware<CheckMiddleware>();
        pipeBuilder.Use(async (context, next) => {
            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync("1 start<br/>");
            await next.Invoke();
            await context.Response.WriteAsync("1 end<br/>");
        });
        pipeBuilder.Use(async (context, next) => {
            await context.Response.WriteAsync("2 start<br/>");
            await next.Invoke();
            await context.Response.WriteAsync("2 end<br/>");
        });
        pipeBuilder.UseMiddleware<Test1Meddleware>();
        pipeBuilder.Run(async context =>
        {
            await context.Response.WriteAsync("Run<br/>");
            dynamic? ojb = context.Items["BodyJson"];
            if (ojb!= null)
            {
                await context.Response.WriteAsync($"{ojb}<br/>");
            }
        });
    });
    app.Run();
}

编写MarkDown渲染中间件

public class MarkDownMiddleware
{
    private readonly RequestDelegate next;
    private readonly IWebHostEnvironment hotEnv;
    public MarkDownMiddleware(RequestDelegate next,IWebHostEnvironment hotEnv) {
        this.next = next; 
        this.hotEnv = hotEnv;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        string path = context.Request.Path.ToString();
        if (path.EndsWith(".md", true, null))//只处理.md文件请求
        {
            await next.Invoke(context);
            return;
        }
        var file = hotEnv.WebRootFileProvider.GetFileInfo(path);
        if(!file.Exists)//是否存在
        {
            await next.Invoke(context);
            return;
        }
        using var stream = file.CreateReadStream();
        //探测文本的的编码类型
        Ude.CharsetDetector detector = new Ude.CharsetDetector();
        detector.Feed(stream);
        detector.DataEnd();//从头读到尾
        string charset = detector.Charset??"UTF-8";
        stream.Position = 0;//复位
        //读取md文件
        using StreamReader reader = new StreamReader(stream,Encoding.GetEncoding(charset));
        string mdText = await reader.ReadToEndAsync();

        //markdown文本转换html
        Markdown markdown = new Markdown();
        string html = markdown.Transform(mdText);

        context.Response.ContentType = "text/html;charset=UTF-8";
        await context.Response.WriteAsync(html);
    }
}

Filter与Middleware的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值