当前位置:首页 » 《关于电脑》 » 正文

C#和WPF入门教程

2 人参与  2024年04月03日 12:36  分类 : 《关于电脑》  评论

点击全文阅读


目录

0. 来点鸡汤1. 概念1.1 C#能做什么1.2 为什么要选择C#,而不是QT或者其它?1.3 winform 和 wpf有什么区别1.4 .net Framework 和 .net Core联系1.5 WPF各个组成部分 2. xaml2.1 xaml中的对象和属性2.2 xaml页面布局2.2.1 层级概念2.2.2 使用 Grid 定义行和列2.2.3 设置行和列 2.3 xaml样式2.3.1 方法一:不给样式命名2.3.2 方法二:给样式命名2.3.3 给样式命名同时继承基础样式 2.4 在资源字典定义样式2.4.1 添加资源字典2.4.2 全局引用资源字典 2.5 控件模板重写 3. C# 代码语法规则3.1 变量 、属性、字段分别是什么?3.2 属性、变量、字段变量(Variables):字段(Fields):属性(Properties):总结区别:易错点 3.3 set{}、get{}用法3.4 App.config用法3.5 将一个类拆开写在多处 4. 数据绑定4.1 原理4.2 xaml 实现数据绑定案例一案例二 4.3 C#代码实现数据绑定4.4 PropertyChanged实现数据绑定(1)原理(2) 界面(3) xaml(4) c# 5 c#中的委托5.1 如何理解委托?5.2 系统自带的两种委托 Action<> 和Func<>5.3 简写委托的形式 C#中的事件Lambda表达式MVVMWPF 定时器配置文件读取UI线程辨识attribute和property 单例:加深理解静态成员和方法LINQ的用法LINQ中where用法和原理LINQ常用扩展方法split方法可能会掉入的陷阱通过Linq读取配置文件linq常见问题 依赖注入依赖注入 : 接口 + 实现直接从类中注入通过服务容器依赖注入 依赖注入:通过构造函数 配置读取配置容器步骤实例:数据源绑定单个类 注册服务步骤实例:直接通过lambda表达式对类配置实例:通过addOption配置 扁平化配置 日志系统日志级别日志记录到控制台核心代码:完整代码 日志记录到文本: NLog官网查看例程Nlog核心代码实例 SeriLog: 结构化日志 Entity Framework Core通过C#代码创建表通过C#对表进行怎删改查插入数据/查询数据删除数据/更新数据 FluenAPI和DataAnnotation区别Guid用法Hi/Lo算法Migration的常用命令反向工程EF core查询执行sql语句EF Core映射Mysql对应关系: 关系配置在任何一方一对多: 关系配置在多端一对多:关系配置在一端 EFCore性能单向导航

0. 来点鸡汤

时不时看看自己以前写的博客,感触很多。已经快一年多没有认真写博客了,今天重新开张,希望以此为契机,重拾生活的信心。
去年研究生毕业,去了北京,年薪拿到了30万。但只在山巅呆了四个月,便草草结束,离开北京各中原由无从说起,家庭和事业那时候只能选一个。虽然选了家庭,但现在想起北京的那份工作,也觉得可惜。离开北京之后,“安安心心”拿5000的月薪又快一年了,岗位也从原来的算法工程师变成程序员,这就是上帝给你开了个窗户,你刚看到一丝希望,谁知上帝开窗就给你一个大逼兜,然后关上窗户,留下你在风中凌乱。刚去北京时,我以为命运的齿轮开始转动,我真正的人生正式开始,原来只是去看了一遭那水中花、镜中月,留存脑海中的北京梦,与现在的满地鸡毛,讽刺啊。

哈哈哈,生活还要继续呢!原来京东买东西,现在拼多多、咸鱼也能凑合,玩算法换成写代码就当给自己夯实基础,领导原来是博士、硕士,现在的领导清一色中专,这不工作上更容易忽悠领导了吗?这样想,好多了。

听我叨逼叨那么多,哈哈,那我顺便说点关于C#的事情吧,也当做我成长的一个记录。这份教程不会每一步都去截图,如果你是小白,建议花个10分钟快速入门一下C#的基本概念,这个链接: C#菜鸟教程 带你快速入门。我写的虽然是WPF入门教程,但是C#遇到的问题我都会记录下来,直到把整个大楼给造好。

1. 概念

C# 怎么读? 读C井?读c星?哈哈
正确读音 C Sharp 音标: [ʃɑːrp]

1.1 C#能做什么

上位机软件、桌面显示软件、unity 3D游戏、网页开发等

1.2 为什么要选择C#,而不是QT或者其它?

(1)C# 简单易上手。qt 基本就C++的语法,用起来很复杂。
别扯什么运行速度,内存那些有的没的,那些东西全是扯犊子,对于新手或者绝大多数人,那些东西可能写一辈子代码也不用考虑,现在的计算机不缺算力和存储空间。主要精力应该是保证功能的实现和稳定运行。
(2)C# 是微软创造出来的,背靠宇宙第一强编辑器 visual studio,对于代码的调试,兼容,有着无可比拟的优势。
我举个例子,每台Windows电脑都有个事件查看器,它记录了电脑的各种异常事件。我们知道,写代码的时间是远远没有调试的时间长的,而用C#写的程序,通过Windows自带的事件查看器就能定位到异常代码是第几行,你就说这点,选不选C#。
(3)学会C# 会的是一类东西。
比如你是用C#写桌面应用程序(winform、WPF),你还可以用C#写网页 (asp.net),现在火热的Unity3D脚本也是通过C#来完成的,只要微软不跨,你说为啥不选一劳永逸的语言。

老子就不听傻逼博主的意见,我就要学qt。
宝啊,你看看我写的其他文章,也是鬼话连篇,但是我写博客没有糊弄各位宝,no copy,no paste.(是不是不知道啥意思,哈哈,快去百度翻译一下再和我犟)。如果你还是不选择C#,我只能画个圈诅咒你 —你写的代码如果有bug,永远也找不到。

1.3 winform 和 wpf有什么区别

C#有控制台应用程序Console,也有桌面应用程序(图形界面),现在主要就是用来展示数据的。
c#有两种方式写桌面应用程序:WPF、winform。我们来看看它们有什么不同。
在这里插入图片描述

winform老,wpf新;winform窗体的控件属性是在C#里实现的,WPF则是在XAML里面实现的winform修改控件复杂,wpf简单winform入门简单,wpf入门难

宝啊,你读到此处想说什么?
什么傻逼博主,说了好像又啥都没说,都是些什么鬼啊?
我就想和你说,winform过时了,要学WPF。

1.4 .net Framework 和 .net Core联系

讲个小插曲,有一次其他部门的一个同事台上发言,原话:“我们这个程序是用 dot net core 开发的”,听到这句话时,我有点懵逼,什么人这是,你就说用C#开发的不就完了么,还tmd左一句dot net core,右一句dot net core,啥也不是。
在这里插入图片描述
看上面这张图片,我选的都是WPF,但是它们的架构不一样。

.net Framework老, .net Core新.net Framework只针对windows平台,但包含了Windows平台的所有特性, .net Core 支持多个平台,但没有前者全面.如果要用xp系统,则使用framework是更好的选择

顺便说一嘴:咱可不是喜欢背后说人坏话,我批评甚至是鄙视我那个说dot net core 的同事,原因是他不写任何代码,也不会写,却总是装逼,咱讨厌这样的人。

1.5 WPF各个组成部分

在这里插入图片描述

分为XAML文件和cs文件,XAML文件用来处理界面,cs文件处理后台逻辑
App.xaml 指定系统启动界面,资源,引入的程序集

=======现在是 北京时间2023年9月27日 23:13,小傲娇的博主困了,不想写了

2. xaml

wpf有两部分构成,一部分是界面(前端设计),负责界面的设计和数据展示。另一部分是程序逻辑(后端),负责业务流程和数据处理。两部分相互独立,装逼的说法叫前后端分离。前端和后端会存在数据交互,可以通过事件或者绑定等方法来实现。

2.1 xaml中的对象和属性

=======现在是 北京时间2023年10月1日 20:41,小傲娇的博主今天没事,接着写。

xaml是xml的扩展,很多用法和想xml是一致的,如果你学过html,那应该会很快入门xaml,这东西只要入门了,写桌面应用程序布局时很爽,你用了这个,基本不会再用回winform了。

对象元素
这里的对象和面向对象编程的概念基本一致。面向对象编程的对象是对一类具有相同属性的物体进行抽象,比如有个Dog类、CAT类,这里的对象是逻辑层面上的。
xaml 中也有对象,比如按钮utton、文本框TextBox等都是对象,这里的对象是布局上的概念,更多是文法层面,不是逻辑上的概念。
在WPF中,xmal代码也称作前端代码(控件),而以.cs结尾的代码叫做 “C#代码”(也叫后端代码或者代码隐藏)。
<!--第一种写法--><Button>按钮1</Button><!--第二种写法--><Button Content="按钮2"/>  
属性
在C#中(其他编程语言也一样),一个类中往往有各种属性,比如学生类中有姓名属性、年纪属性等等。在xaml中,这个概念有点类似,比如有个按钮控件(按钮类),那它也有自己的属性,比如行高、行宽、背景颜色等等。下面这个示例,Grid.Row、Grid.Column都是属性。
<Grid Grid.Row="2" Grid.Column="0" ShowGridLines="True">
属性元素
属性元素是对象中的属性的属性,比如Button是一个对象,background是一个属性,对属性再设置就叫属性元素: SolidColorBrush
<Button>    <Button.Background >        <SolidColorBrush Color="red"></SolidColorBrush>    </Button.Background></Button>

2.2 xaml页面布局

2.2.1 层级概念

xaml设计是按照行和列的概念设计的;xaml是树形结构,在使用前先对Grid进行设计(几行几列);在每个层级下面对界面进行设计
在这里插入图片描述
可能看了上面的图片也觉得一头雾水,我们现在从网页上随便截图一张,结合WPF看看到底什么是层级结构
在这里插入图片描述
我们把这个界面先整体分成三个区域, 即3行1列
第1行第1列 所有的图标依次用stackPanel放置控件就可以了
在第2行第1列 里面进一步切割,可在分成 2行1列
在这里插入图片描述
在第3行第1列 里面进一步切割,可在分成 1行3列
在这里插入图片描述
这样一个界面就被切割好了, 理解上面这个例子,应该就知道WPF层级的概念了

2.2.2 使用 Grid 定义行和列

根据上面层级的概念,将界面划分区域,接下来就可以用< Grid >来实现了. 下面的代码定义2行2列的布局

<Grid>    <Grid.RowDefinitions>        <RowDefinition Height="20"/>        <RowDefinition/>              </Grid.RowDefinitions>        <Grid.ColumnDefinitions>        <ColumnDefinition/>        <ColumnDefinition/>    </Grid.ColumnDefinitions></Grid>

我们还可以在第2行第2列里再布置2行2列,代码实现如下

<Grid>        <Grid.RowDefinitions>            <RowDefinition/>            <RowDefinition/>        </Grid.RowDefinitions>        <Grid.ColumnDefinitions>            <ColumnDefinition/>            <ColumnDefinition/>        </Grid.ColumnDefinitions>        <Grid Grid.Row="1" Grid.Column="1">            <Grid.RowDefinitions>                <RowDefinition/>                <RowDefinition/>            </Grid.RowDefinitions>            <Grid.ColumnDefinitions>                <ColumnDefinition/>                <ColumnDefinition/>            </Grid.ColumnDefinitions>        </Grid>    </Grid>

在这里插入图片描述

2.2.3 设置行和列

绝对值

按比例

auto

<Grid ShowGridLines="True">           <Grid.RowDefinitions>          <RowDefinition Height="auto"/>          <RowDefinition Height="80"/>          <RowDefinition Height="2*"/>      </Grid.RowDefinitions>      <Grid.ColumnDefinitions>          <ColumnDefinition/>          <ColumnDefinition/>          <ColumnDefinition/>      </Grid.ColumnDefinitions>      <Button Grid.Row="0" Grid.Column="0" Width="40" Height="40"/>      <Button Grid.Row="1" Grid.Column="2"/>  </Grid>

2.3 xaml样式

2.3.1 方法一:不给样式命名

直接在wpf控件前面写某类控件的样式,该方法不给样式起名字,后面使用该类控件时不需要引用,默认会使用该类样式。如下面代码,当我创建一个按钮是就会使用 <Window.Resources>里面的样式。

<Window x:Class="Wpf_test.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        xmlns:local="clr-namespace:Wpf_test"        mc:Ignorable="d"        Title="MainWindow" Height="500" Width="700">    <Window.Resources>        <Style TargetType="Button">            <Setter Property="Background" Value="#FF20B9E6"/>            <Setter Property="Height" Value="50"/>            <Setter Property="Width" Value="100"/>        </Style>    </Window.Resources>    <Grid>        <Grid.RowDefinitions>            <RowDefinition/>            <RowDefinition/>        </Grid.RowDefinitions>        <Grid.ColumnDefinitions>            <ColumnDefinition/>            <ColumnDefinition/>        </Grid.ColumnDefinitions>        <Button Content="按钮"/>    </Grid></Window>

=======现在是 北京时间2023年10月1日 22:38,小傲娇的博主累了,不想写了

2.3.2 方法二:给样式命名

<Window.Resources>    <Style x:Key="login" TargetType="Button">        <Setter Property="Background" Value="#FF06C5F0"/>        <Setter Property="Height" Value="50"/>        <Setter Property="Width" Value="100"/>    </Style>    <Style x:Key="quit" TargetType="Button">        <Setter Property="Background" Value="#FFF1F1F1"/>        <Setter Property="Height" Value="50"/>        <Setter Property="Width" Value="100"/>    </Style></Window.Resources>

2.3.3 给样式命名同时继承基础样式

<Window.Resources>    <!--基础样式-->    <Style TargetType="Button">        <Setter Property="FontSize" Value="20"/>    </Style>    <Style x:Key="login" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">        <Setter Property="Background" Value="#FF06C5F0"/>    </Style>    <!--继承基础样式 添加引用-->    <Style x:Key="quit" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">        <Setter Property="Background" Value="#FFF1F1F1"/>    </Style></Window.Resources>

2.4 在资源字典定义样式

在项目管理文件中添加一个资源字典Dictionary,对不同的控件进行样式设计. 在App.xaml中添加一个全局的资源字典, 将Dictionary的文件路径添加进去

2.4.1 添加资源字典

//在项目管理文件中添加一个资源字典Dictionary<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                    xmlns:local="clr-namespace:WpfApp1">    <!--新建一个全局资源字典-->        <!--基础样式-->    <Style TargetType="Button">        <Setter Property="FontSize" Value="20"/>    </Style>    <!--继承基础样式 添加引用-->    <Style x:Key="login" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">        <Setter Property="Background" Value="#FF06C5F0"/>    </Style>    <!--继承基础样式 添加引用-->    <Style x:Key="quit" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">        <Setter Property="Background" Value="#FFF1F1F1"/>    </Style></ResourceDictionary>

2.4.2 全局引用资源字典

<Application x:Class="WpfApp1.App"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"             xmlns:local="clr-namespace:WpfApp1"             StartupUri="Window1.xaml">    <Application.Resources>        <!--添加一个全局的资源资源字典-->        <ResourceDictionary>            <ResourceDictionary.MergedDictionaries>                <ResourceDictionary Source="/BaseButtonStyle.xaml"/>            </ResourceDictionary.MergedDictionaries>        </ResourceDictionary>    </Application.Resources></Application>

2.5 控件模板重写

当你创建一个控件时,这个控件有自己的背景、颜色,这都是系统自定义好的,但是这并不是我们需要的,我们不可能每一次创建时都去修改,所以需要对控件模版改造,这个过程就就叫做控件模版重写。

<Grid>    <Button Content="自定义按钮" Background="#FF00805D" Width="120" Height="100" BorderBrush="Black" BorderThickness="4">        <Button.Template>            <ControlTemplate TargetType="Button">                <Border Name="bbb" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="15">                    <TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>                </Border>                <ControlTemplate.Triggers>                    <Trigger Property="IsMouseOver" Value="True">                        <Setter TargetName="bbb" Property="Background" Value="red"/>                    </Trigger>                    <Trigger Property="IsPressed" Value="True">                        <Setter TargetName="bbb" Property="Background" Value="#FF10F3CA"/>                    </Trigger>                </ControlTemplate.Triggers>            </ControlTemplate>        </Button.Template>    </Button> </Grid>

3. C# 代码语法规则

3.1 变量 、属性、字段分别是什么?

这个概念特别重要,搞不懂不行
类内部的私有变量即为字段,如代码中的变量 a;
属性向外暴露接口(公有部分),使外部能够通过属性访问内部字段, 属性本身不保存数据, 对属性操作实际是对属性对应字段操作; 如代码中对属性b操作,其实是对a操作;

/// <summary>/// 定义一个变量(字段) a ,同时初始化/// </summary>private int a = 1;/// <summary>/// 定义属性 b/// </summary>public int b{    get { return a; }    set { a = value; }}

3.2 属性、变量、字段

在.NET中,属性(Properties)、字段(Fields)和变量(Variables)是编程中经常使用的术语,它们各自有不同的含义和用途。以下是这些术语的基本定义和区别:

变量(Variables):

定义:变量是用来存储数据的标识符,它可以根据程序的需要存储不同类型的数据。访问:通过变量名直接访问。作用域:根据声明的位置(如方法内、类内、命名空间内等),变量的作用域会有所不同。示例:int number = 10;,这里number是一个整型变量。

字段(Fields):

定义:字段是类的成员变量,它隶属于类而不是方法。字段通常是私有的,并且可以通过类的属性、方法或其他成员来访问。访问:通常通过类的实例访问,除非它们是静态的,则可以通过类名直接访问。作用域:字段的作用域通常是整个类。示例:
public class MyClass{    private int myField; // 这是一个私有字段        public int MyProperty // 这是一个属性,下面会详细介绍    {        get { return myField; }        set { myField = value; }    }}

属性(Properties):

定义:属性是类的成员,它提供了一种灵活的方式来读取、写入或计算私有字段的值。属性通常包含get和set访问器。访问:通过类的实例访问,就像访问字段一样,但实际上是在调用方法。作用域:属性的作用域通常是整个类,但可以通过get和set访问器进行更精细的控制。示例:继续上面的MyClass例子,MyProperty是一个属性,它有get和set访问器,允许外部代码读取和修改myField的值。
public int MyProperty // 属性定义{    get { return myField; } // get访问器,用于读取属性值    set { myField = value; } // set访问器,用于设置属性值}

总结区别:

变量是最基本的存储单元,而字段是类的变量成员。属性提供了一种更安全、更灵活的方式来访问类的字段。它们允许在读取或写入字段值之前执行额外的逻辑(例如验证)。通常建议将字段标记为private,并通过公共属性来访问它们,以实现封装和数据隐藏。这样可以确保对象状态的完整性和安全性。

易错点

在C#中, 只能在类内定义变量和属性,同时允许对改变量或者属性进行初始化, 但不允许一个变量直接引用另外一个变量,如下所示,这是新人经常犯的一个错误。
在这里插入图片描述

3.3 set{}、get{}用法

在类Test中设置一个私有变量name,同时设置一个公有变量,通过公有变量name对私有变量NAME操作

    public class Test    {        /// <summary>        /// 字段:一般私有,不对外开放, 首字母一般小写        /// </summary>        private string name;        /// <summary>        /// 属性:一般为共有,作为外部访问对应字段的一个接口, 首字母一般大写        /// </summary>        public string NAME        {            get { return name; }    //通过NAME返回name的值            set            {                if (value == "keson")                {                    Console.WriteLine("hello,keson!");                }                else                {                                       name = value; //通过NAME设置name的值                    Console.WriteLine("that is not keson,is "+ name);                }                }        }    }    class Program    {        static void Main()        {            Test test = new Test();            test.NAME = "孙悟空";        }    }

3.4 App.config用法

App.config文件是系统默认的配置文件, 使用时需要添加引用 System.Configuration.dll, 该配置文件用于修改数据库连接字符串/窗口日志的信息.

App.config代码:

<?xml version="1.0" encoding="utf-8" ?><configuration>    <startup>         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />    </startup>    <appSettings>    <add key="keyName" value="valueName"/>    <add key="keyName2" value="valueName2"/>    <add key="keyName3" value="valueName3"/>  </appSettings></configuration>

C# 代码

拿到配置文件里的内容

    public partial class MainWindow : Window    {        public MainWindow()        {            InitializeComponent();            string settingValue = System.Configuration.ConfigurationManager.AppSettings["keyName"];        }    }

3.5 将一个类拆开写在多处

calss1.cs和class2.cs同属于一个类,c#允许一个类拆开写,这样防止写在一处,不方便阅读。

 public partial class Window1 : Window    {        public Window1()        {            InitializeComponent();        }    }public partial class Window1 : Window    {        public Window1()        {            fun();        }    }                      

4. 数据绑定

4.1 原理

通常是指将目标源(具有依赖属性的对象)绑定到 目标数据上(通常是控件)

数据绑定分为4种:
在这里插入图片描述

4.2 xaml 实现数据绑定

案例一

绑定源:代码隐藏 绑定目标: TextBox

    public partial class MainWindow : Window    {                public MainWindow()        {            InitializeComponent();                        //设置窗体上下文对象   这里设置的是MyClass            DataContext = new MyClass();        }    }    public class MyClass    {        public int hight { get; set; } = 100;        public int width { get; set; } = 101;    }
<Grid>    <StackPanel>         <WrapPanel>             <TextBlock Text="窗口的标题为:"/>             <TextBox Text="{Binding Path=Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />         </WrapPanel>         <WrapPanel>             <TextBlock Text="窗口的高度为:"/>             <!--上面MyClass里的hight-->             <TextBox Text="{Binding hight}" Width="50" />           </WrapPanel>         <WrapPanel>             <TextBlock Text="窗口的宽度为:"/>             <TextBox Text="{Binding width}" Width="50" />         </WrapPanel>     </StackPanel></Grid>

案例二

绑定源:TextBox 绑定目标: TextBox

<TextBox Grid.Row="1" Name="textBox_receiveData" Text={BindingPath=Text,ElementName=tbx2 }"></TextBox>

4.3 C#代码实现数据绑定

public MainWindow(){   InitializeComponent();   BindingData();}private void BindingData(){   //1 创建绑定对象   var binding = new Binding("Text");   //2 设置绑定源   binding.Source = this.keson;   //3 设置绑定目标   keson_copy.SetBinding(TextBlock.TextProperty, binding);}
<StackPanel>    <WrapPanel>        <TextBox Name="keson" Text="永远滴神"/>    </WrapPanel>    <WrapPanel>        <TextBlock Name="keson_copy" />    </WrapPanel></StackPanel>
namespace WpfApp1{    /// <summary>    /// Window1.xaml 的交互逻辑    /// </summary>    public partial class Window1 : Window    {        LoginModel loginModel = new LoginModel();        public Window1()        {            InitializeComponent();                        //数据绑定            this.DataContext = loginModel;            loginModel.user_name = "keson";        }        private void Button_Click(object sender, RoutedEventArgs e)        {            图书馆主界面 lib = new 图书馆主界面();                         if(loginModel.user_name == "张三")   //这里的textbox_userName就是xaml里的x:Name            {                lib.Show();            }            else            {                MessageBox.Show("账号错误");                loginModel.user_name = "";   //账号不是"张三"时,textbox里的内容会清空            }                    }    }    public class LoginModel: INotifyPropertyChanged    {        private string _user_name;        public string user_name        {            get { return _user_name; }            set            {                _user_name = value;                RaisePropertyChanged("user_name");            }        }        public event PropertyChangedEventHandler PropertyChanged;       //1 申明一个事件        private void RaisePropertyChanged(string propertyName)        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));//3 事件绑定一个方法                    }    }}
<Grid Grid.Row="1" Grid.Column="0">    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">        <TextBlock Text="账号:"   FontSize="40" Foreground="#FF000605" HorizontalAlignment="Center" VerticalAlignment="Center"/>        <TextBox Text="{Binding user_name}" x:Name="textbox_userName" Width="300" Background="White" Margin="10,10"  FontSize="40" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"/>    </StackPanel>            </Grid>

4.4 PropertyChanged实现数据绑定

(1)原理

在这里插入图片描述

(2) 界面

(3) xaml

<Window x:Class="WpfApp2.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        xmlns:local="clr-namespace:WpfApp2"        mc:Ignorable="d"        Title="MainWindow" Height="450" Width="800" MinHeight="40">    <StackPanel >        <TextBox Name="tbx" Height="50"  Margin="5" Text="TextBox1"/>        <TextBox Name="tbx2" Height="50"  Margin="5" Text="TextBox2" TextChanged="Tbx2_TextChanged"/>            </StackPanel></Window>

(4) c#

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;namespace WpfApp2{    /// <summary>    /// MainWindow.xaml 的交互逻辑    /// </summary>    public partial class MainWindow : Window    {        Student stu;        public MainWindow()        {                        InitializeComponent();            stu = new Student();            // 准备Binding            Binding binding = new Binding();            binding.Source = stu;            binding.Path = new PropertyPath("Name");            //使用Binding连接数据源于Binding目标            BindingOperations.SetBinding(tbx, TextBox.TextProperty, binding);        }        private void Tbx2_TextChanged(object sender, TextChangedEventArgs e)        {            stu.Name = tbx2.Text;        }    }    class Student : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        private string name;        public string Name        {            get { return name; }            set            {                name = value;                if(PropertyChanged != null)                {                    PropertyChanged.Invoke(this,new PropertyChangedEventArgs("Name"));                }            }        }    }}

5 c#中的委托

5.1 如何理解委托?

这个名词困惑了了我好久,应该对比C++中的函数指针(一个指向函数的指针),理解了函数指针就理解委托了.
函数指针的就是一个指针变量指向函数,例子如下,定义了一个*pf的函数指针,入参是两个int,返回值也是int,将pf的地址指向max函数,这样pf也能使用max函数.

int max(int a,int b){    return a>b?a;b;}int (*pf)(int, int);pf = max;

不同的是委托可以挂载多个方法。
委托是一个容器,这个容器可以挂载不同的方法。先简单说如何使用:定义一个委托,并给改委托挂载方法,执行委托。
具体步骤:

声明委托;创建委托实例;委托绑定方法;调用委托
public delegate int Calculate(int a, int b); //委托相当于函数指针/// <summary>/// 为委托创建一个加法计算方法/// </summary>/// <param name="name"></param>public static int addMethod(int a, int b){    return a + b;}/// <summary>/// 为委托创建一个乘法计算方法/// </summary>/// <param name="name"></param>public static int MutilMethod(int a, int b){    return a * b;}static void Main(string[] args){    /// <summary>    /// 将加法计算方法赋值给委托    /// </summary>    Calculate cal = addMethod;    cal += MutilMethod;    //调用委托(依次调用)    int a = cal(22, 2);                int b = cal(2, 9);    int c = cal(22, 2);    int d = cal(2, 9);    Console.WriteLine(a);    Console.WriteLine(b);    Console.WriteLine(c);    Console.WriteLine(d);                        

运行结果:

44184418请按任意键继续. . .

5.2 系统自带的两种委托 Action<> 和Func<>

Action<> 用于挂载无返回值的方法
Func<> 用于挂载有返回值的方法

static void Main(string[] args)        {            delegateFun DF = F1;            DF();            //内置的委托            //1 指向无返回值方法            Action a = F1;            a();            //2 指向有返回值方法            Func<int, int, int> func = Paremeters_fun;            Console.WriteLine("2 指向有返回值方法,返回值为:" + func(2, 6));            //3 指向无返回值无入参 匿名方法            Action a2 = delegate ()            {                Console.WriteLine("3 指向无返回值无入参 匿名方法");            };            a2();            //4 指向无返回值有入参 匿名方法            Action<int,string> a3 = delegate (int i,string s)            {                Console.WriteLine($"4 指向无返回值有入参 匿名方法: i = {i},s = {s}");            };            a3(1,"keson");            //5 指向有返回值方法 匿名方法            Func<int, int, int> func2 = delegate (int i, int j)            {                return i + j;            };            Console.WriteLine("5 指向有返回值方法 匿名方法: " + func2(50, 6));            //6 指向有返回值lamda表达式            Func<int, int, int> func3 =(int i, int j)=>            {                return i + j;            };            Console.WriteLine("6 指向有返回值lamda表达式:" + func3(50, 6));            //7 指向有返回值lamda表达式(省略入参类型)            Func<int, int, int> func4 = (i,j) =>            {                return i + j;            };            Console.WriteLine("7 指向有返回值lamda表达式(省略入参类型):" + func3(50, 6));                  }

5.3 简写委托的形式

委托经常伴随着Lambda表达式, Lambda表达式变化很多,有时候看到别人用的时候经常觉得莫名奇妙,我们从基本的开始,看看他们是一步步被简化的.

static void Main(string[] args){    int[] nums = { 11, 52, 69, 33, 54, 2, 9, 23, 66, 45 };    //IEnumerable<int> result = nums.Where(a=>a > 10);    //演变顺序    // 1 先写一个匿名方法    Action<int> action = delegate (int i) { Console.WriteLine(i); };    // Action<int> action2 = delegate (i) { Console.WriteLine(i); };   //匿名方法数据类型不能省略    // Action<int> action3 = delegate (int i)  Console.WriteLine(i); ;  //匿名方法大括号不能省略    // 2 换成lamada表达式    Action<int> action4 = (int i) => { Console.WriteLine(i); };  //完整写法    Action<int> action5 = (i) => { Console.WriteLine(i); };      //数据类型省略    Action<int> action6 = (i) => Console.WriteLine(i); ;        //大括号省略    Action<int> action7 = (i) => Console.WriteLine(i);          //分号省略    Action<int> action8 = i => Console.WriteLine(i);          //入参只有一个时,入参的小括号省略    //3 如果是有返回值    Func<int, bool> func1 = delegate (int i)     //先写个匿名方法    {        if (i > 0)            { return true; }        else        {            return false;        }    };    Func<int, bool> func2 = (int i) =>      //换成lamada表达式    {        if (i > 0)        { return true; }        else        {            return false;        }    };    Func<int, bool> func3 = (i) =>      //数据类型省略    {        if (i > 0)        { return true; }        else        {            return false;        }    };    Func<int, bool> func4 = (i) =>      //优化返回代码    {        return i > 0;    };    Func<int, bool> func5 = (i) =>      //返回语句只有一条语句,省略大括号和return关键字             i > 0;    Func<int, bool> func6 = i =>      //入参只有一个,省略入参的括号         i > 0;}

C#中的事件

在类内声明委托在类内声明事件
namespace ConsoleApp1{    /// <summary>    /// 事件发布者    /// </summary>    public class GreetingManager    {        /// <summary>        /// 声明一个委托变量        /// </summary>        /// <param name="name"></param>        public delegate void display(string name);        /// <summary>        /// 声明一个事件        /// </summary>        public event display display_event;  //相当于对委托类型的变量进行封装        /// <summary>        /// 创建一个处理方法        /// </summary>        /// <param name="name"></param>        public void show(string name)        {            if (display_event != null)            {                display_event(name);            }        }    }    /// <summary>    /// 订阅者    /// </summary>    public class GreetWays    {        public void EnglishGreeting(string name)        {            Console.WriteLine("good moring, " + name);        }        public void ChineseGreeting(string name)        {            Console.WriteLine("早上好, " + name);        }    }    class Program    {        static void Main()        {            GreetingManager greetingManager = new GreetingManager();            GreetWays greetWays = new GreetWays();            greetingManager.display_event += greetWays.EnglishGreeting;  //事件绑定方法            greetingManager.display_event += greetWays.ChineseGreeting;            greetingManager.show("keson"); //调用了show就触发了display_event事件            Console.ReadKey();        }    }}        static void Main(string[] args)        {            int[] nums = { 11, 52, 69, 33, 54, 2, 9, 23, 66, 45 };            //IEnumerable<int> result = nums.Where(a=>a > 10);            //演变顺序            // 1 先写一个匿名方法            Action<int> action = delegate (int i) { Console.WriteLine(i); };            // Action<int> action2 = delegate (i) { Console.WriteLine(i); };   //匿名方法数据类型不能省略            // Action<int> action3 = delegate (int i)  Console.WriteLine(i); ;  //匿名方法大括号不能省略            // 2 换成lamada表达式            Action<int> action4 = (int i) => { Console.WriteLine(i); };  //完整写法            Action<int> action5 = (i) => { Console.WriteLine(i); };      //数据类型省略            Action<int> action6 = (i) => Console.WriteLine(i); ;        //大括号省略            Action<int> action7 = (i) => Console.WriteLine(i);          //分号省略            Action<int> action8 = i => Console.WriteLine(i);          //入参只有一个时,入参的小括号省略            //3 如果是有返回值            Func<int, bool> func1 = delegate (int i)     //先写个匿名方法            {                if (i > 0)                    { return true; }                else                {                    return false;                }            };            Func<int, bool> func2 = (int i) =>      //换成lamada表达式            {                if (i > 0)                { return true; }                else                {                    return false;                }            };            Func<int, bool> func3 = (i) =>      //数据类型省略            {                if (i > 0)                { return true; }                else                {                    return false;                }            };            Func<int, bool> func4 = (i) =>      //优化返回代码            {                return i > 0;            };            Func<int, bool> func5 = (i) =>      //返回语句只有一条语句,省略大括号和return关键字                             i > 0;            Func<int, bool> func6 = i =>      //入参只有一个,省略入参的括号                 i > 0;        }        ```# C#不同界面之间互相操作控件- 主界面可以操作子界面,子界面不可以操作主界面```csharp//主界面窗口public partial class Form1 : Form{    public delegate void Del_main();    static public Form1 form1 = new Form1();            public Form1()    {        form1 = this;        InitializeComponent();                }        private void button_ok_Click(object sender, EventArgs e)    {        Form2.form2.Show();    }    private void Form1_FormClosing(object sender, FormClosingEventArgs e)    {        Form2.form2.Close();        Environment.Exit(0);    }}
public partial class Form2 : Form{         static public Form2 form2 = new Form2();    public Form2()    {        form2 = this;        InitializeComponent();                }        private void form2_button_Click(object sender, EventArgs e)    {        Update_textBox_rcvCanData("测试");    }    private void Update_textBox_rcvCanData(string text)    {        if (Form1.form1.textBox.InvokeRequired)        {            //确保是在UI线程调用控件            Form1.form1.textBox.Invoke(new Action<string>(Update_textBox_rcvCanData), text);                        return;        }        Form1.form1.textBox.AppendText(text);    }}

Lambda表达式

在.NET Core中,Lambda表达式是一种简洁的代码块表示形式,通常用于创建匿名方法。Lambda表达式可以包含表达式和语句,并且可用于创建委托或表达式树类型。Lambda表达式在LINQ查询、集合操作、排序、事件处理等方面非常有用。

Lambda表达式的基本语法如下:

(parameter list) => expression

其中,(parameter list) 是参数列表,expression 是Lambda主体表达式。

以下是一些在.NET Core中使用Lambda表达式的常见用法示例:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; //数据源
作为参数传递给方法:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };int evenCount = numbers.Count(n => n % 2 == 0); // 使用Lambda表达式作为Count方法的参数来计算偶数数量
用于LINQ查询:
var filteredNumbers = numbers.Where(n => n > 3); // 使用Lambda表达式过滤大于3的数字
用于排序集合:
var sortedNumbers = numbers.OrderBy(n => n); // 使用Lambda表达式按升序对数字进行排序
作为事件处理程序:
button.Click += (sender, e) => { /* 处理点击事件的代码 */ }; // 使用Lambda表达式作为事件处理程序订阅按钮的Click事件
创建委托实例:
Func<int, int> square = x => x * x; // 使用Lambda表达式创建委托实例,该委托接受一个int参数并返回其平方int result = square(4); // 调用委托实例计算4的平方并获取结果
在并行编程中使用:
Parallel.For(0, numbers.Count, i => { /* 并行处理的代码 */ }); // 使用Lambda表达式定义并行循环中的操作逻辑

这些是.NET Core中Lambda表达式的常见用法示例。通过使用Lambda表达式,你可以以简洁、可读性强且功能强大的方式表示代码块,并在各种场景中应用它们。

MVVM

M model 数据模型

V View 界面

VM ViewModel 整合业务

WPF 定时器

(System.Threading.Timer)的使用

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;using System.Windows.Threading; //using System.Timers;using System.Threading;namespace WpfApp2{            private Timer timer;        public partial class MainWindow : Window    {        public MainWindow()        {                     InitializeComponent();            timer = new Timer(new TimerCallback(timerCall), null, 0, 5000);        }                private void timerCall(object state)        {            this.Dispatcher.BeginInvoke(new Action(() =>            {                tbx3.AppendText("定时器时间到 ");            }));        }    }}

配置文件读取

public partial class MainWindow : Window    {        public string path;        public MainWindow()        {            InitializeComponent();            path = System.AppDomain.CurrentDomain.BaseDirectory + "Config.ini";            CreateFile(path);            ReadConfigFile(path);        }        Dictionary<string, string> DictInitData = new Dictionary<string, string>();        public void ReadConfigFile(string path)        {                       //读取配置文件并保存到字典中            StreamReader sr = new StreamReader(path);            string line;            while ((line = sr.ReadLine()) != null)            {                line = line.Trim();                if (line.StartsWith("#"))                {                    continue;                }                if (!string.IsNullOrEmpty(line) && line.Contains("="))                {                    int equalsIndex = line.IndexOf('=');                    if (equalsIndex > 0)                    {                        string key = line.Substring(0, equalsIndex).Trim();                        string value = line.Substring(equalsIndex + 1).Trim();                        DictInitData.Add(key, value);                    }                }                           }            sr.Close();            if (DictInitData.Count != 3)            {                File.Delete(path);                CreateFile(path);            }                    }        private void CreateFile(string path)        {            //判断配置文件是否存在,不存在创建一个            if (!File.Exists(path))            {                StreamWriter sw = File.CreateText(path);                sw.WriteLine("ip=192.168.0.1");                sw.WriteLine("port=80");                sw.Flush();                sw.Close();                            }        }    }

UI线程

        private void Update_textBox_rcvGuideData(string text)        {            if (this.IsHandleCreated)                textBox_rcvGuideData.BeginInvoke(new Action(() =>                {                    textBox_rcvGuideData.AppendText(text + "\r\n");                    if (textBox_rcvGuideData.Lines.Length > 500)                    {                        textBox_rcvGuideData.Clear();                    }                }));        }

辨识attribute和property

property

property针对类和对象来说(C#代码),下面这个类中的name,fun2都叫property

class Person(){    string name;    int age;    void fun1(){        //吃饭    }        void fun2(){        //睡觉    }}
attribute

attribute是编程语言文法层面的东西(针对xaml来说),比如button按钮的宽度/高度

链接: link

单例:加深理解静态成员和方法

宝啊,你有没有想过什么是单例?类似单身狗吗?茕茕孑立,形影相吊,这好像不太正确,还有影子陪着单身汪的呀!
一个类只能创建一个实例(有啥用?鬼知道哩,反正我没用过)我写这个主要是帮着我理解C#的关键字 ”static“。

internal class Singleton{    private static Singleton uniqueInstance;    private static readonly object lockObject = new object();    private string? name;    private int age;    private Singleton(string name,int age)    {        this.name = name;        this.age = age;    }    public static Singleton GetInstance()    {        if (uniqueInstance == null)        {            lock (lockObject)             {                 if(uniqueInstance == null)                {                    uniqueInstance = new Singleton(name, age);                }            }        }        return uniqueInstance;    }  }

为啥上面的代码无论你实例化多少个对象,实际上只有一个对象,这是为什么?原因就是“static”,无论成员还是方法,加上这个关键字,在实例化的时候,都不属于实例本身,他们都属于类本身,换句话说,类中的静态成员和方法被所有类共享。
上面的uniqueInstance是一个静态变量,无论创建多少个实例,最后这些实例都是共享一个静态成员变量。

LINQ的用法

这部分内容借鉴了杨中科老师的很多东西,大家可以去b站找他的视频学习真的值得一看. 链接: 杨中科老师视频链接

LINQ中where用法和原理

static void Main(string[] args){   int[] nums = { 11, 52, 69, 33, 54, 2, 9, 23, 66, 45 };   // 1. 调用系统的方法   IEnumerable<int> result = nums.Where<int>(a => a > 20 && a < 40);  //where后面的是lamada表达式的简写形式   foreach(var i in result)   {       Console.WriteLine(i);   }   Console.WriteLine(" ");   // 2. 调用自己写的方法1   IEnumerable<int> result2 = SelectNums(nums, a => a > 20);   foreach (var i in result2)   {       Console.WriteLine(i);   }   Console.WriteLine(" ");   // 3. 调用自己写的方法2   IEnumerable<int> result3 = SelectNums2(nums, a => a > 30);   foreach (var i in result3)    //foreach (var i in SelectNums2(nums, a => a > 20))   {       Console.WriteLine(i);   }}static IEnumerable<int> SelectNums(IEnumerable<int> items, Func<int, bool> func){   List<int> list = new List<int>();   foreach(int item in items)   {       if(func(item) == true)       {           list.Add(item);       }   }   return list;}static IEnumerable<int> SelectNums2(IEnumerable<int> items, Func<int, bool> func){   foreach (int item in items)   {       if (func(item) == true)       {           yield return item;       }   }}

LINQ常用扩展方法

        static void Main(string[] args)        {            List<Employee> list = new List<Employee>();            list.Add(new Employee { Id = 1, Name = "张三", Age = 50, Gender = true, Salary = 50000 });            list.Add(new Employee { Id = 2, Name = "李四", Age = 40, Gender = false, Salary = 90000 });            list.Add(new Employee { Id = 3, Name = "赵六", Age = 30, Gender = false, Salary = 30000 });            IEnumerable<Employee> employee = list.Where((e) => { return e.Age > 30; });   // employees.Where(e =>  return e.Age > 30 );            foreach(var i in employee)            {                Console.WriteLine(i);            }            Console.WriteLine(list.Count(e => e.Salary > 50000));            Console.WriteLine(list.Any(e => e.Gender == true));                        // 防御性编程            Console.WriteLine(list.Single(e => e.Name == "张三"));            // Console.WriteLine(list.Single(e => e.Name == "孙悟空"));//没有该条信息,会报异常            Console.WriteLine(list.SingleOrDefault(e => e.Name == "孙悟空"));        }
class Employee    {        public long Id { get; set; }        public string Name { get; set; }        public int Age { get; set; }        public bool Gender { get; set; }        public int  Salary{ get; set; }        /// <summary>        /// C#所有的class和struct都会继承object,而每一个object都会有一个ToString的方法,这里重写该方法        /// </summary>        /// <returns></returns>        public override string ToString()        {            return $"Id={Id},Name={Name},Age={Age},Gender={Gender},Salary={Salary}";        }    }
class Dog    {        public string nickName { get; set; }        public int age { get; set; }        public override string ToString()        {            return $"nickName = {nickName},age={age}";        }            }
static void Main(string[] args)        {            List<Employee> list = new List<Employee>();            list.Add(new Employee { Id = 1, Name = "张三", Age = 50, Gender = true, Salary = 50000 });            list.Add(new Employee { Id = 2, Name = "李四", Age = 40, Gender = false, Salary = 90000 });            list.Add(new Employee { Id = 3, Name = "赵六", Age = 30, Gender = false, Salary = 30000 });            list.Add(new Employee { Id = 4, Name = "gati", Age = 34, Gender = true, Salary = 6000 });            list.Add(new Employee { Id = 5, Name = "jim", Age = 40, Gender = false, Salary = 7000 });            list.Add(new Employee { Id = 6, Name = "ancle", Age = 24, Gender = false, Salary = 2000 });            Console.WriteLine("where:");            IEnumerable<Employee> employee = list.Where((e) => { return e.Age > 30; });   // employees.Where(e =>  e.Age > 30 );            foreach(var i in employee)            {                Console.WriteLine(i);            }            Console.WriteLine("count:");            Console.WriteLine(list.Count(e => e.Salary > 50000));            Console.WriteLine(list.Any(e => e.Gender == true));            // 防御性编程            Console.WriteLine("single:");            Console.WriteLine(list.Single(e => e.Name == "张三"));            // Console.WriteLine(list.Single(e => e.Name == "孙悟空"));//没有该条信息,会报异常            Console.WriteLine(list.SingleOrDefault(e => e.Name == "孙悟空"));            //排序             Console.WriteLine("order:");            foreach (var i in list.OrderBy(e => e.Salary))            {                Console.WriteLine(i);            }            //跳过和取            Console.WriteLine("skip and take:");            foreach (var i in list.Skip(2).Take(30))            {                Console.WriteLine(i);            }            //分组            Console.WriteLine("\r\n groupby:");            var groupItem = list.GroupBy(e => e.Age);            foreach(var i in groupItem)            {                Console.WriteLine(i.Key);                foreach(var j in i)                {                    Console.WriteLine(j);                }                Console.WriteLine("");            }            //投影  类似数据库的select            IEnumerable<int> age_select = list.Select(e => e.Age);   //把所有的年龄取出来            IEnumerable<string> name_select = list.Select(e => e.Name);            Console.WriteLine("\r\n 投影:");            foreach (var i in age_select)            {                Console.WriteLine(i);            }            IEnumerable<Dog> dogs = list.Select(e => new Dog { nickName = e.Name, age = e.Age });            Console.WriteLine("\r\n 投影dog类:");            foreach (var i in dogs)            {                Console.WriteLine(i);            }            Console.WriteLine("\r\n 匿名类型:");            var items = list.Select(e => new{ 姓名 = e.Name,性别 = e.Gender?"男":"女"});            foreach (var i in items)            {                Console.WriteLine(i.姓名+"  "+i.性别);            }            //综合语法            Console.WriteLine("\r\n 综合语法:");            var items2 = list.GroupBy(e => e.Age).Select(g => new { 年龄 = g.Key, MaxSalary = g.Max(e => e.Salary), minSalary = g.Min(e => e.Salary),人数 = g.Count()});            foreach (var i in items2)             {                Console.WriteLine(i. 年龄 + "  " + i.MaxSalary+ "  " + i.minSalary + "  " + i.人数);                           }        }            //类型转化            IEnumerable<Employee> items = list.Where(e => e.Salary > 6000);            List<Employee> L1 = items.ToList();            Employee[] arry = items.ToArray();            //链式语法            Console.WriteLine("\r\n 链式语法:");            var list2 = list.Where(e => e.Id > 2).GroupBy(e => e.Age).OrderBy(g => g.Key).Take(3).                Select(g => new { 年龄 = g.Key, 平均工资 = g.Average(e => e.Salary) });            foreach(var i in list2)            {                Console.WriteLine(i.年龄 + " " + i.平均工资);            }

split方法可能会掉入的陷阱

split会按照特定字符进行分割,同时返回分割后的字符数组.分割后一定要注意字符前后端是否存在空格,不然会掉入大坑.

通过Linq读取配置文件

  //配置文件接口类      public interface IConfigService    {        string GetValue(string name);    }//配置文件实现类  public class IniFileConfig : IConfigService    {        public string FilePath { get; set; }        public string GetValue(string name)        {            var kv = File.ReadAllLines(FilePath).Select(s => s.Split('=')).Select(strs => new { Name = strs[0].Trim(), value = strs[1].Trim() }).SingleOrDefault(keyValue => keyValue.Name == name);            //var kv2 = kv.Select(s => s.Split('='));            if (kv != null)            {                return kv.value;            }            else            {                return null;            }        }    }//调用配置文件   IniFileConfig config = new IniFileConfig();                string SmtpServer = config.GetValue("SmtpServer");            string UserName = config.GetValue("UserName");            string Password = config.GetValue("Password");

linq常见问题

//linq解决面试问题int i = 4;int j = 5;int k = 6;int[] nums = new int[] { i,j,k };//linq求解int max = nums.Max();//math处理int max2 = Math.Max(i, Math.Max(j, k));//三元运算法int max3 = (i = i > j ? i : j) > k ? i : k;int max4 = i > j ? i > k ? i : k : j > k ? j : k;Console.WriteLine(max4);//求解平均值string s = "1,2,3,4,5,6,7,8,9";string[] str = s.Split(',');IEnumerable<int> arry = str.Select(e => Convert.ToInt32(e));Console.WriteLine(arry.Average());var avg = s.Split(',').Select(p => Convert.ToInt32(p));//统计字符串字母出现的频率Console.WriteLine("\r\n 统计字符串字母出现的频率:");string s1 = "hello world,keson,hgongnoring";var items4 = s1.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);foreach(var item in items4){    Console.WriteLine(item);}

依赖注入

将一个类放到一个容器中,使用这个类时不需要实例化,直接从从容器中取出来

using System;using System.Collections.Generic;using System.Threading.Tasks;using System.Linq;using Microsoft.Extensions.DependencyInjection;namespace ConsoleApp1{    class Program    {        delegate void delegateFun();        static void Main(string[] args)        {            /*              * 1 ServiceCollection是内置的反转控制容器;services.BuildServiceProvider是使用容器中服务             * 2 使用步骤:创建控制容器;向容器中注册方法;使用容器的BuildServiceProvider向容器中获取服务            */            //注册服务            ServiceCollection services = new ServiceCollection();            //services.AddTransient<TestServiceImpl>();            //services.AddSingleton<TestServiceImpl>();            services.AddScoped<TestServiceImpl>();      //同一个scope里拿到的对象是一致的            using (ServiceProvider sp = services.BuildServiceProvider())            {                TestServiceImpl testService = sp.GetRequiredService<TestServiceImpl>();                testService.Name = "keson";                testService.SayHello();                TestServiceImpl testService2 = sp.GetRequiredService<TestServiceImpl>();                //Console.WriteLine("testService, testService2:" + object.ReferenceEquals(testService, testService2));                using (IServiceScope scope1 = sp.CreateScope())                {                    TestServiceImpl t = scope1.ServiceProvider.GetService<TestServiceImpl>();                    t.Name = "张三";                    t.SayHello();                    TestServiceImpl t1 = scope1.ServiceProvider.GetService<TestServiceImpl>();                    //Console.WriteLine("t, t1:" + object.ReferenceEquals(t, t1));                    //Console.WriteLine("t, testService:" + object.ReferenceEquals(t, testService));                }                using (IServiceScope scope2 = sp.CreateScope())                {                    TestServiceImpl t = scope2.ServiceProvider.GetService<TestServiceImpl>();                    t.Name = "李四";                    t.SayHello();                }            }                                }    }        public interface ITestServic    {        string Name { get; set; }        void SayHello();    }    public class TestServiceImpl : ITestServic,IDisposable    {        public string Name { get; set; }        public void Dispose()        {            Console.WriteLine("disposable..........");        }        public void SayHello()        {            Console.WriteLine($"hi,my {Name}");        }    }    public class TestServiceImpl2 : ITestServic    {        public string Name { get; set; }        public void SayHello()        {            Console.WriteLine($"你好,我是{Name}");        }    }}

依赖注入 : 接口 + 实现

直接从类中注入

在一个A类中使用另外一个B类,不需要new 一个新的B类对象,只需要在被注入对象A实例化时传入B类的接口即可。

//Program.csstatic void Main(string[] args){    NotificationService notificationService = new NotificationService(new EmailSender());    notificationService.UseSendMessage(); } //-------------------------------------------- public interface IMessageSender{    void SendMessage(string message);} //-------------------------------------------- public class EmailSender : IMessageSender{    public void SendMessage(string message)    {        Console.WriteLine(message);    }} //-------------------------------------------- public class NotificationService{    private readonly IMessageSender messageSender;    public NotificationService(IMessageSender messageSender)    {        this.messageSender = messageSender;    }    public void UseSendMessage()    {        messageSender.SendMessage("hello,keson");    }}

通过服务容器依赖注入

using System;using System.Collections.Generic;using System.Threading.Tasks;using System.Linq;using Microsoft.Extensions.DependencyInjection;namespace ConsoleApp1{    class Program    {        delegate void delegateFun();        static void Main(string[] args)        {            /*              * 1 ServiceCollection是内置的反转控制容器;services.BuildServiceProvider是使用容器中服务             * 2 使用步骤:创建控制容器;向容器中注册方法;使用容器的BuildServiceProvider向容器中获取服务            */            //注册服务            ServiceCollection services = new ServiceCollection();            services.AddScoped<ITestService, TestServiceImpl>();   // 接口 + 实现            services.AddScoped<ITestService, TestServiceImpl2>();            using (ServiceProvider sp = services.BuildServiceProvider())   //当然,你也可以用services.BuildServiceProvider(),这里用变量接一下方便用            {                //services.BuildServiceProvider().GetService<ITestServic>();                ITestService ITest = sp.GetService<ITestService>();                ITest.Name = "孙悟空";                ITest.SayHello();                //Console.WriteLine(ITest.GetType());                ITestService ITest1 = sp.GetRequiredService<ITestService>();     //确定一定有这个服务                IEnumerable<ITestService> ITest2 = sp.GetServices<ITestService>();   //获取多个服务                foreach(var i in ITest2)                {                    Console.WriteLine(ITest2.GetType());                }            }                    }    }        public interface ITestService    {        string Name { get; set; }        void SayHello();    }    public class TestServiceImpl : ITestService,IDisposable    {        public string Name { get; set; }        public void Dispose()        {            Console.WriteLine("disposable..........");        }        public void SayHello()        {            Console.WriteLine($"hi,I am {Name}");        }    }    public class TestServiceImpl2 : ITestService    {        public string Name { get; set; }        public void SayHello()        {            Console.WriteLine($"你好,我是{Name}");        }    }}

依赖注入:通过构造函数

这里有一个小细节,所有实现的接口的实现必须是公有的

using System;using System.Collections.Generic;using System.Threading.Tasks;using System.Linq;using Microsoft.Extensions.DependencyInjection;namespace ConsoleApp1{    class Program    {        delegate void delegateFun();        static void Main(string[] args)        {            ServiceCollection services = new ServiceCollection();            services.AddScoped<Controller>();            services.AddScoped<ILog, LogImpl>();            services.AddScoped<IStorage, StorageImpl>();            services.AddScoped<IConfig, ConfigImpl>();            services.AddScoped<ILog, LogImpl>();            using(var sp = services.BuildServiceProvider())            {                var c = sp.GetRequiredService<Controller>();                c.Test();            }            Console.ReadKey();                    }    }    class Controller    {        private readonly ILog log;        private readonly IStorage storage;        public Controller(ILog log,IStorage storage)        {            this.log = log;            this.storage = storage;        }        public void Test()        {            log.Log("开始上传");            storage.Save(" 名字 ", " 上传的内容 ");            log.Log("上传完毕");                    }    }    interface ILog    {        void Log(string msg);    }    class LogImpl : ILog    {        public void Log(string msg)        {            Console.WriteLine($"日志: {msg}");        }    }    interface IConfig    {        string GetValue(string name);    }    class ConfigImpl:IConfig    {       public string GetValue(string name)        {            return "config file";        }    }    interface IStorage    {        void Save(string name,string content);    }    class StorageImpl:IStorage    {        private readonly IConfig config;        public StorageImpl(IConfig config)        {            this.config = config;        }        public void Save(string name,string content)        {            string server = config.GetValue("server");            Console.WriteLine($"向服务器为: {server}的文件名为: {name}上传:{content}");        }    }   }

配置读取

配置容器

可能年纪大了,对于那种不讲人话的人,特别反感,包括但不限于:领导、同同事、同学、长辈、博主、水军、我自己。他们就一个特点,很简单的事情他们包装的面目全非,听得新人云里雾里,以彰显在别人眼里不值一提的优越感。
什么是配置容器?他就是一种管理配置文件的东西。什么是配置文件,举个例子,你想连接数据库,你得有账户名、密码、端口号等等,这些东西不可能每次登录都自己手动输入,也不可能都会提供输入这些信息的界面,那就得从文件中读取,我们把它叫做配置文件。
*本章的核心就是Configrution这个命名空间,*搞懂这个就ok了,主要包括两个点:怎么创建配置容器?如何通过配置容器读取配置文件。

步骤

构建配置容器;

向配置容器中添加配置文件(json,xml,txt);

通过Build()读取配置文件;

ConfigurationBuilder builder = new();builder.AddJsonFile("config.json",optional:true,reloadOnChange:true);IConfigurationRoot configRoot = builder.Build();

json文件作为绑定源, 配置类作为绑定目标. 将配置类绑定到配置文件. 配置文件肯定得拿出来才能用, 如果一致放在json文件中,无法用,所以用一个和json配置文件相同的类来接受数据

//选项类被绑定到 ICconfigurationRoot 接口Config config = new Config(); configurationRoot.Bind(config);//选项类被绑定到 ICconfigurationRoot 接口 子类Service service = new Service();configurationRoot.GetSection("Service").Bind(service);

实例:数据源绑定单个类

该方式将类单个单个的配置,配置起来简单,但不利于集中管理

//json file{  "key1": "IamString",  "key2": 10,  "key3": true}//------------------------------------------------------------------------using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Configuration.Json;class Program{    static void Main(string[] args)    {        IConfigurationBuilder builder = new ConfigurationBuilder();        builder.AddJsonFile("appsetting.json", optional: true, reloadOnChange: true);        var configurationRoot = builder.Build();                       //选项类被绑定到 ICconfigurationRoot 接口        Config config = new Config();         configurationRoot.Bind(config);        Console.WriteLine($"key1:{config.Key1}");        Console.WriteLine($"key2:{config.Key2}");        Console.WriteLine($"key3:{config.Key3}");        //选项类被绑定到 ICconfigurationRoot 接口 子类        Service service = new Service();        configurationRoot.GetSection("Service").Bind(service);        Console.WriteLine($"Service.Host {service.Host}");        Console.WriteLine($"Service.Host {service.Port}");        //        Console.ReadKey();    }}class Config{    public string Key1 { get; set; }    public int Key2 { get; set; }    public bool Key3 { get; set; }}class Service{    public string Host { get; set; }    public string Port { get; set; }    //不能注入私有属性    //public string Port { get; private set; } = "999";}

注册服务

步骤

创建一个服务容器;

容器对配置类进行注册服务;

services.Configurede<Ttype>{lamda表达式};

获取服务

serviceProvider.GetRequiredService<IOptions<MyOption>>();//或者serviceProvider.GetRequiredService<IOptionsSnapshot<Config>>();

实例:直接通过lambda表达式对类配置

using Microsoft.Extensions.Configuration;//using Microsoft.Extensions.Configuration.Json;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Options;using System;var services = new ServiceCollection();// 通过.Configure 对选项类进行配置(注册服务)services.Configure<MyOption>(n =>{    n.A = "keson";    n.B = "";});var serviceProvider = services.BuildServiceProvider();//通过<IOptions<MyOption>>()获取服务var myOption = serviceProvider.GetRequiredService<IOptions<MyOption>>();MyOption myOptionValue = myOption.Value;Console.WriteLine(myOptionValue.A);Console.WriteLine(myOptionValue.B);public class MyOption{    public string A { get; set; }    public string B { get; set; }}

实例:通过addOption配置

这种方法配置起来也很简单,且可以链式编程(集中配置,再一个地方对多个类绑定数据源)
addOption和IOptionsSnapshot区别:前者是刚运行时就保存在缓存中,后者是每次处理请求时都会重新从IOptionsSnapshot中读取,以便能够获取到最新的配置信息。

//--------------------------json文件-------------------------------{  "name": "keson",  "age": "28",  "proxy": {"address": "192.168.1.0","port": "80"}}//--------------------------main()-------------------------------namespace 配置文件{    internal class Program    {        static void Main(string[] args)        {            ServiceCollection services = new();            services.AddScoped<Controller>();            ConfigurationBuilder builder = new();            /*             //通过json读取配置文件             builder.AddJsonFile("config.json",optional:true,reloadOnChange:true);            */            //通过命令行读取配置文件 可以在调试中设置 不同参数用空格分开,不能有多余的空格            builder.AddCommandLine(args);            IConfigurationRoot configRoot = builder.Build();            services.AddOptions().Configure<Config>(e => configRoot.Bind(e));                        using (var sp= services.BuildServiceProvider())             {                //通过一个Controller类来操控Config                var c2 = sp.GetRequiredService<Controller>();                c2.Test();                //直接操控                var c = sp.GetRequiredService<IOptionsSnapshot<Config>>();                c.Value.name = "孙悟空";                c.Value.age = "500";                Console.WriteLine(c.Value.name);                Console.WriteLine("--------------");                Console.WriteLine(c.Value.age);            }        }    }    class Config    {        public string name { get; set; }        public string age { get; set; }        public Proxy proxy { get; set; }    }    class Proxy    {        public string address { get; set; }        public int Port { get; set; }    }}//--------------------------Controller-------------------------------internal class Controller    {        //用于访问请求生存期的 TOptions 的值         private readonly IOptionsSnapshot<Config> optconfig;        public Controller(IOptionsSnapshot<Config> optconfig)        {            this.optconfig = optconfig;        }        public void Test()        {            Console.WriteLine(optconfig.Value.name);            Console.WriteLine("--------------");            Console.WriteLine(optconfig.Value.age);        }    }

扁平化配置

name=如来 age=10000 proxy:address=1.1.1.1 proxy:port=9999 proxy:IDs:0=69 proxy:IDs:1=69

日志系统

日志级别

Trace -> Debug -> Informatio -> Warning -> Error -> Critical

日志记录到控制台

核心代码:

引用扩展包

将日志记录到控制台

设置记录到控制台代码Log Level

using Microsoft.Extensions.Logging;  //记录日志用的扩展包   loggingBuilder.AddConsole();      //将日志记录到控制台    loggingBuilder.SetMinimumLevel(LogLevel.Trace);  //设置最低输出级别信息

完整代码

//----------------Test()类-------------------------using Microsoft.Extensions.Logging;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace 日志系统{    internal class TestLogging    {        private readonly ILogger<TestLogging> logger;        public TestLogging(ILogger<TestLogging> logger)        {            this.logger = logger;        }        public void Test()        {             logger.LogDebug("开始执行Logging");            logger.LogWarning("程序警告");            logger.LogError("程序失败");            try            {                File.ReadAllText("A://");                logger.LogDebug("读取文件成功");            }            catch (Exception e)            {                logger.LogError(e, "读取文件失败");   //将捕获的异常也打印出来            }                    }    }}//----------------Main()-------------------------using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;using Microsoft.Extensions.Logging.Console;using 日志系统;ServiceCollection services = new ServiceCollection();services.AddLogging(loggingBuilder =>{    loggingBuilder.AddConsole();      //将日志记录到控制台        //loggingBuilder.AddEventLog();  //将日志记录到Windows日志事件中    loggingBuilder.SetMinimumLevel(LogLevel.Trace);  //设置最低输出级别信息    });services.AddScoped<TestLogging>();using(var sp = services.BuildServiceProvider()){    TestLogging testLogging = sp.GetRequiredService<TestLogging>();    testLogging.Test();}

日志记录到文本: NLog

官网查看例程

很多时候我们不仅需要从控制台查看日志, 还需要从日志文件上查看, Microsoft.Extensions.Logging不满足需求, 需要引用 NLog.Extensions.Logging . 下面的例图是访问NLog.Extensions.Logging扩展包的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

复制config代码,注意名字一定要是nlog.config , 存储位置也要修改,直接改为程序的根目录.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Nlog核心代码

NLog是通过Xml实现配置的, 主要有两部分组成: target 和 rules, 先定义目标,通过规则匹配

target:

type: 输出到文件还是控制台name: target的名字,就像你声明一个变量 int a = 5, 引用它的时候,就可以通过name引用.filename: 输出文件名字

rules:

name: 匹配的名字,允许使用正则表达式,例如匹配命名空间的名字minlevel: 需要记录的最低日志权限wirteTo: 需要输出到的匹配的 target name
<targets><target xsi:type="File" name="allfile" fileName="Test.log"/>    <target xsi:type="Console" name="lifetimeConsole" /></targets><rules>       <logger name="*" minlevel="Trace" writeTo="allfile" /><logger name="SystemServices.*" minlevel="Warn" writeTo="lifetimeConsole" /></rules>

实例

Main()
using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;//using Microsoft.Extensions.Logging.Console;using NLog.Extensions.Logging;using SystemServices;using 日志系统;ServiceCollection services = new ServiceCollection();services.AddLogging(loggingBuilder =>{    //loggingBuilder.AddConsole();    //loggingBuilder.AddEventLog();  //将日志记录到Windows日志事件中    loggingBuilder.AddNLog();        //日志记录到文本文件    //loggingBuilder.SetMinimumLevel(LogLevel.Trace);  //设置最低输出级别信息});services.AddScoped<Test1>();services.AddScoped<Test2>();using(var sp = services.BuildServiceProvider()){    Test1 test1 = sp.GetRequiredService<Test1>();    test1.Test();    Test2 test2 = sp.GetRequiredService<Test2>();    test2.Test();}
nlog.config
<?xml version="1.0" encoding="utf-8" ?><nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      autoReload="true"      internalLogLevel="Info"      internalLogFile="internal-nlog-AspNetCore.txt"><!-- enable asp.net core layout renderers --><extensions><add assembly="NLog.Web.AspNetCore"/></extensions><!-- the targets to write to --><targets><!-- File Target for all log messages with basic details --><target xsi:type="File" archiveAboveSize="100" maxArchiveFiles="10"  name="allfile" fileName="logs/nlog-AspNetCore-all-${shortdate}.log"layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"  /><!-- File Target for own log messages with extra web details using some ASP.NET core renderers --><target xsi:type="File" name="ownFile-web" archiveAboveSize="1000" fileName="logs/nlog-AspNetCore-own-${shortdate}.log"layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" /><!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection --><target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" /></targets><!-- rules to map from logger name to target --><rules><!--All logs, including from Microsoft--><logger name="*" minlevel="Trace" writeTo="allfile" /><logger name="日志系统.*" minlevel="Debug"  writeTo="lifetimeConsole" /><logger name="SystemServices.*" minlevel="Warn" maxlevel="Warn" writeTo="lifetimeConsole" /><!--Output hosting lifetime messages to console target for faster startup detection --><logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" /><!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) --><logger name="Microsoft.*" maxlevel="Info" final="true" /><logger name="System.Net.Http.*" maxlevel="Info" final="true" /><logger name="*" minlevel="Trace" writeTo="ownFile-web" /></rules></nlog>

两个Test类

//------------------Test1------------------using Microsoft.Extensions.Logging;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace 日志系统{    internal class Test1    {        private readonly ILogger<Test1> logger;        public Test1(ILogger<Test1> logger)        {            this.logger = logger;        }        public void Test()        {             logger.LogDebug("Test1开始执行");            logger.LogWarning("Test1程序警告");            logger.LogError("Test1程序失败");                    }    }}//------------------Test2------------------using Microsoft.Extensions.Logging;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using 日志系统;namespace SystemServices{    internal class Test2    {        private readonly ILogger<Test2> logger;        public Test2(ILogger<Test2> logger)        {            this.logger = logger;        }        public void Test()        {            logger.LogDebug("Test2开始执行");            logger.LogWarning("Test2开始执行程序警告");            logger.LogError("Test2开始执行程序失败");        }    }}

SeriLog: 结构化日志

以键值对的形式传保存日志

主要代码和Nlog基本一致,只要把 SeriLog添加到容器中就好

using Serilog;using Serilog.Formatting.Json;ServiceCollection services = new ServiceCollection();services.AddLogging(loggingBuilder =>{    //loggingBuilder.AddConsole();    //loggingBuilder.AddEventLog();  //将日志记录到Windows日志事件中    //loggingBuilder.AddNLog();        //日志记录到文本文件    //loggingBuilder.SetMinimumLevel(LogLevel.Trace);  //设置最低输出级别信息    //使用SeriLog    Serilog.Log.Logger = new LoggerConfiguration()    .MinimumLevel.Debug()    .WriteTo.Console(new JsonFormatter())    .WriteTo.File(new JsonFormatter(), "logs/SeriLog.log")    .CreateLogger();    loggingBuilder.AddSerilog();});

Entity Framework Core

通过C#代码创建表

假设我们要设计这样一张表,我们是不是得一句一句的去写sql语句

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

操作步骤:

先建实体类, 再建实体配置类, 再建数据库配置

控制台迁移数据: Add-Migration Init

将数据写入数据库: update-database

// 1.建立实体类internal class Person    {        public int Id { get; set; }        public string Name { get; set; }        public int Age { get; set; }        public DateTime Birthday { get; set;}        public string BirthPlace { get; set; }    }//2.配置实体类internal class PersonEntityConfig:IEntityTypeConfiguration<Person>    {        public void Configure(EntityTypeBuilder<Person> builder)  //Configure继承自IEntityTypeConfiguration        {            builder.ToTable("T_Persons");        }    }//3.也可以只定义实体类,但不配置,系统会默认配置internal class Dog    {        public int Id { get; set; }        public string Name { get; set; }    }//4.写个数据库配置类  将要写入数据库的类用DbSet<Type>在这里声明,同时该类继承自DbContext.类里重写两个方法,OnConfiguring用啦配置数据的连接   OnModelCreating组装数据模型internal class MyDbContext:DbContext{    public DbSet<Book> Books { get; set; }    public DbSet<Person> Persons { get; set; }    public DbSet<Dog> Dog { get; set; }    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)    {        string connStr = "Server=.;Database=demo1;Trusted_Connection=True;Encrypt=false;";        optionsBuilder.UseSqlServer(connStr);        optionsBuilder.LogTo(Console.WriteLine);    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        base.OnModelCreating(modelBuilder);        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);    }}//5. 控制台迁移数据: Add-Migration Init;  将数据写入数据库: update-database

通过C#对表进行怎删改查

上面的内容是对数据库的表进行设计, 怎么实现对设计好的表进行增删改查

插入数据/查询数据

-------------------------main()-----------------------------using EF_Core;using (MyDbContext ctx = new MyDbContext()){    //ctx相当于逻辑上的数据库    Dog d = new Dog();    d.Name = "旺财";    ctx.Dog.Add(d);   //把d对象加入Dog这个逻辑上的表里     //ctx.SaveChanges();     await ctx.SaveChangesAsync();    Book b1 = new Book { Name = "三体", AuthorName = "刘慈欣", Price = 15 };    Book b2 = new Book { Name = "西游记", AuthorName = "罗贯中", Price = 20 };    Book b3 = new Book { Name = "水浒传", AuthorName = "施耐庵", Price = 18 };    Book b4 = new Book { Name = "红楼梦", AuthorName = "曹雪芹", Price = 19 };    ctx.Books.Add(b1);    ctx.Books.Add(b2);    ctx.Books.Add(b3);    ctx.Books.Add(b4);       var books = ctx.Books.Select(b => b.Price ==15).ToList();  //查询数据        IQueryable<Book> books = ctx.Books.Where(b => b.Price > 18);    foreach (Book book in books)    {        Console.WriteLine(book.Name);        Console.WriteLine("**********");    }}

删除数据/更新数据

    //删除内容: 1.先查出实体 2.从内存中删除数据    var b = ctx.Books.Where(b=>b.Name == "西游记");    foreach(var i in b)    {        //ctx.Books.Remove(i);          //删除数据        i.AuthorName = "Guanzhong Luo"; //跟新数据    }

FluenAPI和DataAnnotation区别

FluenAPI复杂不耦合, DataAnnotation简单但是存在耦合

# FluenAPI: 单独写个配置类     internal class PersonEntityConfig:IEntityTypeConfiguration<Person>    {        public void Configure(EntityTypeBuilder<Person> builder)        {            builder.ToTable("T_Persons");        }    }# DataAnnotation: 直接通过注解的方式配置   [Table("T_Cat")]   internal class Cat   {       public int Id { get; set; }       [Required]       [MaxLength(69)]       public string Name { get; set; }       public int CatId1 { get; set; }        [NotMapped]  //不将该属性映射到数据库,尽量不要用        public int CatId2 {  get; set; }   }

Guid用法

---------------实体类---------------------internal class Rabbit    {        public Guid Id { get; set; }        public string Name { get; set; }    }---------------main()---------------------    Rabbit  r1 = new Rabbit();    r1.Name = "红兔";     ctx.Rabbits.Add(r1);    await ctx.SaveChangesAsync();

Hi/Lo算法

Migration的常用命令

把数据库升级或者回滚到某个状态: update-database migrationState

删除最后一次的迁移脚本: remov-migration

生成SQL迁移脚本: script-migration

add-migration 取一个名字

updat-database

反向工程

Scaffold-DbContext "Server=.;Database=Student;Trusted_Connection=True;MultipleActiveResultSets=true;Encrypt=false;" Microsoft.EntityFrameworkCore.SqlServer

EF core查询执行sql语句

标准日志: optionsBuilder.UseLoggerFactory(loggerFactory)简单日志: optionsBuilder.LogTo(Console.WriteLine)ToQueryString()方法: var books = ctx.Books.Where(b => b.Price ==15); string sql = books.ToQueryString();

怎么记录C#代码对数据库的操作步骤,可以有标准日志和简单日志的方法

 --------------BOOk类----------------internal class Book{    public long Id { get; set; }    public string Name { get; set; }    public string Title { get; set; }    public string AuthorName { get; set; }    public float Price { get; set; }} --------------BOOk配置类------------------     using EF_Core;using static System.Reflection.Metadata.BlobBuilder;using (MyDbContext ctx = new MyDbContext()){    Book b1 = new Book { Name = "三体", AuthorName = "刘慈欣", Price = 15 };    ctx.Books.Add(b1);    await ctx.SaveChangesAsync();   var books = ctx.Books.Where(b => b.Price ==15);   string sql = books.ToQueryString();   Console.WriteLine(sql);    } -------------Main()------------------internal class MyDbContext:DbContext{    private static ILoggerFactory loggerFactory =LoggerFactory.Create(b => b.AddConsole());    public DbSet<Book> Books { get; set; }    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)    {        string connStr = "Server=.;Database=Student;Trusted_Connection=True;Encrypt=false;";        optionsBuilder.UseSqlServer(connStr);        optionsBuilder.UseLoggerFactory(loggerFactory); //标准日志        optionsBuilder.LogTo(Console.WriteLine);   //简单日志    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        base.OnModelCreating(modelBuilder);        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);    }}

EF Core映射Mysql

所有代码都和上面的SQLServer一样,只是在DbContext上修改, 在OnConfiguring上进行配置

string MySqlConneStr = "server=localhost;user=root;password=123456;database=demo1";//特别注意:此处使用的是:Pomelo.EntityFramework.MySql包.var serverVersion = new MySqlServerVersion(new Version(8, 0, 34));optionsBuilder.UseMySql(MySqlConneStr, serverVersion);

对应关系: 关系配置在任何一方

一个Artcle对应多个Comment

四种关系: 弄清楚谁是主语,谁是宾语

onemany
HasHasOneHasMany
WithWithOneWithMany

一对多: 关系配置在多端

Comment类和配置

internal class Comment    {        public long Id {  get; set; }        public string? Message { get; set; }        public Artcle? Artcle1 { get; set; }    }    internal class CommentConfig : IEntityTypeConfiguration<Comment>    {        public void Configure(EntityTypeBuilder<Comment> builder)        {            builder.ToTable("T_Commnet");            builder.HasOne(c => c.Artcle1).WithMany(a=>a.Comments);        }    }

Artcle类和配置类

internal class Artcle{    public long Id { get; set; }    public string? Title { get; set; }    public string? Message { get; set; }    public List<Comment> comments { get; set; } = new List<Comment>();}internal class ArtcleConfig : IEntityTypeConfiguration<Artcle>{    public void Configure(EntityTypeBuilder<Artcle> builder)    {        builder.ToTable("T_Artcle");    }}

MyDbContext

internal class MyDbContext:DbContext{    public DbSet<Artcle> Artcles { get; set; }    public DbSet<Comment> Comments { get; set; }    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)    {        string connStr = "Server=.;Database=demo1;Trusted_Connection=True;Encrypt=false;";        optionsBuilder.UseSqlServer(connStr);        //optionsBuilder.LogTo(Console.WriteLine);    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        base.OnModelCreating(modelBuilder);        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);    }}

main()

using EF_Core;using Microsoft.EntityFrameworkCore;using 一对多;using (MyDbContext ctx = new MyDbContext()){    /*    Artcle artcle = new();    artcle.Title = "keson is the best";    artcle.Message = "据说,他是当代扫地僧";    //Comment c1 = new() { Message = "太牛了" };    Comment c2 = new() { Message = "也太搞笑了" };    //artcle.Comments.Add(c1);    artcle.Comments.Add(c2);        ctx.Artcles.Add(artcle);    ctx.SaveChanges();    */    /*    //IQueryable<Artcle> artcles = ctx.Artcles.Where(a => a.Id == 1);  //不查询关联表    IQueryable<Artcle> artcles = ctx.Artcles.Include(a=>a.Comments). Where(a => a.Id == 1);  //关联表也查询出来    foreach (var artcle in artcles)    {        Console.WriteLine(artcle.Id);        Console.WriteLine(artcle.Title);        foreach(var c in artcle.Comments)        {        }    }     */    /*    var comments = ctx.Comments.Include(c=>c.Artcle1).Where(c => c.Id == 1);    foreach(var c in comments)    {        Console.WriteLine(c.Id);        Console.WriteLine(c.Artcle1.Id);        Console.WriteLine(c.Artcle1.Message);    }    */        //上面的取法会取出所有的字段,通过匿名方法可以只获取需要的字段,提高数据库性能    var a1 = ctx.Artcles.Select(a => new { a.Id, a.Title }).First();    Console.WriteLine(a1.Id + a1.Title);}

一对多:关系配置在一端

所有的代码都一样,只是配置在一端

    internal class ArtcleConfig : IEntityTypeConfiguration<Artcle>    {        public void Configure(EntityTypeBuilder<Artcle> builder)        {            builder.ToTable("T_Artcle");            builder.HasMany(a => a.Comments).WithOne(c => c.Artcle1);        }    }

EFCore性能

EF Core绝大部分的性能超过绝大多数程序员

单向导航

withmany()不带参数

有主从表的用一对多

只是基础处关系的用单向导航

internal class User{    public long Id { get; set; }    public string Name { get; set; }}internal class UserConfig : IEntityTypeConfiguration<User>{    public void Configure(EntityTypeBuilder<User> builder)    {        builder.ToTable("T_User");    }}*******************************        internal class Leave    {        public long Id { get; set; }        public User Requester { get; set; }        public User? Approver { get; set; }        public string? remake { get; set; }    }internal class LeaveConfig : IEntityTypeConfiguration<Leave>{    public void Configure(EntityTypeBuilder<Leave> builder)    {        builder.ToTable("T_Leaves");        builder.HasOne<User>(l => l.Requester).WithMany();        builder.HasOne<User>(l => l.Approver).WithMany();    }}*******************************    internal class MyDbContext:DbContext{    public DbSet<User> Users { get; set; }    public DbSet<Leave> Leaves { get; set; }    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)    {        string connStr = "Server=.;Database=demo1;Trusted_Connection=True;Encrypt=false;";        optionsBuilder.UseSqlServer(connStr);        optionsBuilder.LogTo(Console.WriteLine);    }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {        base.OnModelCreating(modelBuilder);        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);    }}--------------------main()----------------------    using EF_Core;using 单向导航;Console.WriteLine("Hello, World!");using (MyDbContext ctx = new MyDbContext()){    /*    var l = ctx.Leaves.FirstOrDefault();    if (l != null)    {        Console.WriteLine(l);    }    */    User u1 = new User { Name = "孙悟空" };    Leave leave = new Leave { Requester = u1, remake = "回家结婚" };    ctx.Leaves.Add(leave);    ctx.SaveChanges();}


点击全文阅读


本文链接:http://zhangshiyu.com/post/89998.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1