.Net Core骨灰级教程

Dot Net Core 进阶教程


前言

.NET Core 是一个开源、跨平台的框架,用于构建现代 Web 应用、微服务、物联网 (IoT) 应用和云服务。它由 Microsoft 开发,并于 2016 年正式发布。.NET Core 是 .NET Framework 的一个轻量级、模块化版本,旨在提供更高的性能和更好的跨平台支持。


NutGet

NuGet 是 .NET 生态系统中的一个包管理器,用于管理和分发 .NET 库和工具。类似于java的Maven,python的pip,Node.js中的npm

NutGet官网

使用

  • 首先我们选择要下载的依赖,这里随便举个Pomelo.EntityFrameworkCore.MySql 3.2.6版本例子
    在这里插入图片描述

VSCode傻瓜下载

  • 第一步找到界面
    在这里插入图片描述
  • 搜索,选择项目版本下载
    在这里插入图片描述
  • 可以看到我们下载好的包了,如果要卸载点击卸载即可,或者删掉ItemGroup中的代码
    在这里插入图片描述

程序包管理器

  • 打开管理器
    在这里插入图片描述

  • 复制下载/卸载命令执行
    在这里插入图片描述

异步

异步和多线程的区别

这里举一个例子-服务员点餐,当有多个服务员,这多个服务员给用户菜单,当用户点完菜服务员才可以走,多个服务员就是多线程。可以看到单个用户的点餐效率并没有改变。 而异步就是,这个服务员给完用户菜单就可以给别的用户菜单,而不必等待。

async,await介绍

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

  • eg
    在这里插入图片描述

      static async Task downUrlContent(string url,string filename)
            {
                //由于HttpClient实现了IDisposable接口,需要using关闭资源
                using (HttpClient client = new HttpClient())
                {
                    //获取请求的内容
                   string content = await client.GetStringAsync(url);
                    //保存本地
                   await File.WriteAllTextAsync(filename, content);
                }
            }
    

原理

  • 总结:async的方法会被C#编译器编译成一个类,会主要根据awaiti调用
    进行切分为多个状态,对async方法的调用会被拆分为对MoveNext的调
    用,用await看似是“等待”,经过编译后,其实没有“wait”。
  • 注意:异步不等于多线程使用async不会new 一个新的线程来执行,如需要就要使用多线程了,例如Task.run();等等

甩手

当然我们可能会看到没有使用async和await搭配使用的api,其实这种是直接‘甩手‘的,有利于提高性能

  • 正常eg

     static async Task<string> doSelectNum(int i)
        {
            switch (i)
            {
                case 1:
                    string s = await File.ReadAllTextAsync(@"E:\1.txt");
                    return s;
                case 2:
                    string y= await File.ReadAllTextAsync(@"E:\20241030.log");
                    return y;
            }
            return "";
        }
    
  • '甩手’eg 由于返回的都是Task,我们直接返回不做拆分之前我们说的原理,由于async反编译是封装了一个类,然后分了几个代码端执行。只甩手Task,不“拆完了再装反编译上面的代码:只是普通的方法调用这样运行效率更高,不会造成线程浪费。

       static Task<string> doSelectNum(int i)
            {
                switch (i)
                {
                    case 1:
                        return File.ReadAllTextAsync(@"E:\1.txt");
                       
                    case 2:
                      return File.ReadAllTextAsync(@"E:\20241030.log");
    
                }
                throw new Exception();
            }
    

CancellationToken

有时需要提前终止任务,比如:请求超时、用户取消请
求,很多异步方法都有CancellationToken参数,用于
获得提前终止执行的信号。

  • 示例

    public static async Task cancelToken1Async2(string url, int n,CancellationToken token)
            {
                using (HttpClient client = new HttpClient())
                {
                    for (int i = 0; i < n; i++)
                    {
                        string readUrlContent = await client.GetStringAsync(url);
                        Console.WriteLine(readUrlContent);
                        //如果达到token设定的值
                        if (token.IsCancellationRequested)
                        {
                            Console.WriteLine("凌冬将至");
                            break;
                        }
                    }
                }
            }
    

    这里我们可以构建一个CancellationToken,CancellationToken中我们可以设置执行终止的时间,例如:
    CancellationTokenSource cs = new CancellationTokenSource();cs.CancelAfter(1000);//1s 后终止;
    await cancelToken1Async2(url, 100, cs.Token);//传入token

举一反三

当再web中,用户在点击完请求之后,关闭浏览器,而服务器还在运行,这就浪费不必要的资源,而CancellationToken可以帮我们识别这一点,自动关闭服务器资源,所以只需要传递CancellationToken来保证这点。

public async Task<IActionResult> Index(CancellationToken token)
        {
            await cancelToken1Async2(@"https://www.baidu.com", 1000, token);//传递
            return View();
        }
        public static async Task cancelToken1Async2(string url, int n, CancellationToken token)
        {
            using (HttpClient client = new HttpClient())
            {
                for (int i = 0; i < n; i++)
                {
                    var readUrlContent = await client.GetAsync(url, token);//另一种形式
                    string html = await readUrlContent.Content.ReadAsStringAsync();
                    Debug.WriteLine(html);
                    如果达到token设定的值
                    //if (token.IsCancellationRequested)
                    //{
                    //    Console.WriteLine("凌冬将至");
                    //    break;
                    //}
                }
            }
        }
  • 这里用了另一种形式来获取html,之前的demo也行

异步其他问题

异步不用sleep

  • 因为sleep会阻塞线程,而异步本质是在运行时可以运行其他内容

    使用 await Task.Delay();

WhenAll

Task类的重要方法:
1.Task<Task>WhenAny(IEnumerable<Task> tasks)等,任何一个Task完成,Task就完成
2.Task<TResult[]>WhenAll<TResult>(paramTask<TResult>:[]tasks)等,所有Task完成,Task才完成。用于等待多个任务执行结束但是不在乎它们的执行顺序。
3.FromResult)创建普通数值的Task对象

  • 当有多个异步任务时,await 是等待异步执行完再执行下面的,但是我们需要多个异步任务不需要等待,可以同时执行,可以用when all来获取几个异步的返回

计算路径文件字符

public async Task<IActionResult> WenAllIndex()
        {
            string url = @"C:\Users\xiaohh\Desktop\上传文件\testread";
            //获取路径下的所有文件
            string[] allFilePaths = Directory.GetFiles(url, "*.*", SearchOption.AllDirectories);
            Task<int>[] task = new Task<int>[allFilePaths.Length];
            for(int i = 0; i < task.Length; i++)
            {
                task[i] = readSingleLength(allFilePaths[i]);
            }
            int[] countChar = await Task.WhenAll(task);//获取各个任务的结果
            int len = countChar.Sum();
            ViewBag.Len = len;
            return View();
        }
        /// <summary>
        /// 计算文件的字符数
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<int> readSingleLength(string url)
        {
            var result =  await System.IO.File.ReadAllTextAsync(url);
            return result.Length;
        }
  • 每个文件用异步读取字符数

LINQ

委托

1.委托是可以指向方法的类型,调用委托变量时执行的就是变量指向的方法
2.NET中定义了泛型委托Action(无返回值)和Func(有返回值),所以一般不用自定义委托类型。

  • 什么意思呢?

    通常我们定义一个变量 int x =1//那么这个x就是指向int类型的变量
    委托同理,委托就是指向方法的变量,FunM:为你定义的方法名,deM为你定义的委托
    deM m = FunM;此时你就可以使用变量调用方法了,m()
    
  • 例子

    在这里插入图片描述

  • 我们可以不用定义委托,使用Func或者Action

     			Func<string> f1 = Func1M;
                //public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
                Func<int,int,int> f2 = Func2M;//前两个参数,后一个返回值
                ViewBag.F1M = f1();//调用方法
                ViewBag.F2M = f2(1, 4);
                return View();
    

Lambda表达式

知道了委托,那么Lambda表达式就不难理解了,Lambda表达式就是将方法匿名,没有方法名,例如上面的表达式我们可以用Lambda

         Func<int,int,int> f2 = Func2M;//前两个参数,后一个返回值
            Func<int, int, int> f22 = delegate (int x, int y)
            {
                return x + y;
            };
            Func<int, int, int> f222 = (int x, int y) => { return x + y; };

上面三种形式都是一样的

linq的用法和sql语句很类似,和java stream流的过滤也类似,所以自己搜教程去学习

  • eg

     		List<Person> list =PersonUtil.getPersons();
                //按年龄分组,获取最大值,然后将年龄和组别的最大值映射成一个对象
               var gList=list.GroupBy(e => e.Age).Select(e => new
                {
                    MyKey=e.Key,
                    Max = e.Sum(e=>e.Age)
                }) ;
                foreach(var item in gList)
                {
                    Console.WriteLine($"{item.MyKey}-{item.Max}");
                }
    
  • eg2

    //统计频次大于2的字符,并按频次顺序排列
                string str = "aflajdflk2jiedsljflkjfalsjfeoifjdsalkfa";
                //根据字符分组,然后构建字符,个数对象
                var dic = str.GroupBy(c => c).Select(c => new
                {
                    letter = c.Key,
                    cont = c.Count()
                }).OrderByDescending(x=>x.cont).ToDictionary(g => g.letter, g => g.cont);
                foreach(var pair in dic)
                {
                    Console.WriteLine(pair.Key+"======"+pair.Value);
    
                }
    

依赖注入(DI)

依赖注入(Dependency Injection,简称 DI)是一种设计模式,用于减少代码之间的耦合度,提高代码的可测试性和可维护性。通过依赖注入,对象的依赖关系不再由对象自身创建或管理,而是由外部提供给对象。例如:本来你要自己做饭,现在雇佣了一个名厨师,想要什么菜告诉他就行了,你等着吃。

DI几个概念

服务(service):对象;
注册服务;
服务容器:负责管理注册的服务;(管理对象)
查询服务:创建对象及关联对象;(从容器中获取对象)
对象生命周期:Transient(瞬态:获取的对象都是new);Scoped
(范围:在范围之内都是同一个对象);Singleton(单例:都是同一个对象)

NET中使用DI

NET中使用DI
1、测试代码见备注
2、根据类型来获取和注册服务
可以分别指定服务类型(service type)和实现类
型(implementation type)。这两者可能相同,
也可能不同。服务类型可以是类,也可以是接口,
建议面向接口编程,更灵活。
3、.NET控制反转组件取名为Dependencylnjection,
但它包含ServiceLocator的功能。

生命周期

1、给类构造函数中打印,看看不同生命周期的对象创建
使用serviceProvider…CreateScope()创建Scope。
2、如果一个类实现了IDisposable:接口,则离开作用域之后
容器会自动调用对象的Dispose方法
3、不要在长生命周期的对象中引用比它短的生命周期的对
象。在ASP.NET Core中,这样做默认会抛异常
4、生命周期的选择:如果类无状态,建议为Singleton;如
果类有状态,且有Scope控制,建议为Scoped,因为通常这
种Scope控制下的代码都是运行在同一个线程中的,没有并
发修改的问题;在使用Transient的时候要谨慎。
5、.NET注册服务的重载方法很多,看着文档琢磨吧

步骤(服务定位器)
  • Install-Package Microsoft.Extensions.DependencyInjection

  • using Microsoft.Extensions.DependencyInjection;

  • ServiceCollection用来构造容器对象IServiceProvider。调用ServiceCollection的BuildServiceProvider来获取对象

  • 简单来说就是,创建容器,放入容器,获取对象

    using ConsoleApp1;
    using Microsoft.Extensions.DependencyInjection;
    
    ServiceCollection services = new ServiceCollection();
    //services.AddTransient<Person>(); //瞬态,每次生成不同对象
    //services.AddSingleton<Person>();//单例,每次都是一个对象
    services.AddScoped<Person>();//在scope范围内生成的是一个对象
    using(ServiceProvider provider = services.BuildServiceProvider())
    {
       
        var person = provider.GetService<Person>();
        person.Name = "zhangsan";
        person.say();
    
        var person2 = provider.GetService<Person>();
        person2.Name = "lisi";
        person2.say();
    
        var isTure = Object.ReferenceEquals(person, person2);
        Console.WriteLine(isTure); //true
    
        Person person3;
        using (IServiceScope scope = provider.CreateScope())
        {
             person3 = scope.ServiceProvider.GetService<Person>();
            person.Name = "zhangsan";
            person.say();
    
            var person4 = scope.ServiceProvider.GetService<Person>();
            person2.Name = "lisi";
            person2.say();
    
            var isTure2 = Object.ReferenceEquals(person3, person4);
            Console.WriteLine(isTure2);//true 同一范围
        }
    
       var isNotArea =  Object.ReferenceEquals(person, person3);
        Console.WriteLine(isNotArea);//false  不同范围
    
    }
    
    
    
  • 实现接口重写方法

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        internal class Person:IPerson,IDisposable
        {
            public string Name { get; set; }
    
            public void Dispose()
            {
                Console.WriteLine("过了using域就会执行");
            }
    
            public void say()
            {
                Console.WriteLine("say Hello Person===>"+Name);
            }
        }
    }
    
    
  • 输出

    在这里插入图片描述

步骤(DI)
  • C#依赖注入是根据对象的构造函数来创建赋值的

    using Microsoft.Extensions.DependencyInjection;
    using System;
    
    ServiceCollection services = new ServiceCollection();
    //加入容器
    services.AddTransient<ILog, Log>();//指明服务是ILog,实现类是Log
    services.AddTransient<Controller>();
    using (ServiceProvider provider = services.BuildServiceProvider())
    {
        //根据构造函数自动注入ILog
        var c = provider.GetService<Controller>();
    
        c.toJinx();
    }
    class Controller
    {
        readonly ILog log;
    
        public Controller(ILog log)
        {
            this.log = log;
        }
    
        public void toJinx()
        {
            log.Log("下载文件");
        }
    }
     interface ILog
    {
        void Log(string msg);
    }
    class Log : ILog
    {
        void ILog.Log(string msg)
        {
            Console.WriteLine("记录日志===>"+msg);
        }
    }
    
    
    

配置系统

Json配置

1、创建一个json文件,文件名随意,比如config.json,
设置“如果较新则复制”。参考图片
在这里插入图片描述

2、NuGet:安装Microsoft.Extensions.Configuration和
Microsoft.Extensions.Configuration.Json
3、编写代码,先用简单的方式读取配置。
在这里插入图片描述

ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("config.json",optional:true,reloadOnChange:true);
IConfigurationRoot configurationRoot = configurationBuilder.Build();
string wangzhe = configurationRoot["GAME"];
string douyi = configurationRoot.GetSection("APP:douyi").Value;
Console.WriteLine(wangzhe+douyi);

EFCORE

ORM

  • 什么是ORM

    以对象的形式进行数据库的读取,ORM是负责程序和数据库之间转换的中间层
    1、EF Core是对于底层DO.NET Core的封装,因此
    DO.NET Core支持的数据库不一定被EF Core支持。
    2、EF Core支持所有主流的数据库,包括MIS SQL ServerOracle、MySQL、PostgreSQL、SQLite等。可以自己实现Provider支持其他数据库。
    3、对于SQLServer支持最完美,MIySQL、PostgreSQL也不错(有能解决的小坑)。这三者是NET圈中用的最多的个。本课程主要用SQLServer讲,如果用其他数据库只要改一行代码+绕一些小坑即可,大部分代码用法不变。EFCore能尽量屏蔽底层数据库差异。

框架搭建

  • 安装包:Install-Package Microsoft.EntityFrameworkCore.SqlServer

  • 新建实体类Book

  • 新建配置类BookConfig

      class BookConfig : IEntityTypeConfiguration<Book>
        {
            public void Configure(EntityTypeBuilder<Book> builder)
            {
                 builder.ToTable("T_Books");//映射的表
            }
        }
    
  • 新建MyDbContext

    class MyDBContext:DbContext
        {
            public DbSet<Book> books;
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                base.OnConfiguring(optionsBuilder);
                optionsBuilder.UseSqlServer("你的数据库连接字符串");
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
            }
        }
    
  • 使用(数据插入为例)

    static void Main(string[] args)
            {
                Console.WriteLine("Hello World!");
                using(var mycont = new MyDBContext())
                {
                    Book book = new Book();
                    book.Name = "钢铁是怎样练成的";
                    mycont.books.Add(book);
                    mycont.SaveChanges();//保存更改
                }
            }
    
  • 其他操作可自行搜索

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彩虹海!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值