.net core 高级进阶 1

Thread/ThreadPool/Task/TaskFactory

1、进程和线程的区别

进程是正在执行中的程序,进程中执行的每个任务即为一个线程;
线程属于进程;线程离不开委托;

NetFramework 1.0版本:
线程Thread是C#语言对操作计算机线程的一个封装类;

线程代码讲解

Thread thread = new Thread ();

thread.Suspend();//暂停
thread.Resume();//让暂停的线程继续执行

thread.Abort();//终止线程
thread.ResetAbort();//终止的线程继续执行;静态方法;

Thread.Sleep();//等待
thread.Join(2000);//限时等待,过时不候;

while(thread.ThreadState != ThreadState.Stopped)
{
    Thread.Sleep(2000);
}

//线程的优先级
thread.Priority = ThreadPriority.Highest;//最好不要通过这种方式设置;只是从概念上增加优先级,计算机整体的具体优先级还是以实际为准;

thread.IsBackgroud = true;//设置后台线程;当进程关闭,线程随之取消;

2、单线程

按照顺序一个一个执行

3、多线程

同时工作 同时工作 同时工作 同时工作 同时工作 同时工作
1.效率高
2.消耗资源高
3.各线程执行顺序 难以控制

4、多线程回调

如何控制 多线程 的执行顺序 --> 回调 (多线程实例:线程 2 需要使用 线程 1 中的结果)

//Thread中没有控制线程执行顺序的方法;但可以自己通过封装的方式实现线程回调

//多线程实例:线程 2 需要使用 线程 1 中的结果;
private void main()
{
	//1.普通多线程按顺序执行
	ThreadStart threadStart = new ThreadStart (() =>
		Console.WriteLine("线程1");
	);
	Action action = () => Console.WriteLine("线程2");
	This.ThradWithCallBack(threadStart, action);
	
	//2.如果需要有返回值呢?
	Func<int> func= () =>   //定义了一个委托
	{
		return DateTime.Now.Year;
	};
	//int iResult = ThradWithReturn(func);
	Func<int> fResult = ThradWithReturn(func);
	int iResult = fResult.Invoke();
	
}

//使用自定义封装方式老进行多线程回调
private void ThradWithCallBack(ThreadStart threadStart,Action action)
{
	//开启一个线程去执行传进来的两个线程;
	ThreadStart start = () =>{
		threadStart.Invoke();//去执行线程
		action.Invoke();
	};
	new Thread(start).Start();
}

private Func<T> ThradWithReturn<T>(Func<int> func)
{
	T t = default(T);
	ThreadStart thradStart = () =>
	{
		t = func.Invoke();
	}
	ThreadStart threadStart = new ThreadStart (thradStart);
	threadStart.Start();
	
	return new Func<T>(() => 
	{
		//threadStart.join(1000);//可以添加限时等待,
		return t;
	});
}

5、线程池 ThreadPool

线程池中有很多创建好的线程,用的时候拿出来用,用完了或者不用了,就放回线程池;


private void main()
{
	Console.WriteLine("线程1-1");
	
	//1.普通多线程按顺序执行
	{
		WaitCallback waitCallback = new WaitCallback( o =>
			Console.WriteLine("线程2-1");
		)
		ThreadPool.QueueUserWorkItem(waitCallback,"线程池");//从线程池中获取线程
	}
	
	//2.回调(线程等待)
	{
		ManualResetEvent mre = new ManualResetEvent(false);//设置中间变量mre的默认值为false
		ThreadPool.QueueUserWorkItem(o => 
		{
			Console.WriteLine("线程o")
			mre.Set();
		});//从线程池中获取线程
		
		//线程等待
		mre.WaitOne();//等待上一个线程先执行;
	}
	
	Console.WriteLine("线程1-1");
}



6、Task

6.1 Task与TaskFactory 的区别?

Task是对线程的一个封装类,实际上是一个线程
TaskFactory :创建Task的地方;

总结:Task来自于ThreadPool


private void main()
{
	Console.WriteLine("线程1-1");
	//1.如何申请一个线程
	{
		Task task = new Task(()=>{
			Console.WriteLine("线程2");
		});
		task.Start();
	}
	{
		TaskFactory taskFactory = new TaskFactory();
		taskFactory.StartNew(() => {
			Console.WriteLine("线程3");
		});
	}
	{
		//申请线程
		ThreadPool.SetMaxThreads(8, 8);//设置最大线程数量,【全局的】;
		List<int> thradIds= new List<int>();
		List<Task> tasklist = new List<Task>();
		for(int i=0; i<50; i++)
		{
			tasklist.Add(Task.Run(() => {//申请一个线程
				thradIds.Add("获取当前线程Id");
				Console.WriteLine("当前线程Id");
			}))
		}
		Task.WaitAll(tasklist.ToArray());//阻塞主线程,等待所有子线程完成任务后,主线程再继续;(会导致主界面卡顿)
		int iResult = threadIds.Distinct().Count();
		
		//iResult=8;------------------------
		//总结:Task来自于ThreadPool--------
	}
	{
		List<Task> tasklist = new List<Task>();
		//单线程进行讲课
		Teach("课程1");//Teach() 用户自定义方法
		Teach("课程2");
		Teach("课程3");
		Teach("课程4");
		Console.WriteLine("课程教学完毕,准备分配任务编写代码...");
		//讲完课之后,让多个人同时去编码(多线程)
		TaskFactory taskFactory = Task.Factory();
		tasklist.Add(taskFactory.StaretNew(() => Coding("同学1","后台管理")));//Coding()用户自定义方法;
		tasklist.Add(taskFactory.StaretNew(() => Coding("同学2","前台页面")));
		tasklist.Add(taskFactory.StaretNew(() => Coding("同学3","微服务架构的搭建")));
		tasklist.Add(taskFactory.StaretNew(() => Coding("同学4","小程序接口实现")));
		
		//所有同学都顺利完成,我们准备聚个餐,K个歌...
		{
			//1.Task.WaitAll()
			Task.WaitAll(tasklist.ToArray());//等待所有同学完成工作后,主线程才继续执行;会造成主线程中界面卡顿;
			Console.WriteLine($"所有项目搭建完毕,我们准备聚个餐,K个歌...");
		}
		{
			//2.taskFactory.ContinueWhenAll()
			//使用了回调,在一堆任务执行完毕之后,去申请一个新的线程来执行新的动作;
			taskFactory.ContinueWhenAll(tasklist.ToArray(),t => Console.WriteLine($"所有项目搭建完毕,我们准备聚个餐,K个歌..."));
		}
		{
			//3.taskFactory.ContinueWhenAny()当执行了某一个任务时,主线程就会继续执行了;
			taskFactory.ContinueWhenAny(tasklist.ToArray(),t => Console.WriteLine($"当某一个同学完成工作了,老师就准备环境的搭建"));
		}
		{
			//4.如何控制线程数量;
			//为什么要限制线程数量?因为要合理分配计算机系统资源;
			List<Task> tasklist = new List<Task>();
			for(int i=0 ; i<10000 ; i++)
			{
				int k = 1;
				if(tasklist.Count(t => t.Status == TaskStatus.Running) >= 20)//如果线程数量为20了,则主线程开始阻塞,且必须等到有一个线程完成之后,再继续主线程
				{
					Task.WaitAny(tasklist.ToArray());
					//下面这句代码自行调试和添加条件;暂未修改
					tasklist = tasklist.Where(t => t.Status != TaskStatus.RanToCompletion && t.Status != TaskStatus.Canceled && t.Status != TaskStatus.Faulted).ToList();//保留未执行完毕的线程;RanToCompletion()是否完成;Canceled取消;Faulted异常;
				}
				//添加一个新的线程
				tasklist.Add(Task.Run(()={
					Thread.Sleep(2000);
					Console.WriteLine($"This is new Thread.");//容易直接卡死计算机;
				}));
				Console.WriteLine($"线程数量为={tasklist.Count()}");
			}
		}
		{
			//5.如果新申请的线程里面有返回值怎么获取呢?
			Func<int> func = ()=>{ //定义一个委托delegate,返回当前年份;
				Thread.Sleep(3000);
				return DateTime.Now.Year;
			};
			Task<int> task = new Task<int>(func);
			task.Start();
			int i = task.Result;//这里会阻塞页面
			
		}
		
	}
	
	Console.WriteLine("线程1-1");
}

Task.WaitAll()
与taskFactory.ContinueWhenAll()类似,但是Task.WaitAll()会阻塞主线程,造成卡顿;当所有线程完毕之后,主线程继续;

taskFactory.WaitAny()
主线程进行阻塞,当有一个线程执行完毕后,主线程继续执行;

taskFactory.ContinueWhenAll()
举例:某个主页会涉及到多个数据,数据1来源于DB,数据2来源于第三方,数据3来源于其他DB;
传统顺序:一个一个进行查找,比较耗时;如果每一步平均耗时1秒,那么传统顺序会耗时3秒
使用ContinueWhenAll的时候,系统会进行同时查找,总耗时为1秒;提高查询效率;

taskFactory.ContinueWhenAny()

举例:查找某个数据;
传统思路查找顺序:(1)找缓存 --> (2)找接口 --> (3)找DB
使用ContinueWhenAny的时候,创建了多个线程去不同的地方同时进行查找,当有一个完成查找之后,就可直接返回数据,页面进行相应;

7、表达式目录树 Expression<>

表达式目录树 实际上是一个数据结构(二叉树)。

为什么使用表达式目录树?
答:表达式目录树 – 动态的
初级阶段:数据库查询的时候,都是拼接SQL语句(多一个条件就拼接一个查询字符串);
现在:使用LinqToSql;

Expression<Func<int, int, int>> ex1 =  (m, n) => m * n + 2 + 3;//如下图所示

.net core 高级进阶 1

//1.委托
Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//(m, n) => m * n + 2实际上是一个匿名方法;Func<返回值, m, n>
var func1 = exp.Compile();
int IResult = func1.Invoke(1, 2);

//2.表达式目录
var peopleQuery = new List<People>().AsQueryable();
Expression<Func<People, bool>> expression = p => p.Id.Tostring().Equals("5");
PeopleQuery.Where(expression);
//进行编译
//通过ILspy反编译
//看中间语言,经过解析后,可以如下表示;
ParameterExperssion p = Experssion.Paramter(typeof(People),"p");
FieldInfo fieldId = typeof(People).GetField("Id");
MemberExperssion exp = Experssion.Field(p,fieldId);
MethodInfo toString = Typeof(People).GetField("Id");
var toString = Experssion.Call(exp ,toString ,Array.Empty<Expression>());
MethodInfo equals = typeof(string).GetMethod("Equals",new Type[] {typeof(string)});
Experssion expressionContant = Expression.Contant("5");

var equalExp = Expression.Call(toStringExp,equals, new Expression[]{
	expressionContant
});//equalExp == p.Id.Tostring().Equals("5")

Expression<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(equalExp, new ParamterExpression[]{
	p
});//expression == p => p.Id.Tostring().Equals("5")

bool bResult = experssion.Compile().Invoke(new People(){Id = 5});//experssion.Compile()定义了一个委托
{
	
}

7.1 表达式目录树是如何拆解成Sql语句被数据库识别

//ExpressionVisitor//带有Visitor的是访问者模式

//ExpressionVisitor.Visit()方法是访问表达式目录树的入口:1、首先会判断是什么类型的表达式目录(And、GreaterThan);2、会自动的调用到更加专业的方法中去进一步访问(ExpressionVisitor.多个具体分方法名());
//ExpressionVisitor.多个具体分方法名()//转换成SQL能识别的;例1:将 C#中的 && 转换成 SQL 中的 and;例2:程序中需要只允许减号-的存在,一般这个时候,需要我们去重写微软封装的方法,通过人为的判断,将+号全部改成减-号;

我们可以自己定义一个类,来继承自ExpressionVisitor,然后重写封装的方法;
public static main()
{
	Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//定义了一个表达式目录树;
	OperationsVisitor visitor = new OperationsVisitor();
	Expression expNew = visitor.Modify(exp);
}

public class OperationsVisitor : ExpressionVisitor
{
	public Expression Modify(Expression expression)
	{
		return this.Visitor(expression);//调用ExpressionVisitor中的Visitor方法;访问表达式目录树的入口
	}
	
	//1.重写ExpressionVisitor中的VisitBinary方法
	protected override Expression VisitBinary(ExpressionBinary b)//VisitBinary对应ExpressionBinary
	{
		if(b.NodeType == ExpressionType.Add)
		{
			Expression left = this.Visit(b.Left);//递归进入VisitBinary()
			Expression right = this.Visit(b.Right);
			return Expression.Subtract(left, right);
		}
		return base.VisitBinary(b);
	}
	//同理可以重写多个基类中的方法
}

.net core 高级进阶 1
.net core 高级进阶 1
.net core 高级进阶 1


8.async / await 语法糖

async 修饰方法名称的;

主线程遇到await A后就返回了,await A后面的内容由新线程执行;await A的线程遇到await B时又被返回了(await A被放回线程池,表示A活干完了),await B后面的内容被await B执行;总结:线程遇到await A后就返回
程序中要么不用await,要么从头用到尾;

//当程序中没有async时,想用async时;
await Task.CompletedTask();//官方框架推荐
await Task.Delay();
结果:可以增加吞吐量,提升性能;
但是:不是所有的程序都适用async/await;如下

await / async能并发吗?一定能提升性能吗?意义何在?
单个方法内不能并发,线程在遇到await 时就返回了,后续内容由新线程执行,属于串行执行;
不能提升性能,性能会低于同步调用,比如:对于同一个方法,单次处理,是没有性能提升的,只会增加负荷;
意义:1、在多请求并发处理,且资源有限的时候,能增加吞吐量(增加单位时间处理的请求)

举例:一个孩子进食时间为一小时,**1个老师喂4个孩子(await并行)的时候与和一个老师喂一个孩子(普通串行)**进行比较;await并行时,老师在喂第一个孩子的时候,剩下三个孩子自己进食,然后依次喂食,最终花费时间会小于4小时;普通串行时,老师一个一个喂,最终还是花费4小时;

ILSpy 5.x反编译dll文件,查看源码

ILSpy 中存在状态机,用变量_State表示,状态机模式:一个对象根据不同的状态会有不同的行为(对象只有一个,类似于红绿灯)

Task和await的区别
举例:读本地文件;
Task方式–启动10个线程,分别等着读取文件 ,线程一直处于等待状态,cpu一直被消耗;
await方式–实际上看不到线程,调用系统封装的异步API,发起请求,线程就回去了–由硬件自行读取,读完之后发信号,然后线程池再安排线程去处理;
类似例子:还有调用webApi,WCF,远程数据库链接,连接第三方、读缓存;(都值得使用async)
类似于纯CPU计算类型的(不值得使用async,反而浪费线程资源)

9.反射

9.1反射的原理、反射的使用

9.1.1反射的原理

什么是反射?
答:Reflection命名空间,是微软的一个帮助类库,可以读取metadate元数据,可以使用对应metadate元数据中的方法;

高级语言到机器识别过程:C#高级语言 --> 编译器编译 --> DLL/EXE(又包含了metadate元数据(类似于清单,只能看见方法名)、IL(中间语言)) --> CLR/JIT --> 机器码010101

9.1.2反射的使用

IOC中的实现;

//基础类
public class SqlServerHelper : IDBHelper
{
	public SqlServerHelper()
	{
		
	}
	
	public void Query()
	{
		//查询内容
	}
}

public Interface IDBHelper()
{
	public void Query();
}
public static IDBHelper CreateInstance()
{
	//1.动态加载DLL
	Assembly assembly = Assembly.Load("dll名称");//LoadFrom()
	//2.获取类型
	Type type = assembly.GetType("dll名称.对应的Helper类A");//Helper类A继承IDBHelper
	//3.创建对象
	object odbHelper = Activator.CreateInstance(Type);
	//4.类型转换成对应的Helper
	IDBHelper dbHelper = odbHelper.IDBHelper;
	//5.调用方法
	dbHelper.Query();
}

9.1.3反射的好处和局限

为什么要这么做呢?
1.这样可以断开对细节的依赖(不用添加命名空间引用)

2.实现程序可配置:公司来了技术经理,要求更换数据库为Mysql,按照普通方式写的话,需要改代码,重新编译,重新发布等麻烦步骤;通过反射的话,只需要修改appsetting.json中的即可,不需要重新改代码,重新编译等麻烦步骤,实现了程序可配置;(前提两个数据库的Helper类已经存在了,减少了改代码,编译,发布等步骤)

3.增加了程序扩展性(如果公司只有sqlserver和mysqlhelper,没有orcale数据库的helper类,现在又需要使用orcale数据库,我们最终只需要添加新的orcale project + 改配置文件即可;)

4.实现的就是IOC控制反转的核心思想

9.1.3.2 局限

9.1.4封装

9.1.2中的程序看起来需要很多代码,所以我们要进行封装一下就好;

public class SimpleCreateInstance()
{
	public Static IDBHelper CreateOnstance()
	{
		//(1)基本封装代码
		Assembly assembly = Assembly.Load("dll名称A");//LoadFrom()
		Type type = assembly.GetType("dll名称A.对应的Helper类A");//这两个步骤可通过配置文件进行优化,如下
		object odbHelper = Activator.CreateInstance(Type);
		IDBHelper dbHelper = odbHelper.IDBHelper;
		return dbHelper;
		
		
		//(2)优化封装代码
		//core中在appsetting.json通过key value进行配置;
		//配置如:"dbHelperRefliction":"dll名称A.对应的Helper类A, dll名称A.dll"
		string TypeName = CustomConfigManager.GetConfig("dbHelperRefliction").Split(',')[0];  //dll名称A.对应的Helper类A
		string DllName = CustomConfigManager.GetConfig("dbHelperRefliction").Split(',')[1];  //dll名称A
		
		Assembly assembly = Assembly.Load(DllName);//LoadFrom()
		Type type = assembly.GetType(TypeName);//这两个步骤可通过配置文件进行优化,如下
		object odbHelper = Activator.CreateInstance(Type);
		IDBHelper dbHelper = odbHelper.IDBHelper;
		return dbHelper;
	}
}
public static main()
{
	IDBHelper idbHelper = SimpleCreateInstance.CreateInstance();
	idbHelper.Query();
}

9.2反射在项目的应用

MVC中的实现:
localhost:8080/Home/Index
为什么浏览器访问这个路径,就可以进入到Action中去呢?最终就是调用Action。


{
	//----------------------------
	//(1)无参公共方法
	Console.WriteLine("反射调用普通方法");
	//1.动态加载dll
	Assembly assembly = Assembly.LoadForm(@"xxx.dll");
	//2.获取类型
	Type type =  assembly.GetType(@"xxx.dll.类名");
	//3.创建对象
	object oTest = Activator.CreateInstance(type);
	//方法如何调用呢?
	//4.获取方法
	MethodInfo show1 = type.GetMethod("show1");//show1为自定义的无参方法;
	//5.利用Invoke去调用方法
	show1.Inoke(oTest,new object[0]);//Inoke需要传输两个参数,所以我们就创建了一个空的对象;new object[0]写法是新的写法;
	//结果:成功调用

	//-----------------------------
	//(2)带参数的公共方法
	MethodInfo show2 = type.GetMethod("show2");//show2为自定义的有参方法;show2(int id);
	show2.Inoke(oTest,new object[]{123});//成功
	show2.Inoke(oTest,new object[]{"ZiFuChuan"});//失败;注意:在传递参数的时候,一定要注意,必须要和方法的参数类型一致;
	
	//-------------------------------
	//(3)带参数的公共重载方法
	//show3(int id, string name);
	//show3(string name, int id);
	//show3(int id);
	
	//MethodInfo show3 = type.GetMethod("show3");// 这种方式是有错误的,因为不知道是哪个具体的方法;
	MethodInfo show3 = type.GetMethod("show3", new Type[] {typeof(int), typeof(string)});//这样就获取到了show3(int id, string name)方法
	show3.Inoke(oTest,new object[]{123, "ZiFuChuan"});//成功
	
	//---------------------
	//(4)带参数的**私有**方法
	//show4为自定义的有参方法;show4(string name);
	//MethodInfo show4 = type.GetMethod("show4");//因为是private,所以这样获取不到的
	MethodInfo show4 = type.GetMethod("show4", BindingFlags.NonPublic | BindingFlags.Instance);//传递枚举进去NonPublic,就可以找得到private方法了;
	show4.Inoke(oTest,new object[]{"ZiFuChuan"});
	
	//---------------------
	//(5)静态方法
	MethodInfo show5 = type.GetMethod("show5");//4.获取方法
	show5.Inoke(oTest,new object[]{"ZiFuChuan"});//成功
	show5.Inoke(null,new object[]{"ZiFuChuan"});//成功。静态方法的调用其实不需要具体的实例,只需要“类型.静态方法名”即可,所以这里不传递实例也是可以的。调用静态方法的基础知识;
	
	//---------------------
	//(6)普通类的泛型方法
	//show6<T, W, X>(T t, W w, X x)
	//普通方法调用方式:对象.Show6<int, string, DateTime>(123, "ZiFuChuan", DateTime.Now);
	//普通方法调用方式:对象.Show6(123, "ZiFuChuan", DateTime.Now);这种方法之所以成功是因为语法糖来确定的;
	MethodInfo show6 = type.GetMethod("show6");//4.获取泛型方法,成功
	//show6.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now});//失败
	
	MethodInfo showNew6 = show6.MakeGenericMethod(new Type[]{typeof(int), typeof(string), typeof(DateTime)});//MakeGenericMethod指定类型
	showNew6.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now});
	
	//---------------------
	//(7)泛型类的泛型方法1
	//show7<T, W, X>(T t, W w, X x);;public class A<T, W, X>
	Assembly assembly = Assembly.LoadForm(@"xxx.dll");
	//这里class为A
	//Type type =  assembly.GetType(@"xxx.dll.A");//结果:获取不到
	Type type =  assembly.GetType(@"xxx.dll.A`3");//结果:成功;`3表示泛型,参数有3个;
	
	//object oTest = Activator.CreateInstance(type);//失败,因为通过普通方法 A obj = new A();是不允许的;需要指定类中的泛型;
	Type typeNew = type.MakeGenericType(typeof(int), typeof(string), typeof(DateTime));//MakeGenericType指定类型
	object oTest = Activator.CreateInstance(typeNew);//成功
	
	MethodInfo show7 = typeNew.GetMethod("show7");//成功,因为是基于类的泛型;这里就不需要再次指定类型了
	show7.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now});

	//---------------------
	//(8)泛型类的泛型方法2
	//show8<T, W, X>(T t, W w, X x);;public class A<T>//+++++++注意这里类的泛型+++++++
	Assembly assembly = Assembly.LoadForm(@"xxx.dll");
	//这里class为A
	Type type =  assembly.GetType(@"xxx.dll.A`1");//结果:成功;`1表示泛型,参数有1个;
	Type typeNew = type.MakeGenericType(typeof(int), typeof(string), typeof(DateTime));//MakeGenericType指定类型
	object oTest = Activator.CreateInstance(typeNew);//成功
	
	MethodInfo show8 = typeNew.GetMethod("show8");//成功的,但是剩下2个泛型类未指定;需要进行指定
	show8New = show8.MakeGenericMethod(new Type[]{typeof(int), typeof(DateTime)});//MakeGenericMethod指定类型,方法中剩下的泛型按顺序指定;
	show7.Inoke(oTest,new object[]{"ZiFuChuan", 123, DateTime.Now});//成功
	
}

总:所以实际上在MVC请求网站的时候 ,会一步一步的解析出域名、端口、ControllerName、方法Index;实际上是通过反射的方式定位到具体的位置;

9.3反射封装框架,在线手写ORM框架

9.3.1通过反射操作属性或字段

{
	///1.普通方式
	People people = new People()
	{
		Id = 123,
		Name = "zifucahun",
		Age = 25
	};
	
	Console.WriteLine($"{people.Id}");//取值
	
	///2.反射的实现,如下
	Type type = typeof(People);
	object oPeople = Activator.CreateInstance(type);//创建实例
	//赋值
	foreach(var prop in type.GetProperties)
	{
		if(prop.Name.Equals("Id"))
		{
			prop.SetValue(oPeople, 123);//赋值
		}
	}
	//取值
	foreach(var field in type.GetFields())
	{
		Console.WriteLine($"oPeople.{field.Name} = {field.GetValue(oPeople)}");//遍历取值
	}
	
	//反射的好处
	//1.通过普通方式无论是赋值还是取值都需要需改代码(当增加、删除、更新一个字段时)
	//2.反射取值的时候,就不需要改代码,但是赋值的还是需要修改的;	
	
}

9.3.2 使用反射在ORM框架的实现


public class main
{
	SqlServerHelper sqlServerHelper = new SqlServerHelper();
	
	//Student 和Teacher 两个Model对应数据库表;
	Student s1 = sqlServerHelper.Query(1);
	Teacher t1 = sqlServerHelper.Query(1);
}

public class SqlServerHelper
{
	//ORM,面向对象思想。本质:通过对类的操作,实现对数据库的操作(类与数据库的字段和类型必须一致)
	
	Public T Query<T>(int id)  //Public T Query<T>(string sqlstr)
	{
		//1.数据库连接字符串
		string conn ="Data Souce=主机名称; Database=数据库名称;User ID=sa;Password=数据库登录密码; MultipleActiveResultSets=True;";
		
		//2.准备SQl语句
		string sql = "";//+++++SQL语句可以拆分外部,灵活性更大;++++
		
		Type type = typeof(T);//获取泛型T的类名称;//泛型T类对应数据库表;
		object oObj = Activator.CreateInstance(type);//创建泛型T类对应的实例oObj 
		
		//3.连接数据库
		Using(SqlConnection connection = new SqlConnection(conn))
		{
			connection.Open();
			SqlCommand sqlCommand = new SqlCommend(sql, connection);//将SQL语句连接数据库;
			SqlDataReader reader = sqlCommand.ExecuteReader();//准备执行数据读取
			reader.Read();//读取数据
			
			//下面使用反射的方式;
			foreach(var prop in type.GetProperties())//遍历T的所有属性
			{
				//给泛型T的实例对象oObj设置属性值,属性值通过从reader索引获取;value = reader[属性名称];
				prop.SetValue(oObj,reader[prop.Name]);//注意写法
			}
		}
		return (T)oObj;// return oObj as T;//注意这两种写法的区别
	}
	
	//如果只想查询部分字段,可以通过 “特性” 进行过滤;
}

上一篇:java data 时间


下一篇:Hangfire .NET任务调度框架实例