1.异常

 

/// 1 try catch旨在上端使用,保证对用户的展示
/// 2 下端不要吞掉异常,隐藏错误是没有意义的,抓住再throw也没意义
/// 3 除非这个异常对流程没有影响或者你要单独处理这个异常

 

2.常用的异常

  2.1与参数有关的异常类:派生自SystemException,用于方法成员传递参数时发生异常

    ArgumentException类:用于处理参数无效的异常。ArgumentNullException类:参数为空。

    FormatException类:用于处理参数格式错误的异常。

  2.2与数组有关的异常:继承自SystemException

    IndexOutOfException类:用于处理下标超出了数组长度所引发的异常。

    ArrayTypeMismatchException类:用于处理在数组中存储了数据类型不正确的元素所引发的异常。

  2.3与IO有关的异常:继承自SystemException

    IOException类:用于处理进行文件输入输出操作时所引发的异常。

  2.4内存溢出有关的异常

    OverFlowException类:用于处理程序逻辑错误造成的死循环异常。

  2.5与算术有关的异常

    ArithmeticException类:用于处理与算术有关的异常,比如0做除数。DivideByZeroException类。

  2.6数据库操作异常类

    DbException类:用于处理所有数据源操作异常类的基类。

    SqlException类:用于处理针对sql数据源的异常,比如数据库链接错误。

3.异常处理

  3.1“底层方法”将第一次捕获的异常传递给上级调用者进一步详细处理

 1 public static int Update(string sql)
 2 {
 3   SqlConnection conn = new SqlConnection(connString);
 4   SqlCommand cmd= new SqlCommand(sql,conn);
 5   try
 6   {
 7     conn.Open();
 8     return cmd.ExecuteNonQuery();
 9   }       
10   catch(Exception ex)
11   {
12     //此处可以添加日志
13     throw ex;
14   }
15   finally
16   {
17     conn.Close();
18   }
19 }    
20 
21 try catch throw finally
try catch throw finally

 3.2"中层调用者"可以使用多路捕获异常并封装详细异常信息

  添加具体异常对象,通过判断错误号决定异常的类型。

    1.多路捕获异常不是必须的,只有需要的时候才使用。

    2.可以添加多个catch块。

    3.一定要把Exception类放在最后。

    ex.Number的使用,throw new Exception("********"+ex.Message);

    throw ex;这种未经包装

    throw new Exception("******"+ex.Message);这种稍微包装了一下

    异常一般放在业务逻辑里面,不需要放在界面里面,这样界面捕获后就直接显示。

  3.3异常处理的建议

    3.3.1在代码底层出现问题,不要隐瞒,第一时间终止运行

   3.3.2 UI层直接把异常catch住,底层尽量不要catch异常,有问题直接throw异常

   3.3.3除非能容忍某个异常,那么自己try catch,然后日志,剩下的继续

    如果代码有问题,尽量早可能报错,早可能解决

 4.系统功能较多并且业务复杂时,可以使用三层架构设计项目

  表示UI:界面层

  业务逻辑层BLL:将界面层或数据访问层的部分职责(逻辑代码)分离出来

  数据访问层DAL:主要负责连接读写数据库

  表示层UI、业务逻辑层BLL、数据访问层都需要访问实体层(Models)。

   

 5.项目命名的一般规范

  解决方案      项目名称+Pro

  用户界面层  项目名称

  业务逻辑层  项目名称+BLL  业务逻辑类:实体类名+Manager

  数据访问层  项目名称+DAL  数据访问类:实体类名+Service

  实体对象     一般和数据库实体相同 

6.理解框架

  作用:支撑、主导

7.典型两层架构设计原理

  分表示层和数据访问层,表示层由前台开发人员夫负责,主要是和用户交互的界面;数据访问层由后台开发人员负责,主要完成数据库的操作。表示层和数据访问层由实体层连接,分层与实体类无必然联系,实体类只是数据传递的载体,什么时候都可以使用。

8.关于类库项目

  类库项目专门提供软件的功能模块

  类库项目最终生成的是一个dll文件

  通过封装,类库项目能够很好的与外界协作,安全性高

9.两层架构项目之间的引用

  数据访问层DAL需引用实体层Models,表示层From需引用数据访问层DAL和实体层Models。

10.实体对象的可序列化

  10.1实体对象在程序中不断的被传递

    表示层(UI)<——>实体对象Models<——>数据访问层DAL

  10.2序列化与反序列

    序列化:将对象状态转换为可保持或传输的格式的过程,在序列化过程中,对象的公有字段和私有字段以及类的名称(包括包含该类的程序集)都被转化为字节流,然后写入数据流。

    反序列化:将流转化为对象,与序列化结合可轻松地存储和传输数据。

  10.3对象序列化的意义

    10.3.1将对的状态保持在存储媒体中,以便可以在以后重新创建精确的副本。

    10.3.2将对象从一个应用程序域发送到另一个应用程序域,对象序列化以后保证数据传输的稳定和安全。

    实体类Models的前面加   [Serializable]

11.接口  

  接口又叫接口类,接口也是类

  使用关键字interface定义,接口类名称通常使用I开头

  接口中的属性、方法等,只是做一个声明,没有任何实现

  接口中的属性、方法等,默认都是public。

  方法声明类似于抽象方法

  11.1接口具有强制性,实现接口的类必须实现接口的所有成员

  11.2一个类既可以实现多个接口,也可以同时继承其他类

    通常把父类写在接口的前面,继承了一个类,实现了一个接口

  class Student:Person,IStudent,IComparable{...}

 1 //定义接口
 2     interface IMultiPrinter
 3     {
 4         void Print(string content);
 5         void Copy(string content);
 6         bool Fax(string content);
 7     }
 8 
 9 //类实现接口
10     class HPMultiPrinter:IMultiPrinter
11     {
12         public void Print(string content)
13         {
14             Console.WriteLine("惠普打印机正在打印..." + content);
15         }
16 
17         public void Copy(string content)
18         {
19             Console.WriteLine("惠普打印机正在复印..." + content);
20         }
21 
22         public bool Fax(string content)
23         {
24             Console.WriteLine("惠普打印机正在传真..." + content);
25             return true;
26         }
27     }
28 
29 //调用接口
30         static void Main(string[] args)
31         {
32             //使用接口定义,类实现该对象
33             IMultiPrinter objPrinter = new HPMultiPrinter();
34             objPrinter.Print("学生成绩表");
35             objPrinter.Copy("学生信息表");
36             objPrinter.Fax("老师信息表");
37             Console.ReadKey();
38         }
接口的简单应用实例

12.接口的实践

  12.1提高团队成员并行开发项目的效率

    接口使用者只关心接口的应用功能,不必关心接口的内部实现

    接口实现者只关心如何实现接口的内部实现,不关心谁使用

  12.2提高系统的可维护性

    当用户的需求改变时,只需要修改接口的实现,系统即可更新

13.接口总结

  接口的应用场合

    如果某一个功能点需求变化较多,应使用接口保证系统的可扩展性

    如果想实现团队成员的并行开发,可以使用接口来规范对象的使用

  接口编写规范

    接口成员只能是一个声明

    实现接口的类必须全部实现接口中规定的属性、方法

  特别说明

    接口的使用不是必须的,要根据用户的需求来决定

14.继承多态

  多态的应用大大提高了程序的可扩展性

  继承多态实现的条件:1、父类中必须有抽象方法和虚方法

            2、子类必须重写父类中的抽象方法或虚方法

            3、子类对象必须转换成父类类型去使用

  父类类型作为方法的参数类型,调用时实际传递的是子类的对象

15.接口多态

  实现接口多态的条件:1、一个接口必须被两个或两个以上类实现

            2、接口实现类必须转换成接口类型去使用

  接口作为方法参数类型,调用时实际传递的是接口实现类的对象

  接口多态在实践中应用最广泛

  通俗来说,同样一个方法(方法声明的是接口类对象),因为传递的对象(传递的是接口的派生类)不一样,执行的过程和结果也不一样,这就是接口多态

 1 //定义接口
 2     interface IMultiPrinter
 3     {
 4         void Print(string content);
 5         void Copy(string content);
 6         bool Fax(string content);
 7     }
 8 
 9 //惠普类
10     class HPMultiPrinter : IMultiPrinter
11     {
12         public void Print(string content)
13         {
14             Console.WriteLine("惠普打印机正在打印..." + content);
15         }
16 
17         public void Copy(string content)
18         {
19             Console.WriteLine("惠普打印机正在复印..." + content);
20         }
21 
22         public bool Fax(string content)
23         {
24             Console.WriteLine("惠普打印机正在传真..." + content);
25             return true;
26         }
27     }
28 
29 //佳能类
30     class JNMultiPrinter : IMultiPrinter
31     {
32         public void Print(string content)
33         {
34             Console.WriteLine("佳能打印机正在打印..." + content);
35         }
36 
37         public void Copy(string content)
38         {
39             Console.WriteLine("佳能打印机正在复印..." + content);
40         }
41 
42         public bool Fax(string content)
43         {
44             Console.WriteLine("佳能打印机正在传真..." + content);
45             return true;
46         }
47     }
48 
49 //实现多态
50     class Program
51     {
52         static void Main(string[] args)
53         {
54             Print(new HPMultiPrinter());
55             Print(new JNMultiPrinter());
56             Console.ReadLine();
57         }
58 
59         static void Print(IMultiPrinter objPrinter)
60         {
61             objPrinter.Print("学生成绩表");
62             objPrinter.Copy("学生信息表");
63             objPrinter.Fax("老师信息表");
64         }
65     }
接口多态的简单应用

16.接口与抽象类

  抽象类 接口
不同点 用abstract定义 用interface定义
只能继承一个类 可以实现多个接口
非抽象派生类必须实现抽象方法 实现接口的类必须实现所有成员
需要override实现抽象方法 直接实现
相似点 都不能直接实例化
都包含未实现的方法
子类或“接口实现类”必须实现未实现的方法

 17.设计模式与简单工厂

  简单工厂是设计模式中的一种。

   什么是设计模式?设计模式是人们在开发中遇到的共性问题而提出的一个解决方案

           程序开发中的设计模式只是一种参考,而不是一成不变的

  简单工厂:典型应用是解决单一对象创建的扩展问题

  抽象工厂:典型应用是解决多种类型数据库访问问题或不同业务逻辑

  单例模式:典型应用是在开发中设计购物车的时候需使用

18.简单工厂

  

  实现原理:工厂通过“选择”的方法来指定应该创建哪个“接口实现类的对象”

       “工厂”其实就是一个对象创建的方法,让对象“延迟创建”

  简单工厂创建步骤:

    1、添加接口。

    2、添加需要实现接口的类并实现接口。

    3、添加工厂(建议使用静态类)。
      <1>定义接口变量。
      <2>读取配置文件。
      <3>使用反射创建接口实现类的对象。
      <4>返回接口变量(实际返回的是一个接口实现类的对象)

    4、添加配置文件并配置相应的节点。

    5、调用工厂方法并实现功能。

 1 //接口
 2     /// <summary>
 3     /// 打印接口(产品原型)
 4     /// </summary>
 5     public interface IReport
 6     {
 7         void StartPrint();
 8     }
 9 
10 //实现接口的类1    
11     public class ExcelReport:Factory.IReport
12     {
13         public void StartPrint()
14         {
15             System.Windows.Forms.MessageBox.Show("...正在调用Excel报表程序");
16         }
17     }
18 
19 //实现接口的类2
20     public class WordExport:Factory.IReport
21     {
22         public void StartPrint()
23         {
24             System.Windows.Forms.MessageBox.Show("...正在调用Word报表程序");
25         }
26     }
27 
28 //工厂类
29     public class Factory
30     {
31         //【1】定义接口变量
32         static IReport objReport = null;
33         //【2】读取配置文件
34         static string reportType = ConfigurationManager.AppSettings["ReportType"].ToString();
35         //【3】根据配置文件初始化接口变量
36         public static IReport Choose()
37         {
38             switch (reportType)
39             {
40                 case "ExcelReport":
41                     objReport = new ExcelReport();
42                     break;
43                 case "WordReport":
44                     objReport = new WordExport();
45                     break;
46                 default:
47                     break;
48             }
49             return objReport;
50         }
51     }
52 
53 //实现调用代码
54         private void btn_Print_Click(object sender, EventArgs e)
55         {
56             //IReport objReport = new ExcelReport();//new WordReport
57             //根据工厂提供的具体产品来使用
58             //这种设计方法,使得调用者和后台实现方法完全脱离,大大增加了系统的可扩展性
59             IReport objReport = Factory.Factory.Choose();
60             objReport.StartPrint();
61         }
简单工厂的应用

19.反射 Reflection

  反射的概念

    反射是.NET中的一个重要技术。通过反射,可以在运行时获得某个类型的各种信息,包括方法、属性、事件、及构造函数等,还可以获得每个成员的名称等信息。

  反射的特点

    在程序运行时,动态创建对象、调用方法、设置属性和激励事件,而不是在编译的时候完成。

  反射的应用

    在VS中的智能提示、使用MSIL反汇编工具查看IL代码用的都是反射技术。

    Java开发工具Eclipse中的插件使用,也是用反射技术。

  开发中的应用

    系统需要基于插件开发的时候,必须要用反射。

    在简单工厂和抽象工厂设计模式中将使用反射技术。

    使用反射一般都要配合接口使用。

    反射技术使得系统性能一定程度降低,除非必要情况,反射不宜过多使用。

 1 //1、创建接口类
 2 namespace ICal
 3 {
 4     public interface ICalculator
 5     {
 6         int Add(int a, int b);
 7         int Sub(int a, int b);
 8     }
 9 }
10 
11 //2、创建计算器类实现接口
12 namespace CalDll
13 {
14     public class Calculator : ICal.ICalculator
15     {
16         public int Add(int a, int b)
17         {
18             return a + b;
19         }
20 
21         public int Sub(int a, int b)
22         {
23             return a - b;
24         }
25     }
26 }
27 
28 //3、使用反射技术应用接口
29 using System.Reflection;//【1】引入反射命名空间
30 using ICal;//【2】引入接口类库
31 
32         private void btn_Cal_Click(object sender, EventArgs e)
33         {
34             //动态加载程序集并创建对象
35             ICalculator objCal = (ICalculator)Assembly.LoadFrom("CalDll.dll").CreateInstance("CalDll.Calculator");
36             //通过接口完成运算
37             int result = objCal.Add(Convert.ToInt16(this.tb_Num1.Text.Trim()), Convert.ToInt16(this.tb_Num2.Text.Trim()));
38             this.lbl_Result.Text = result.ToString();
39         }
用反射技术写了一个计算器应用程序

   反射:System.Reflection  .Net框架提供帮助类库,可以读取并使用metadata。程序可配置可扩展,不需要修改代码,首次运行时读取的。Assembly.Load(Path)这里的path是Debug这个路径下的dll文件名,通过这样并不需要重新引用。IOC,依赖抽象接口,Reflection+Factory+Config

Console.WriteLine("************************Reflection*****************");
//【1】 加载dll
//方式1:dll名称无后缀,从当前目录即Debug加载
Assembly assembly = Assembly.Load("Ruanmou.DB.MySql");
//方式2:完整路径的加载  可以是别的目录   加载不会错,但是如果没有依赖项,使用的时候会错
Assembly assembly1 = Assembly.LoadFile(@"D:\MyReflection\bin\Debug\Ruanmou.DB.MySql.dll");
//方式3://带后缀或者完整路径
Assembly assembly2 = Assembly.LoadFrom("Ruanmou.DB.MySql.dll");
foreach (var item in assembly.GetModules())
{
    Console.WriteLine(item.FullyQualifiedName);
}
foreach (var item in assembly.GetTypes())
{
    Console.WriteLine(item.FullName);
}
//【2】获取类
Type type = assembly.GetType("Ruanmou.DB.MySql.MySqlHelper");//2 获取类型信息
//【3】创建对象
object oDBHelper = Activator.CreateInstance(type);
//oDBHelper.Query();//oDBHelper是objec不能调用,但实际上方法是有的   因为编译器不认可所以这句会报错
//【4】类型转换
IDBHelper iDBHelper = (IDBHelper)oDBHelper;
//【5】方法调用
iDBHelper.Query();
通过Reflection调用方法的5个步骤及加载反射的3种方式
Console.WriteLine("************************Reflection+Factory+Config*****************");
IDBHelper iDBHeler = Factory.CreateHelper();//1/2
iDBHeler.Query();//可配置可扩展   反射是动态的  依赖的是字符串
Console.WriteLine("************************Common*****************");
IDBHelper iDBHelper = new SqlServerHelper();
iDBHelper.Query();
使用Factory创建反射对象与通常实例化的比较
public class ReflectionTest
{
    #region Identity
    /// <summary>
    /// 无参构造函数
    /// </summary>
    public ReflectionTest()
    {
        Console.WriteLine("这里是{0}无参数构造函数", this.GetType());
    }

    /// <summary>
    /// 带参数构造函数
    /// </summary>
    /// <param name="name"></param>
    public ReflectionTest(string name)
    {
        Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
    }

    public ReflectionTest(int id)
    {
        Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
    }
    #endregion
}


Assembly assembly = Assembly.Load("Ruanmou.DB.SqlServer");
Type type = assembly.GetType("Ruanmou.DB.SqlServer.ReflectionTest");
object oReflectionTest1 = Activator.CreateInstance(type);//调用无参数构造函数
object oReflectionTest2 = Activator.CreateInstance(type, new object[] { 123 });//调用int型构造函数
object oReflectionTest3 = Activator.CreateInstance(type, new object[] { "123" });//调用string型构造函数
通过Reflection调用不同构造函数
public class GenericClass<T, W, X>
{
    public void Show(T t, W w, X x)
    {
        Console.WriteLine("t.type={0},w.type={1},x.type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name);
    }
}


Assembly assembly = Assembly.Load("Ruanmou.DB.SqlServer");
Type type = assembly.GetType("Ruanmou.DB.SqlServer.GenericClass`3");//有几个参数,就占位符+几
//object oGeneric = Activator.CreateInstance(type);
Type newType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
object oGeneric = Activator.CreateInstance(newType);
通过反射调用泛型
/// <summary>
/// 反射测试类
/// </summary>
public class ReflectionTest
{
    #region Method
    /// <summary>
    /// 无参方法
    /// </summary>
    public void Show1()
    {
        Console.WriteLine("这里是{0}的Show1", this.GetType());
    }
    /// <summary>
    /// 有参数方法
    /// </summary>
    /// <param name="id"></param>
    public void Show2(int id)
    {

        Console.WriteLine("这里是{0}的Show2", this.GetType());
    }
    /// <summary>
    /// 重载方法之一
    /// </summary>
    /// <param name="id"></param>
    /// <param name="name"></param>
    public void Show3(int id, string name)
    {
        Console.WriteLine("这里是{0}的Show3", this.GetType());
    }
    /// <summary>
    /// 重载方法之二
    /// </summary>
    /// <param name="name"></param>
    /// <param name="id"></param>
    public void Show3(string name, int id)
    {
        Console.WriteLine("这里是{0}的Show3_2", this.GetType());
    }
    /// <summary>
    /// 重载方法之三
    /// </summary>
    /// <param name="id"></param>
    public void Show3(int id)
    {

        Console.WriteLine("这里是{0}的Show3_3", this.GetType());
    }
    /// <summary>
    /// 重载方法之四
    /// </summary>
    /// <param name="name"></param>
    public void Show3(string name)
    {

        Console.WriteLine("这里是{0}的Show3_4", this.GetType());
    }
    /// <summary>
    /// 重载方法之五
    /// </summary>
    public void Show3()
    {

        Console.WriteLine("这里是{0}的Show3_1", this.GetType());
    }
    /// <summary>
    /// 私有方法
    /// </summary>
    /// <param name="name"></param>
    private void Show4(string name)
    {
        Console.WriteLine("这里是{0}的Show4", this.GetType());
    }
    /// <summary>
    /// 静态方法
    /// </summary>
    /// <param name="name"></param>
    public static void Show5(string name)
    {
        Console.WriteLine("这里是{0}的Show5", typeof(ReflectionTest));
    }
    #endregion
}


Console.WriteLine("************************Reflection+Method*****************");
Assembly assembly = Assembly.Load("Ruanmou.DB.SqlServer");
Type type = assembly.GetType("Ruanmou.DB.SqlServer.ReflectionTest");
object oReflectionTest = Activator.CreateInstance(type);
foreach (var item in type.GetMethods())
{//遍历所有的方法名
    Console.WriteLine(item.Name);
}
//oReflectionTest.Show1();//object不能直接调用方法
{
    //调用Ruanmou.DB.SqlServer命名空间下ReflectionTest类中void Show1()方法
    MethodInfo method = type.GetMethod("Show1");
    method.Invoke(oReflectionTest, null);
}
{
    //调用Ruanmou.DB.SqlServer命名空间下ReflectionTest类中void Show2(int id)方法
    MethodInfo method = type.GetMethod("Show2");
    method.Invoke(oReflectionTest, new object[] { 123 });
}
{
    //调用Ruanmou.DB.SqlServer命名空间下ReflectionTest类中static void Show5(string name)
    MethodInfo method = type.GetMethod("Show5");
    //使用Reflection调用静态方法,实例那里可以指定为null,因为static是全局唯一的
    method.Invoke(oReflectionTest, new object[] { "麦田的稻草人" });
    method.Invoke(null, new object[] { "果然" });//等同上句
}
{
    //void Show3()
    MethodInfo method = type.GetMethod("Show3", new Type[] { });
    method.Invoke(oReflectionTest, new object[] { });
}
{
    //void Show3(int id)
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(int) });
    method.Invoke(oReflectionTest, new object[] { 123 });
}
{
    //void Show3(string name)
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(string) });
    method.Invoke(oReflectionTest, new object[] { "Ant" });
}
{
    //void Show3(int id, string name)
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(int), typeof(string) });
    method.Invoke(oReflectionTest, new object[] { 234, "W" });
}
{
    //void Show3(string name, int id)
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
    method.Invoke(oReflectionTest, new object[] { "W", 234 });
}
{
    //用Reflection调用私有方法private void Show4(string name)
    MethodInfo method = type.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);
    method.Invoke(oReflectionTest, new object[] { "天空之上" });
}
通过Reflection调用各种方法,包括有无参数、参数重载、私有方法、静态方法
namespace Ruanmou.DB.SqlServer
{
    public class GenericDouble<T>
    {
        public void Show<W, X>(T t, W w, X x)
        {
            Console.WriteLine("t.type={0},w.type={1},x.type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name);
        }
    }
}

Assembly assembly = Assembly.Load("Ruanmou.DB.SqlServer");
Type typeGenericDouble = assembly.GetType("Ruanmou.DB.SqlServer.GenericDouble`1");//泛型类有一个参数<T>
Type newType = typeGenericDouble.MakeGenericType(new Type[] { typeof(int) });
object oGeneric = Activator.CreateInstance(newType);
MethodInfo method = newType.GetMethod("Show");
MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) });//泛型方法后两个参数<W,X>
methodNew.Invoke(oGeneric, new object[] { 123, "流浪诗人", DateTime.Now });//<int,string,Datetime>
通过Reflection调用反射类和反射方法
namespace Ruanmou.Model
{
    /// <summary>
    /// 实体
    /// </summary>
    public class People
    {
        public People()
        {
            Console.WriteLine("{0}被创建", this.GetType().FullName);
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Description;
    }
}

Assembly assembly = Assembly.Load("Ruanmou.Model");
Type type = assembly.GetType("Ruanmou.Model.People");
//以上两句也可以用这句代替  Type type = typeof(People);
object oPeople = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{//遍历所有的属性
    Console.WriteLine(type.Name);
    Console.WriteLine(prop.Name);
    Console.WriteLine(prop.GetValue(oPeople));
    if (prop.Name.Equals("Id"))
    {
        prop.SetValue(oPeople, 234);
    }
    else if (prop.Name.Equals("Name"))
    {
        prop.SetValue(oPeople, "风潇潇");
    }
    Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(oPeople)}");
}
foreach (var field in type.GetFields())
{//遍历所有的字段
    Console.WriteLine(type.Name);
    Console.WriteLine(field.Name);
    Console.WriteLine(field.GetValue(oPeople));
    if (field.Name.Equals("Description"))
    {
        field.SetValue(oPeople, "高级班的新学员");
    }
    Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(oPeople)}");
}
通过Reflection调用属性和字段
namespace Ruanmou.Model
{
    /// <summary>
    /// 实体
    /// </summary>
    public class People
    {
        public People()
        {
            Console.WriteLine("{0}被创建", this.GetType().FullName);
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description;
    }

    public class PeopleDTO
    {
        public PeopleDTO()
        {
            Console.WriteLine("{0}被创建", this.GetType().FullName);
        }
        public int Id { get; set; }
        public string Name { get; set; }//ShortName  特性
        public string Description;
    }
}

//【1】先声明初始化一个people对象
People people = new People();
people.Id = 123;
people.Name = "Lutte";
people.Description = "高级班的新学员";

PeopleDTO peopleDTO_1 = new PeopleDTO()
{
    Id = people.Id,
    Name = people.Name,
    Description = people.Description
};//通常可以这样声明一个PeopleDTO硬编码

//【2】利用反射根据Dll和了类名创建类型
Assembly assembly = Assembly.Load("Ruanmou.Model");
Type typePeople = assembly.GetType("Ruanmou.Model.People");
Type typePeopleDTO = assembly.GetType("Ruanmou.Model.People");
//以上三句可以用下面两句代替
//Type typePeople = typeof(People);
//Type typePeopleDTO = typeof(PeopleDTO);
//【3】创建一个DTO实例
object peopleDTO = Activator.CreateInstance(typePeopleDTO);
foreach (var prop in typePeopleDTO.GetProperties())
{//【4】遍历属性
    object value = typePeople.GetProperty(prop.Name).GetValue(people);
    prop.SetValue(peopleDTO, value);
}
foreach (var filed in typePeopleDTO.GetFields())
{//【5】遍历字段
    object value = typePeople.GetField(filed.Name).GetValue(people);
    filed.SetValue(peopleDTO, value);
}
通过Reflection对相同属性和字段的两个类的对象赋值,People与PeopleDTO

   反射优点:动态。面向对象语言是静态的,相对安全的,但很多是写死的。

  反射缺点:写起来复杂、避开编译器的检查(有时有异常)、反射性能差一点(性能优化,空间换时间) 、MVC和EF第一次访问很慢,后面很快

  所有的访问修饰符对于反射都是没用的。

//需要把YKMDLL.dll放到执行目录Debug下
namespace YKMDLL
{
    public class CalaAdd
    {
        /// <summary>
        /// 共有字段
        /// </summary>
        public int addResult;
        /// <summary>
        /// 共有属性
        /// </summary>
        public int FirstNum { get; set; }
        /// <summary>
        /// 共有属性
        /// </summary>
        public int SecondNum { get; set; }
        /// <summary>
        /// 共有方法
        /// </summary>
        /// <returns></returns>
        public int Add()
        {
            return FirstNum + SecondNum;
        }
    }
}

//【1】加载程序集
Assembly assembly = Assembly.Load("YKMDLL");
//【2】遍历程序集下所有类型的所有共有成员(属性、字段、方法)
foreach (Type type in assembly.GetTypes())
{
    Console.WriteLine($"**当前类型是{type.Name}");
    foreach (PropertyInfo propInfo in type.GetProperties())
    {//仅遍历共有属性
        Console.WriteLine($"    属性类型:{propInfo.PropertyType}    属性名:{propInfo.Name}");
    }
    foreach (FieldInfo fieldInfo in type.GetFields())
    {//仅遍历共有字段
        Console.WriteLine($"    字段类型:{fieldInfo.FieldType}    字段名:{fieldInfo.Name}");
    }
    foreach (MethodInfo methodInfo in type.GetMethods())
    {//仅遍历共有方法
        Console.WriteLine($"    方法返回值类型:{methodInfo.ReturnType}    方法名:{methodInfo.Name}");
    }
}
//【3】获取指定类类型
Type type_Add = assembly.GetType("YKMDLL.CalaAdd");
//【4】创建实例
object obj_Add = Activator.CreateInstance(type_Add);
//【5】设置属性值
PropertyInfo prop_AddFirst = type_Add.GetProperty("FirstNum");
prop_AddFirst.SetValue(obj_Add, 123);
PropertyInfo prop_AddSecond = type_Add.GetProperty("SecondNum");
prop_AddSecond.SetValue(obj_Add, 2);
//【6】执行方法
MethodInfo method_Add = type_Add.GetMethod("Add");
int result = Convert.ToInt32(method_Add.Invoke(obj_Add, null));
Console.WriteLine($"反射方法计算的结果:{prop_AddFirst.GetValue(obj_Add)}+{prop_AddSecond.GetValue(obj_Add)}={result}");
自己写的反射测试

20.基于接口设计三层架构

  开发团队协作的保障——指定项目开发规范

    项目命名、模块命名、类编写规范、注释要求...

    数据库设计规范:表的命名、实体属性命名、约束...

    项目开发中协作的形式:垂直分工、并行开发

  垂直分工协作

    任务分工:按功能模块

    技术要求:要求开发人员必须熟悉项目各模块(DAL、BLL、UI)编写方法

    应用场合:中小型项目,且开发团队力量较小

    现实比较:类似于小企业的“作坊式”生产

    人员特点:开发人员的工作贯穿整个项目

  并行开发协作

    任务分工:按层(BLL、DAL、UI)划分

    技术要求:只要求开发人员熟悉项目其中一层(BLL、DAL、UI)的业务和编写方法

    应用场合:大中型项目,且开发团队力量强

    现实比较:类似于大企业的“专业化、流水线”生产

    人员特点:熟悉系统业务的开发人员设计BLL业务层

         熟悉数据库开发的人员设计DAL数据层

         善于设计用户界面的开发人员设计UI

 21.特性

  特性:就是一个类,直接或间接继承自Attribute,一般以Attribute结尾,声明时候可以省略Attribute。特性可以影响程序的运行。使用方法是用中括号,感觉上每一个特性都可以带来对应的功能,例如在一个类前加【Serializable】就表明给可以序列化和反序列化,其实这是一种错觉,实际上特性添加后,编译会在元素内部产生IL,但是无法直接使用,仅会在metadata里面有记录。

//修饰特性 AttributeTargets指该特性用在哪里,All表示全部;AllowMultiple表示特性是否是多重修饰;Inherited表示使用该特性是否可以被继承下去
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
修饰特性
//修饰特性 AttributeTargets指该特性用在哪里,All表示全部;AllowMultiple表示特性是否是多重修饰;Inherited表示使用该特性是否可以被继承下去
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class CustomAttribute : Attribute
{
    public CustomAttribute()
    { }
    public CustomAttribute(int id)
    {
        Console.WriteLine("********************");
    }
    public string Description { get; set; }
    public string Remark = null;
    public void Show()
    {
        Console.WriteLine($"This is {nameof(CustomAttribute)}");
    }
    //委托  事件 都没问题
}
自定义一个CustomAttribute特性
[Custom(123, Description = "1234", Remark = "2345")]//方法不行
public class Student
{
    [CustomAttribute]
    public int Id { get; set; }
    [Leng(5,10)]//还有各种检查
    public string Name { get; set; }
    [Leng(20, 50)]
    public string Accont { get; set; }

    [Custom()]//给【该方法】添加特性
    [return: Custom()]//给【该方法返回值】添加特性
    public string Answer([Custom]string name)//给【该方法参数】添加特性
    {
        return $"This is {name}";
    }
}
定义一个Student类去使用CustomAttribute特性
public class Manager
{
    public static void Show(Student student)
    {
        //特性本身是没用的,只有通过反射创建特性类,才可以执行特性类中的方法,获取特性类中的属性、字段
        //【1】类特性
        Type type = typeof(Student); //student.GetType();
        if (type.IsDefined(typeof(CustomAttribute), true))//创建特性实例前应先检查有没有,而不是直接创建实例,这样性能高
        {
            //Attribute attribute0 = type.GetCustomAttribute(typeof(CustomAttribute), true);
            //CustomAttribute attribute = (CustomAttribute)attribute0;
            //上面两句等同于下面一句
            CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
            Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
            attribute.Show();
        }

        //【2】属性特性
        PropertyInfo property = type.GetProperty("Id");
        if (property.IsDefined(typeof(CustomAttribute), true))
        {
            CustomAttribute attribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true);
            Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
            attribute.Show();
        }

        //【3】方法特性
        MethodInfo method = type.GetMethod("Answer");
        if (method.IsDefined(typeof(CustomAttribute), true))
        {
            CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true);
            Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
            attribute.Show();
        }

        //【4】方法参数特性
        ParameterInfo parameter = method.GetParameters()[0];
        if (parameter.IsDefined(typeof(CustomAttribute), true))
        {
            CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute), true);
            Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
            attribute.Show();
        }

        //【5】方返回值数特性
        ParameterInfo returnParameter = method.ReturnParameter;
        if (returnParameter.IsDefined(typeof(CustomAttribute), true))
        {
            CustomAttribute attribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true);
            Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
            attribute.Show();
        }
    }
}
CustomAttribute特性的管理

 小结:特性本身是没用的,程序运行的过程中找到特性并加以利用,也就是说没有破坏类型封装的前提下,可以加点额外的信息与行为。不反射就使用不了特性,也就是说特性必须结合反射来使用。任何一个可以生效的特性,都是因为有地方主动使用了的。类使用特性的时候,那个类必须先写好调用特性的代码。

//第一部分【Demo部分】
using System;
using System.Reflection;

namespace YKMAttribute
{
    // 枚举类型
    public enum UserState
    {
        [Remark("正常")]
        Normal = 0,
        [Remark("冻结")]
        Frozen = 1,
        [Remark("删除")]
        Deleted = 2
    }

    //特性类
    public class RemarkAttribute : Attribute
    {
        public RemarkAttribute(string remark)
        {//特性类构造函数
            _remark = remark;
        }
        private string _remark = null;
        public string GetAttributeRemark()
        {//特性类返回remark方法
            return _remark;
        }
    }

    //Remark特性管理类
    public static class RemarkManager
    {
        //根据枚举值得到中文含义
        public static string GetRemark(this Enum value)
        {
            Type type = value.GetType();
            FieldInfo filedInfo = type.GetField(value.ToString());
            if (filedInfo.IsDefined(typeof(RemarkAttribute), true))
            {
                RemarkAttribute attribute = (RemarkAttribute)filedInfo.GetCustomAttribute(typeof(RemarkAttribute), true);
                //上面这句是下面这两句的合写版
                //Attribute attribute0 = filedInfo.GetCustomAttribute(typeof(RemarkAttribute), true);
                //RemarkAttribute attribute1 = (RemarkAttribute)attribute0;
                return attribute.GetAttributeRemark();
            }
            else
            {
                return value.ToString();
            }
        }
    }
}

//第二部分【测试部分】
static void Main(string[] args)
{
    //正常应该这样写,静态类静态方法传入一个枚举值
    Console.WriteLine(RemarkManager.GetRemark(UserState.Normal));
    //以下两种用到了扩展方法,注意该方法声明GetRemark(this Enum value)中的this
    Console.WriteLine(UserState.Frozen.GetRemark());
    Console.WriteLine(UserState.Deleted.GetRemark());
    Console.ReadKey();
}
特性应用——>>给枚举值添加一个中文描述

/* 定义一个Syudent类,三个属性
 * 要求1:Name属性长度在5~10
 * 要求2:Address属性长度在20~50
 * 要求3:QQ范围为1001~999999999999
 */ 
public class Student
{
    [Length(5,10)]
    public string Name { get; set; }
    [Length(20, 50)]
    public string Address { get; set; }
    [Long(10001, 999999999999)]
    public long QQ { get; set; }
}


/* 抽象特性类
 * 两个特性子类
 * 特性验证管理类(其中包含特性验证方法)
 */
namespace YKMAttribute
{
    /* 分析可知Name和Address都是验证长度,QQ是验证范围
     * Name和Address可以共用一个特性指定不同的上下限长度
     * QQ可以用另一个特性指定上下限范围
     * 分析结果:【用一个抽象泛型类,和两个子类泛型类】
     */

    //抽象特性类
    public abstract class AbstractValidateAttribute : Attribute
    {
        //抽象特性类的抽象方法
        public abstract bool Validate(object value);
    }

    [AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
    public class LengthAttribute : AbstractValidateAttribute
    {
        private int _min = 0;
        private int _max = 0;
        public LengthAttribute(int min, int max)
        {
            _min = min;
            _max = max;
        }
        public override bool Validate(object value)
        {
            if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
            {
                int length = value.ToString().Length;
                if (length >= _min && length <= _max)
                {
                    return true;
                }
            }
            return false;
        }
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
    public class LongAttribute : AbstractValidateAttribute
    {
        private long _min = 0;
        private long _max = 0;
        public LongAttribute(long min, long max)
        {
            _min = min;
            _max = max;
        }
        public override bool Validate(object value)
        {
            if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
            {
                long result = 0;
                if (long.TryParse(value.ToString(), out result))
                {
                    if (result >= _min && result <= _max)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    //特性验证管理类
    public static class ValidateExtension
    {
        public static bool Validate(this object o)//注意这里有this,扩展方法
        {
            Type type = o.GetType();
            foreach (PropertyInfo info in type.GetProperties())
            {//通过反射遍历所有的属性
                if (info.IsDefined(typeof(AbstractValidateAttribute), true))
                {//判断是否有配置该特性的属性
                    //如果有配置该特性的属性,得到该属性所有的AbstractValidateAttribute特性
                    object[] attributeArray = info.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
                    foreach (AbstractValidateAttribute attribute in attributeArray)//在这一步转换的
                    {//遍历所有的特性
                        if (!attribute.Validate(info.GetValue(o)))
                        {//检测该方法,判断该值
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
}

//测试
static void Main(string[] args)
{
    Student stu = new Student()
    {
        Name = "MeTrun",
        Address="银河系太阳系地球村江苏省苏州市昆山市开发区世贸东一号13栋1367室",
        QQ=521256
    };
    Console.WriteLine($"stu合格结果:{stu.Validate()}");
    stu.Name = "唐三";
    Console.WriteLine($"stu修改Name后结果:{stu.Validate()}");
    stu.Name = "MeTrun";
    stu.Address = "水帘洞";
    Console.WriteLine($"stu修改Address后结果:{stu.Validate()}");
    stu.Address = "银河系太阳系地球村江苏省苏州市昆山市开发区世贸东一号13栋1367室";
    stu.QQ = 1119;
    Console.WriteLine($"stu修改QQ后结果:{stu.Validate()}");
    Console.ReadKey();
}
特性应用——>>验证属性是否OK

 22.反射、特性、泛型作业

 

//(数据库)实体类基类
public class BaseModel
{
    public int Id { get; set; }
}

//用户类
public class User : BaseModel
{
    [ChinaName("姓名"),Length(5,10)]
    public string Name { get; set; }
    public string Account { get; set; }
    [ChinaName("密码")]
    public string Password { get; set; }
    [ChinaName("邮箱")]
    public string Email { get; set; }
    public string Mobile { get; set; }
    public int CompanyId { get; set; }
    public string CompanyName { get; set; }
    //public int State { get; set; }
    [Column("State"),ChinaName("状态")]
    public int Status { get; set; }
    public int UserType { get; set; }
    public DateTime? LastLoginTime { get; set; }
    public DateTime CreateTime { get; set; }
    public int CreatorId { get; set; }
    public int LastModifierId { get; set; }
    public DateTime LastModifyTime { get; set; }
}

//公司类
public class Company : BaseModel
{
    public string  Name { get; set; }
    public DateTime CreateTime { get; set; }
    public int CreatorId { get; set; }
    public int? LastModifierId { get; set; }//可空
    public DateTime LastModifyTime { get; set; }
}
实体类与实体类基类

 

/// <summary>
/// 静态常量类
/// </summary>
public class StaticConstant
{
    /// <summary>
    /// sqlServer数据库连接字符串
    /// </summary>
    public static string SqlServerConnString = ConfigurationManager.ConnectionStrings["Customers"].ConnectionString;

    private static string DALTypeDll = ConfigurationManager.AppSettings["DALTypeDLL"];
    /// <summary>
    /// Dll名称(命名空间)
    /// </summary>
    public static string DAlDllName = DALTypeDll.Split(',')[0];
    /// <summary>
    /// 类型名称
    /// </summary>
    public static string DALTypeName = DALTypeDll.Split(',')[1];
}
静态常量类
/// <summary>
/// 仅修饰属性的特性:列名
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute:Attribute
{
    public ColumnAttribute(string name)
    {
        _name = name;    
    }
    private string _name = null;
    public string GetColumnName()
    {
        return _name;
    }
}

//中文名特性
public class ChinaNameAttribute:Attribute
{
    public ChinaNameAttribute(string name)
    {
        _name = name;
    }
    private string _name = null;
    public string GetChinaName()
    {
        return _name;
    }
}
映射的特性
//抽象特性类
public abstract class AbstractValidateAttribute : Attribute
{
    //抽象特性类的抽象方法
    public abstract bool Validate(object value);
}

//Email验证特性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class EmailAttribute : AbstractValidateAttribute
{
    public override bool Validate(object oValue)
    {
        return oValue != null
            && Regex.IsMatch(oValue.ToString(), @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
    }
}

//长度验证特性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class LengthAttribute : AbstractValidateAttribute
{
    private int _min = 0;
    private int _max = 0;
    public LengthAttribute(int min, int max)
    {
        _min = min;
        _max = max;
    }
    public override bool Validate(object value)
    {
        if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
        {
            int length = value.ToString().Length;
            if (length >= _min && length <= _max)
            {
                return true;
            }
        }
        return false;
    }
}

//数值范围验证特性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class LongAttribute : AbstractValidateAttribute
{
    private long _min = 0;
    private long _max = 0;
    public LongAttribute(long min, long max)
    {
        _min = min;
        _max = max;
    }
    public override bool Validate(object value)
    {
        if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
        {
            long result = 0;
            if (long.TryParse(value.ToString(), out result))
            {
                if (result >= _min && result <= _max)
                {
                    return true;
                }
            }
        }
        return false;
    }
}

//电话验证特性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class MobileAttribute : AbstractValidateAttribute
{
    public override bool Validate(object oValue)
    {
        return oValue != null && Regex.IsMatch(oValue.ToString(), @"^[1]+[3,5]+\d{9}");
    }
}

//正则表达式验证特性
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class RegexAttribute : AbstractValidateAttribute
{
    private string _RegexExpression = string.Empty;
    public RegexAttribute(string regex)
    {
        this._RegexExpression = regex;
    }

    public override bool Validate(object oValue)
    {
        return oValue != null && Regex.IsMatch(oValue.ToString(), _RegexExpression);
    }
}
验证的特性
public static class AttributeHelper
{
    /// <summary>
    /// 判断该属性是否使用Column特性,如果使用Column特性,就返回特性指定的列名,如果未使用,就直接返回列名
    /// </summary>
    /// <param name="prop"></param>
    /// <returns></returns>
    public static string GetColumnName(this PropertyInfo prop)
    {
        if (prop.IsDefined(typeof(ColumnAttribute), true))
        {
            ColumnAttribute attribute = (ColumnAttribute)prop.GetCustomAttribute(typeof(ColumnAttribute), true);
            return attribute.GetColumnName();
        }
        else
        {
            return prop.Name;
        }
    }

    //中文名
    public static string GetChinaName(this PropertyInfo prop)
    {
        if (prop.IsDefined(typeof(ChinaNameAttribute), true))
        {
            ChinaNameAttribute attribute = (ChinaNameAttribute)prop.GetCustomAttribute(typeof(ChinaNameAttribute), true);
            return attribute.GetChinaName();
        }
        else
        {
            return prop.Name;
        }
    }

    public static bool Validate<T>(this T t) where T : BaseModel
    {
        Type type = t.GetType();
        foreach (PropertyInfo info in type.GetProperties())
        {//通过反射遍历所有的属性
            if (info.IsDefined(typeof(AbstractValidateAttribute), true))
            {//判断是否有配置该特性的属性
                //如果有配置该特性的属性,得到该属性所有的AbstractValidateAttribute特性
                object[] attributeArray = info.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
                foreach (AbstractValidateAttribute attribute in attributeArray)//在这一步转换的
                {//遍历所有的特性
                    if (!attribute.Validate(info.GetValue(t)))
                    {//检测该方法,判断该值
                        return false;
                        //throw new Exception($"{info.Name}的值{info.GetValue(t)}设置不正确");//throw与return二选一
                    }
                }
            }
        }
        return true;
    }
}
特性帮助类
public interface IBaseDAL
{
    //查询Id
    T Find<T>(int id) where T : BaseModel;

    //查询列表
    List<T> FindAll<T>() where T : BaseModel;

    //更新实体
    void Update<T>(T t) where T : BaseModel;

    //实体删除
    void Delete<T>(T t) where T : BaseModel;

    //Id删除
    void Delete<T>(int id) where T : BaseModel;

    //插入实体
    void Insert<T>(T t) where T : BaseModel;
}
接口
//实现接口的类
public class BaseDAL: IBaseDAL
{
    // 泛型访问数据库的方法,用BaseModel约束
    // 用id去查询单个实体
    public T Find<T>(int id) where T : BaseModel
    {
        string sql = $"{TSqlHelper<T>._findSql}{id}";
        List<T> listT = new List<T>();
        using (SqlConnection conn=new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            conn.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            listT = ReaderToList<T>(reader);
        }
        return listT.FirstOrDefault();
    }

    // 泛型访问数据库的方法,用BaseModel约束
    // 查询出所有数据表的全部数据列表
    public List<T> FindAll<T>() where T : BaseModel
    {
        string sql = $"{TSqlHelper<T>._findAllSql}";
        List<T> listT = new List<T>();
        using (SqlConnection conn=new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            conn.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            listT = ReaderToList<T>(reader);
        }
        return listT;
    }

    /// <summary>
        /// 显示中文名
        /// </summary>
        /// <typeparam name="T"></typeparam>
    public static void ShowChinaName<T>()
        {
            Type type = typeof(T);
            foreach (PropertyInfo prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(ChinaNameAttribute), true))
                {
                    ChinaNameAttribute attribute = (ChinaNameAttribute)prop.GetCustomAttribute(typeof(ChinaNameAttribute), true);
                    Console.WriteLine($"属性:[{prop.Name}]  中文名:[{attribute.GetChinaName()}]");
                }
                else
                {
                    Console.WriteLine($"属性:[{prop.Name}]  中文名:[null]");
                }
            }
        }

    //更新实体
    public void Update<T>(T t) where T : BaseModel
    {
        if (!t.Validate<T>())//在这里使用特性进行验证
        {
            throw new Exception("数据不正确");
        }
        Type type = typeof(T);
        var props = type.GetProperties().Where(p => !p.Name.Equals("Id"));
        string columns = string.Join(",", props.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}"));
        var paras = props.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
        //必须参数化 否则引号?   或者值里面还有引号
        string sql = $"update [{type.Name}] set {columns} where Id={t.Id}";
        using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddRange(paras);
            conn.Open();
            int result = cmd.ExecuteNonQuery();
            if (result==0)
            {
                throw new Exception("更新失败,Update数据不存在");
            }
        }
    }

    //实体删除
    public void Delete<T>(T t) where T : BaseModel
    {
        string sql = $"{TSqlHelper<T>._deleteSql}{t.Id}";
        using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            conn.Open();
            int result = cmd.ExecuteNonQuery();
            if (result == 0)
            {
                throw new Exception("删除失败,Delete数据不存在");
            }
        }
    }

    //Id删除
    public void Delete<T>(int id) where T : BaseModel
    {
        string sql = $"{TSqlHelper<T>._deleteSql}{id}";
        using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            conn.Open();
            int result = cmd.ExecuteNonQuery();
            if (result == 0)
            {
                throw new Exception("删除失败,Delete数据不存在");
            }
        }
    }

    //插入实体
    public void Insert<T>(T t) where T : BaseModel
    {
        Type type = typeof(T);
        var props = type.GetProperties().Where(p => !p.Name.Equals("Id"));
        string columns = string.Join(",", props.Select(p => $"[{p.GetColumnName()}]"));
        string values = string.Join(",", props.Select(p => $"@{p.GetColumnName()}"));
        var paras = props.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
        //必须参数化 否则引号?   或者值里面还有引号
        string sql = $"insert into [{type.Name}] ({columns}) values ({values})";
        using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
        {
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddRange(paras);
            conn.Open();
            int result = cmd.ExecuteNonQuery();
            if (result == 0)
            {
                throw new Exception("插入失败,Insert数据不存在");
            }
        }
    }

    #region 封装的方法
    // 查询一个SqlDataReaser的List集
    private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel
    {
        Type type = typeof(T);
        List<T> listT = new List<T>();
        while (reader.Read())
        {
            //这里得t必须声明在while{}中,如果不是,相当于只声明了一个t,后面每一个t都是对前一个重新赋值。
            T t = (T)Activator.CreateInstance(type);
            foreach (PropertyInfo prop in type.GetProperties())
            {
                object obj = reader[prop.GetColumnName()];
                if (obj is DBNull)
                {
                    obj = null;
                }
                prop.SetValue(t, obj);
            }
            listT.Add(t);
        }
        return listT;
    }
    #endregion
}
重要:实现接口的类(增查删改)
//泛型静态缓存
//可以理解这个类就是管理Sql语句的,因为发现sql语句有很多类似的地方,所以在此利用泛型进行了一些封装
//但是在实际封装之后发现封装的并不尽人意,查询和更新与插入是不一样的,更新与插入需要参数化
//即使这里封装了,但在引用的时候还是要再次使用type.GetProperties();

//最终决定能缓存就缓存,不能缓存就算了,所以就果断删除_insertSql和_updateSql
public class TSqlHelper<T> where T : BaseModel
{
    public static string _findSql = null;
    public static string _findAllSql = null;
    public static string _deleteSql = null;
    //public static string _updateSql = null;
    //public static string _insertSql = null;

    static TSqlHelper()
    {
        Type type = typeof(T);
        //删除仅需要类型
        _deleteSql = $"delete from {type.Name} where Id=";
        //查询需要Id
        var props = type.GetProperties();
        string columns = string.Join(",", props.Select(p => $"[{p.GetColumnName()}]"));
        _findSql = $"select {columns} from [{type.Name}] where Id=";
        _findAllSql= $"select {columns} from [{type.Name}]";
        //更新与插入不需要Id
        //var propsNonId = type.GetProperties().Where(p => !p.Name.Equals("Id"));
        //string columns0 = string.Join(",", propsNonId.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}"));
        //string columns1 = string.Join(",", propsNonId.Select(p => $"[{p.GetColumnName()}]"));
        //string values = string.Join(",", propsNonId.Select(p => $"@{p.GetColumnName()}"));
        //_updateSql = $"update [{type.Name}] set {columns0} where Id=";
        //_insertSql = $"insert into [{type.Name}] ({columns1}) values ({values})";
    }
}
泛型静态缓存
//简单工厂+配置文件+反射
public class DALFactory
{
    static DALFactory()
    {
        Assembly assembly = Assembly.Load(StaticConstant.DAlDllName);
        DALType = assembly.GetType(StaticConstant.DALTypeName);
    }
    private static Type DALType = null;
    public static IBaseDAL CreateInstance()
    {
        return (IBaseDAL)Activator.CreateInstance(DALType);
    }
}
简单工厂+配置文件+反射

 

/* 仔细分析增删改查方法,发现有很多重复代码,步骤细分如下
 * 【1】连接对象【2】Command对象【3】打开连接【4】个别需要添加参数【5】执行增删改方法【6】返回一个结果
 * 考虑使用泛型解耦,先说不同的地方,每个Command对象的sql语句不同,增删改方法不同,返回结果不同
 * 返回结果不同考虑使用泛型,带返回值的委托,微软已经给我们定义好了——>>Func
 * 代码如下:需要注意的一个地方Command对象
 */
public T ExecSQL<T>(string sql, Func<SqlCommand, T> func)
{
    using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
    {
        using (SqlCommand cmd = new SqlCommand(sql, conn))
        {
            try
            {
                conn.Open();
                T t = func.Invoke(cmd);
                return t;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

private T ExcuteSql<T>(string sql, Func<SqlCommand, T> func)
{
    using (SqlConnection conn = new SqlConnection(StaticConstant.SqlServerConnString))
    {
        using (SqlCommand command = new SqlCommand(sql, conn))
        {
            conn.Open();
            SqlTransaction sqlTransaction = conn.BeginTransaction();
            try
            {
                command.Transaction = sqlTransaction;
                T tResult = func.Invoke(command);
                sqlTransaction.Commit();
                return tResult;
            }
            catch (Exception ex)
            {
                sqlTransaction.Rollback();
                throw ex;
            }
        }
    }
}

public T Find<T>(int id) where T : BaseModel
{
    Type type = typeof(T);
    string sql = $"{TSqlHelper<T>.FindSql}{id};";
    Func<SqlCommand, T> func = new Func<SqlCommand, T>(command =>
       {
           SqlDataReader reader = command.ExecuteReader();
           List<T> list = this.ReaderToList<T>(reader);
           T tnew = list.FirstOrDefault();
           return tnew;
       });
    T t = ExecSQL<T>(sql, func);
    return t;
}

public List<T> FindAll<T>() where T : BaseModel
{
    Type type = typeof(T);
    string sql = TSqlHelper<T>.FindAllSql;
    List<T> list = new List<T>();
    Func<SqlCommand, List<T>> func = (command =>
         {
             SqlDataReader reader = command.ExecuteReader();
             list = this.ReaderToList<T>(reader);
             return list;
         });
    list = ExecSQL<List<T>>(sql, func);
    return list;
}

public void Update<T>(T t) where T : BaseModel
{
    Type type = typeof(T);
    var propArray = type.GetProperties().Where(p => !p.Name.Equals("Id"));
    string columnString = string.Join(",", propArray.Select(p => $"[{p.GetColumnName()}]=@{p.GetColumnName()}"));
    var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();
    string sql = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}";
    Func<SqlCommand, int> func = (command =>
      {
          command.Parameters.AddRange(parameters);
          int iResult = command.ExecuteNonQuery();
          return iResult;
      });
    int result = ExecSQL<int>(sql, func);
    if (result==0)
    {
        throw new Exception("Update数据不存在");
    }
}
增查删改有重复代码,使用委托解耦