C#泛型委托的概念及实例解析

泛型委托,可以分开看,是泛型和委托的组合。

泛型:

对于泛型的使用之前的文章已经写了,可以参考C#之泛型_故里2130的博客-CSDN博客,不清楚的可以先看一看。

委托:

这个比较简单,从delegate关键字到匿名委托方法,再到Action和Func,Action是无返回值的委托,Func是有返回值的委托,现在基本上都是基于Action和Func来写委托的,有些和业务逻辑连接起来写匿名委托方法。

现在我们看一个案例

1.建立一个控制台程序,建立student类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp4
{
    public class Student
    {
        public int ID { get; set; }
 
        public string name { get; set; }
 
        public int age { get; set; }
 
        public string sex { get; set; }
 
        public string grade { get; set; }
    }
}
2.我们初始化数据后,分别进行查询操作

进行3次查询后,然后分别输出对应的名字,这样是我们平时的做法,一点错误都没有。可以看到都是where的功劳,现在我们可以使用泛型委托自己自定义一个和where一样的功能。

只要遇到代码有重复的逻辑,就可以使用委托

using System;
using System.Collections.Generic;
using System.Linq;
 
namespace ConsoleApp4
{
    static class Program
    {
        static void Main(string[] args)
        {
            List<Student> student = new List<Student>();
            student.Add(new Student() { age = 20, grade = "一班", ID = 1, name = "张三", sex = "男" });
            student.Add(new Student() { age = 21, grade = "一班", ID = 2, name = "李四", sex = "女" });
            student.Add(new Student() { age = 22, grade = "二班", ID = 3, name = "王五", sex = "女" });
            student.Add(new Student() { age = 23, grade = "二班", ID = 4, name = "老六", sex = "男" });
 
            var stu = student.Where(s => s.grade == "一班");
            foreach (var item in stu)
            {
                Console.WriteLine(item.name);
            }
 
            Console.WriteLine("==============分界线==============");
            var stu1 = student.Where(s => s.sex == "男");
            foreach (var item in stu1)
            {
                Console.WriteLine(item.name);
            }
 
            Console.WriteLine("==============分界线==============");
            var stu2 = student.Where(s => s.sex == "男" && s.age > 20);
            foreach (var item in stu2)
            {
                Console.WriteLine(item.name);
            }
        }
    }
}
3. 结果

4. 建立一个WhereNew类,表示泛型委托类

特别要注意,Invoke是执行方法,传入参数,返回bool值

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp4
{
    public static class WhereNew
    {
        public static IEnumerable<T> NewWhere<T>(this IEnumerable<T> items, Func<T, bool> func)
        {
            List<T> ls = new List<T>();
            foreach (var item in items)
            {
                if (func.Invoke(item)) //执行方法,传入参数,返回bool值
                {
                    ls.Add(item);
                }
            }
            return ls;
        }
    }
}
5.使用

using System;
using System.Collections.Generic;
using System.Linq;
 
namespace ConsoleApp4
{
    static class Program
    {
        static void Main(string[] args)
        {
            List<Student> student = new List<Student>();
            student.Add(new Student() { age = 20, grade = "一班", ID = 1, name = "张三", sex = "男" });
            student.Add(new Student() { age = 21, grade = "一班", ID = 2, name = "李四", sex = "女" });
            student.Add(new Student() { age = 22, grade = "二班", ID = 3, name = "王五", sex = "女" });
            student.Add(new Student() { age = 23, grade = "二班", ID = 4, name = "老六", sex = "男" });
 
            //var stu = student.Where(s => s.grade == "一班");
            IEnumerable<Student> stu = student.NewWhere(s => s.grade == "一班");
            foreach (var item in stu)
            {
                Console.WriteLine(item.name);
            }
 
            Console.WriteLine("==============分界线==============");
            //var stu1 = student.Where(s => s.sex == "男");
            IEnumerable<Student> stu1 = student.NewWhere(s => s.sex == "男");
            foreach (var item in stu1)
            {
                Console.WriteLine(item.name);
            }
 
            Console.WriteLine("==============分界线==============");
            //var stu2 = student.Where(s => s.sex == "男" && s.age > 20);
            IEnumerable<Student> stu2 = student.NewWhere(s => s.sex == "男" && s.age > 20);
            foreach (var item in stu2)
            {
                Console.WriteLine(item.name);
            }
        }
    }
}
6.效果,和之前where的效果是一样的

  

从中我们可以看到where和自定义的NewWhere,功能是一样的,也就是说where也是用了泛型委托。泛型就是T,委托就是func。所以,我们可以传递一个类型的数据进去,然后再传递一个方法进去,自动就加工返回值了。

  public static IEnumerable<T> NewWhere<T>(this IEnumerable<T> items, Func<T, bool> func)
        {
            List<T> ls = new List<T>();          //T就是泛型,委托就是func
            foreach (var item in items)
            {
                if (func.Invoke(item)) //执行方法,传入参数,返回bool值
                {
                    ls.Add(item);
                }
            }
            return ls;
        }
/************************************************************************

C#委托之一例看懂泛型委托

以为委托在编程中频繁使用,所以微软为使开发者方便使用委托,省去繁琐的重复定义 。给我们提供了三种定义好的泛型委托,分别是 Action、Func和Predicate。下面分别介绍

Action: 此委托绑定的方法不能有返回值,方法可以有至多16个参数,当然也可以无参数;

Func : 此委托绑定的方法可以有返回值。方法可以有至多16个参数;

Predicate: 此委托返回值为布尔型,方法只能又一个参数;

知道了这三个委托的特点下面我们通过这个例子来进一步熟悉他们的用法。 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace 委托demo_泛型委托

{

    //泛型委托demo  2022年10月8日21:19:55

    //使用的是系统定义好的 Action,Func,Predicate三个泛型委托。他们各自使用场景如下

    class Program

    {  

        static void Main(string[] args)

        {

            //无参数的委托

            Action del1 = new Action(toSayHello);

            del1();

            //有参数的委托

            Action<string> del2 = new Action<string>(toSayAnything);

            del2("我叫lhz");

            //有参数,有返回值的委托

            Func<int ,int,int > del3=new Func<int,int,int>(toAdd);

            Console.WriteLine(toAdd(5, 2));

            //参数只能有一个,且返回值类型为bool型

            Predicate<int> del4=new Predicate<int>(isAdult);

            Console.WriteLine("是否成年:" + del4(13));

            Console.ReadKey();

        }

        //无参数,无返回值方法

        public static void toSayHello()

        {

            Console.WriteLine("Hello!");

        }

        //有参数,无返回值方法

        public static void toSayAnything(string s)

        {

            Console.WriteLine(s);

        }

        //输入两个整数 进行相加的方法

        public static int toAdd(int a, int b)

        {

            return a + b;

        }

        //输入年龄 判断是否成年的方法

        public static bool isAdult(int age)

        {

            if (age > 18)

                return true;

            else

                return false;

        }

    }

}

  总结,前面我们说过委托的四个步骤,因为我们使用的泛型委托是系统自定义好的,所以就省略了 咱们的步骤1:委托的定义

利用三个泛型的特点结合使用场景灵活的选择委托便可以。

/*********************************************************************************

C#泛型之泛型委托

在看泛型委托之前还需要先了解委托的概念。
这里讲的委托有两种类型一种是有返回值的,另一种是事件委托。

    //定义有返回值的委托 
    public delegate string GenricDelegate<T, S>(T title, S author);

    //定义事件委托。
    public delegate void GenricDelegateEnent<E,P>(E Name,P Address);

    public class GenericDelegateClass<V,F>
    {
        //声明委托
        public GenricDelegate<V, F> GdeleValue;
        //声明事件委托
        public event GenricDelegateEnent<V, F> GdEvent = null;

        public string GetValues(V title, F author)
        {
            //调用委托
            return GdeleValue(title, author);
        }

        public GenericDelegateClass()
        {
        }

        public void InvokeEvent(V name, F address)
        {
            if (GdEvent != null)
            {
                //调用委托
                GdEvent(name, address);
            }
        }
    }

上面我们定义及调用了泛型委托,接下来就来梆定委托。

        private void btnDelegate_Click(object sender, EventArgs e)
        {

            GenericDelegateClass<string, string> gd = new GenericDelegateClass<string, string>();
            //将DelegateReturn事件梆定给GdeleValue
            gd.GdeleValue = new GenricDelegate<string, string>(DelegateReturn);
            //将GenericEvent事件梆定给GdEvent
            gd.GdEvent += new GenricDelegateEnent<string, string>(GenericEvent<string,string>);
        }

        public string DelegateReturn<T,S>(T title,S author)
        {
            return title.ToString() + author;
        }

        private void GenericEvent<V, F>(V name, F address)
        {
            //
        }

在这里我们看到我在梆定DelegateReturn的时候并没有带泛型参数。在这里的泛型参数其实是没什么意义的。因为他的类型取决于调用委托的方法的类型。也就是在前面那段代码中InvokeEvent方法的类型,这里的DelegateReturn要用泛型方法是可以随时跟InvokeEvent的参数类型保持一至。这样梆定后我们再来调用gd.GetValues("my generic post","fastyou");这样调用的其实就是DelegateReturn的方法,这就是委托的好处了,同样调用gd.InvokeEvent("my generic post","fastyou");就是GenericEvent方法。

/*********************************************************************

C#内置泛型委托之Func委托

一、什么是Func委托

Func委托代表有返回类型的委托

二、Func委托定义

查看Func的定义:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

using System.Runtime.CompilerServices;

namespace System

{

    //

    // 摘要:

    //     封装一个方法,该方法具有两个参数,并返回由 TResult 参数指定的类型的值。

    //

    // 参数:

    //   arg1:

    //     此委托封装的方法的第一个参数。

    //

    //   arg2:

    //     此委托封装的方法的第二个参数。

    //

    // 类型参数:

    //   T1:

    //     此委托封装的方法的第一个参数的类型。

    //

    //   T2:

    //     此委托封装的方法的第二个参数的类型。

    //

    //   TResult:

    //     此委托封装的方法的返回值类型。

    //

    // 返回结果:

    //     此委托封装的方法的返回值。

    [TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]

    public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

}

你会发现,Func其实就是有多个输出参数并且有返回值的delegate。

3、示例

Func至少0个输入参数,至多16个输入参数,根据返回值泛型返回。必须有返回值,不可void。

Func<int> 表示没有输入参参,返回值为int类型的委托。

Func<object,string,int> 表示传入参数为object, string ,返回值为int类型的委托。

Func<object,string,int> 表示传入参数为object, string, 返回值为int类型的委托。

Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型),返回值为int类型的委托。

代码示例如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunDemo

{

    class Program

    {

        static void Main(string[] args)

        {

            // 无参数,只要返回值

            Func<int> fun1 = new Func<int>(FunWithNoPara);

            int result1= fun1();

            Console.WriteLine(result1);

            Console.WriteLine("----------------------------");

            Func<int> fun2 = delegate { return 19; };

            int result2 = fun2();

            Console.WriteLine(result2);

            Console.WriteLine("----------------------------");

            Func<int> fun3 = () => { return 3; };

            int result3 = fun3();

            Console.WriteLine(result3);

            Console.WriteLine("----------------------------");

            //有一个参数,一个返回值

            Func<int, int> fun4 = new Func<int, int>(FunWithPara);

            int result4 = fun4(4);

            Console.WriteLine($"这里是一个参数一个返回值的方法,返回值是:{result4}");

            Console.WriteLine("----------------------------");

            // 使用委托

            Func<int, string> fun5 = delegate (int i) { return i.ToString(); };

            string result5 = fun5(5);

            Console.WriteLine($"这里是一个参数一个返回值的委托,返回值是:{result5}");

            Console.WriteLine("----------------------------");

            // 使用匿名委托

            Func<int, string> fun6 = (int i) =>

            {

                return i.ToString();

            };

            string result6 = fun6(6);

            Console.WriteLine($"这里是一个参数一个返回值的匿名委托,返回值是:{result6}");

            Console.WriteLine("----------------------------");

            // 多个输入参数

            Func<int, string, bool> fun7 = new Func<int, string, bool>(FunWithMultiPara);

            bool result7 = fun7(2, "2");

            Console.WriteLine($"这里是有多个输入参数的方法,返回值是:{result7}");

            Console.WriteLine("----------------------------");

            // 使用委托

            Func<int, string, bool> fun8 = delegate (int i, string s)

            {

                return i.ToString().Equals(s) ? true : false;

            };

            bool result8 = fun8(2, "abc");

            Console.WriteLine($"这里是有多个输入参数的委托,返回值是:{result8}");

            Console.WriteLine("----------------------------");

            // 使用匿名委托

            Func<int, string, bool> fun9 = (int i, string s) =>

            {

                return i.ToString().Equals(s) ? true : false;

            };

            bool result9 = fun9(45, "ert");

            Console.WriteLine($"这里是有多个输入参数的匿名委托,返回值是:{result9}");

            Console.ReadKey();

        }

        static int FunWithNoPara()

        {

            return 10;

        }

        static int FunWithPara(int i)

        {

            return i;

        }

        static bool FunWithMultiPara(int i,string s)

        {

            return i.ToString().Equals(s) ? true : false;

        }

    }

}

运行结果:

4、真实示例

在下面的示例中,利用Func委托封装数据库通用访问类。

1、定义BaseModel基类

1

2

3

4

5

6

7

8

9

10

11

12

13

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.Model

{

    public class BaseModel

    {

        public int Id { get; set; }

    }

}

2、定义Student类继承自BaseModel基类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.Model

{

    public class Student : BaseModel

    {

        public string Name { get; set; }

        public int Age { get; set; }

        public int Sex { get; set; }

        public string Email { get; set; }

    }

}

3、定义数据库访问方法接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

using FunApplication.Model;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.IDAL

{

    public interface IBaseDAL

    {

        T Query<T>(int id) where T : BaseModel;

        List<T> QueryAll<T>() where T : BaseModel;

        int Insert<T>(T t) where T : BaseModel;

        int Update<T>(T t) where T : BaseModel;

        int Delete<T>(int id) where T : BaseModel;

    }

}

4、定义属性帮助类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

using System;

using System.Collections.Generic;

using System.Linq;

using System.Reflection;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.AttributeExtend

{

    public static class AttributeHelper

    {

        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;

            }

        }

    }

}

5、定义ColumnAttribute类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.AttributeExtend

{

    [AttributeUsage(AttributeTargets.Property)]

    public class ColumnAttribute : Attribute

    {

        public ColumnAttribute(string name)

        {

            this._Name = name;

        }

        private string _Name = null;

        public string GetColumnName()

        {

            return this._Name;

        }

    }

}

6、定义数据库方法接口实现类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

using FunApplication.IDAL;

using FunApplication.Model;

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data.SqlClient;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Reflection;

using FunApplication.AttributeExtend;

namespace FunApplication.DAL

{

    public  class BaseDAL : IBaseDAL

    {

        // 数据库链接字符串

        private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString;

        public  int Delete<T>(int id) where T : BaseModel

        {

            int result = 0;

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                string strSQL = "delete from Student where Id=@Id";

                SqlParameter para = new SqlParameter("Id", id);

                SqlCommand command = new SqlCommand(strSQL, conn);

                command.Parameters.Add(para);

                conn.Open();

                result = command.ExecuteNonQuery();

            }

            return result;

        }

        public int Insert<T>(T t) where T : BaseModel

        {

            int result = 0;

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                Type type = typeof(T);

                var propArray = type.GetProperties().Where(p => p.Name != "Id");

                string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) ";

                SqlCommand command = new SqlCommand(strSQL, conn);

                var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();

                command.Parameters.AddRange(parameters);

                conn.Open();

                result = command.ExecuteNonQuery();

            }

                return result;

        }

        public T Query<T>(int id) where T : BaseModel

        {

            Type type = typeof(T);

            string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));

            string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id={id}";

            T t = null;// (T)Activator.CreateInstance(type);

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                SqlCommand command = new SqlCommand(sql, conn);

                conn.Open();

                SqlDataReader reader = command.ExecuteReader();

                List<T> list = this.ReaderToList<T>(reader);

                t = list.FirstOrDefault();         

            }

            return t;

        }

        public List<T> QueryAll<T>() where T : BaseModel

        {

            Type type = typeof(T);

            string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));

            string sql = $"SELECT {columnString} FROM [{type.Name}] ";

            List<T> list = new List<T>();

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                SqlCommand command = new SqlCommand(sql, conn);

                conn.Open();

                SqlDataReader reader = command.ExecuteReader();

                list = this.ReaderToList<T>(reader);

            }

            return list;

        }

        public int Update<T>(T t) where T : BaseModel

        {

            int result = 0;

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                Type type = typeof(T);

                var propArray = type.GetProperties().Where(p => p.Name != "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 strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}";

                SqlCommand command = new SqlCommand(strSQL, conn);

                command.Parameters.AddRange(parameters);

                conn.Open();

                result = command.ExecuteNonQuery();           

            }

            return result;

        }

        private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel

        {

            Type type = typeof(T);

            List<T> list = new List<T>();

            while (reader.Read())//表示有数据  开始读

            {

                T t = (T)Activator.CreateInstance(type);

                foreach (var prop in type.GetProperties())

                {

                    object oValue = reader[prop.GetColumnName()];

                    if (oValue is DBNull)

                        oValue = null;

                    prop.SetValue(t, oValue);//除了guid和枚举

                }

                list.Add(t);

            }

            return list;

        }

    }

}

7、在Main()方法中调用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

using FunApplication.DAL;

using FunApplication.Model;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication

{

    class Program

    {

        static void Main(string[] args)

        {

            #region MyRegion

            BaseDAL dal = new BaseDAL();

            // 查询

            Student student = dal.Query<Student>(2);

            Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");

            Console.WriteLine("----------------------------");

            // 查询所有

            List<Student> list = dal.QueryAll<Student>();

            Console.WriteLine($"集合个数:{list.Count}");

            Console.WriteLine("----------------------------");

            // 插入

            Student studentIns = new Student()

            {

                Name = "小明",

                Age = 20,

                Sex = 2,

                Email = "[email protected]"

            };

            bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;

            Console.WriteLine($"插入执行结果:{resultIns}");

            Console.WriteLine("----------------------------");

            // 更新

            Student studentUpd = new Student()

            {

                Id = 1,

                Name = "zhangsan1234",

                Age = 20,

                Sex = 2,

                Email = "[email protected]"

            };

            bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false;

            Console.WriteLine($"更新执行结果:{resultUpd}");

            Console.WriteLine("----------------------------");

            // 删除

            bool resultDel = dal.Delete<Student>(3) > 0 ? true : false;

            Console.WriteLine($"删除执行结果:{resultDel}");

            #endregion

            Console.ReadKey();

        }

    }

}

8、结果

9、优化

仔细观察上面步骤7中的代码,你会发现在每个方法中都有重复的代码,打开链接,执行SqlCommand命令,那么这些重复的代码能不能提取到一个公共的方法中进行调用呢?答案是可以的,那就是利用Func委托,看下面优化后的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

using FunApplication.AttributeExtend;

using FunApplication.IDAL;

using FunApplication.Model;

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Data.SqlClient;

using System.Linq;

using System.Reflection;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication.DAL

{

    public class FunBaseDAL : IBaseDAL

    {

        // 数据库链接字符串

        private static string strConn = ConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString;

        public int Delete<T>(int id) where T : BaseModel

        {

            Type type = typeof(T);

            string sql = $"delete from {type.Name} where Id=@Id";

            Func<SqlCommand, int> func = (SqlCommand command) =>

            {              

                SqlParameter para = new SqlParameter("Id", id);

                command.Parameters.Add(para);

                return command.ExecuteNonQuery();

            };

            return ExcuteSql<int>(sql, func);

        }

        public int Insert<T>(T t) where T : BaseModel

        {

            int result = 0;

            Type type = typeof(T);

            var propArray = type.GetProperties().Where(p => p.Name != "Id");

            string strSQL = "insert into Student Values (@Name,@Age,@Sex,@Email) ";

            var parameters = propArray.Select(p => new SqlParameter($"@{p.GetColumnName()}", p.GetValue(t) ?? DBNull.Value)).ToArray();

            Func<SqlCommand, int> func = (SqlCommand command) =>

            {

                command.Parameters.AddRange(parameters);

                return command.ExecuteNonQuery();

            };

            result = ExcuteSql<int>(strSQL, func);

            return result;

        }

        public T Query<T>(int id) where T : BaseModel

        {

            Type type = typeof(T);

            string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));

            string sql = $"SELECT {columnString} FROM [{type.Name}] WHERE Id=@Id";

            T t = null;

            DataTable dt = new DataTable();

             

            Func<SqlCommand, T> func = (SqlCommand command) =>

            {

                SqlParameter para = new SqlParameter("@Id", id);

                command.Parameters.Add(para);

                SqlDataAdapter adapter = new SqlDataAdapter(command);

                //SqlDataReader reader = command.ExecuteReader();

                //List<T> list = this.ReaderToList<T>(reader);

                adapter.Fill(dt);

                List<T> list = ConvertToList<T>(dt);

                T tResult = list.FirstOrDefault();

                return tResult;

            };

            t = ExcuteSql<T>(sql, func);

            return t;

        }

        public List<T> QueryAll<T>() where T : BaseModel

        {

            Type type = typeof(T);

            string columnString = string.Join(",", type.GetProperties().Select(p => $"[{p.GetColumnName()}]"));

            string sql = $"SELECT {columnString} FROM [{type.Name}] ";

            T t = null;

            Func<SqlCommand, List<T>> func = (SqlCommand command) =>

            {

                SqlDataReader reader = command.ExecuteReader();

                List<T> list = this.ReaderToList<T>(reader);

                return list;

            };

            return ExcuteSql<List<T>>(sql, func);

        }

        public int Update<T>(T t) where T : BaseModel

        {

            int result = 0;

            Type type = typeof(T);

            var propArray = type.GetProperties().Where(p => p.Name != "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 strSQL = $"UPDATE [{type.Name}] SET {columnString} WHERE Id={t.Id}";

            Func<SqlCommand, int> func = (SqlCommand command) =>

            {

                command.Parameters.AddRange(parameters);

                return command.ExecuteNonQuery();

            };

            result = ExcuteSql<int>(strSQL, func);

            return result;

        }

        //多个方法里面重复对数据库的访问  想通过委托解耦,去掉重复代码

        private T ExcuteSql<T>(string sql, Func<SqlCommand, T> func)

        {

            using (SqlConnection conn = new SqlConnection(strConn))

            {

                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;

                    }

                }

            }

        }

        private List<T> ReaderToList<T>(SqlDataReader reader) where T : BaseModel

        {

            Type type = typeof(T);

            List<T> list = new List<T>();

            while (reader.Read())//表示有数据  开始读

            {

                T t = (T)Activator.CreateInstance(type);

                foreach (var prop in type.GetProperties())

                {

                    object oValue = reader[prop.GetColumnName()];

                    if (oValue is DBNull)

                        oValue = null;

                    prop.SetValue(t, oValue);//除了guid和枚举

                }

                list.Add(t);

            }

            reader.Close();

            return list;

        }

    }

}

10、在Main()方法中调用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

using FunApplication.DAL;

using FunApplication.Model;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace FunApplication

{

    class Program

    {

        static void Main(string[] args)

        {

            #region 传统实现

            //BaseDAL dal = new BaseDAL();

             查询

            //Student student = dal.Query<Student>(2);

            //Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");

            //Console.WriteLine("----------------------------");

             查询所有

            //List<Student> list = dal.QueryAll<Student>();

            //Console.WriteLine($"集合个数:{list.Count}");

            //Console.WriteLine("----------------------------");

             插入

            //Student studentIns = new Student()

            //{

            //    Name = "小明",

            //    Age = 20,

            //    Sex = 2,

            //    Email = "[email protected]"

            //};

            //bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;

            //Console.WriteLine($"插入执行结果:{resultIns}");

            //Console.WriteLine("----------------------------");

             更新

            //Student studentUpd = new Student()

            //{

            //    Id = 1,

            //    Name = "zhangsan1234",

            //    Age = 20,

            //    Sex = 2,

            //    Email = "[email protected]"

            //};

            //bool resultUpd = dal.Update<Student>(studentUpd) > 1 ? true : false;

            //Console.WriteLine($"更新执行结果:{resultUpd}");

            //Console.WriteLine("----------------------------");

             删除

            //bool resultDel = dal.Delete<Student>(5) > 1 ? true : false;

            //Console.WriteLine($"删除执行结果:{resultDel}");

            #endregion

            #region 利用委托

            // 查询

            FunBaseDAL dal = new FunBaseDAL();

            Student student = dal.Query<Student>(1);

            Console.WriteLine($"姓名:{student.Name},年龄:{student.Age},Email地址:{student.Email}");

            Console.WriteLine("----------------------------");

            // 查询所有

            List<Student> list = dal.QueryAll<Student>();

            Console.WriteLine($"集合个数:{list.Count}");

            Console.WriteLine("----------------------------");

            // 插入

            Student studentIns = new Student()

            {

                Name = "tom",

                Age = 19,

                Sex = 1,

                Email = "[email protected]"

            };

            bool resultIns = dal.Insert<Student>(studentIns) > 0 ? true : false;

            Console.WriteLine($"插入执行结果:{resultIns}");

            Console.WriteLine("----------------------------");

            List<Student> list1 = dal.QueryAll<Student>();

            Console.WriteLine($"插入后集合个数:{list1.Count}");

            Console.WriteLine("----------------------------");

            // 更新

            Student studentUpd = new Student()

            {

                Id = 2,

                Name = "马六123",

                Age = 20,

                Sex = 2,

                Email = "[email protected]"

            };

            bool resultUpd = dal.Update<Student>(studentUpd) > 0 ? true : false;

            Console.WriteLine($"更新执行结果:{resultUpd}");

            Console.WriteLine("----------------------------");

            // 删除

            bool resultDel = dal.Delete<Student>(8) > 0 ? true : false;

            Console.WriteLine($"删除执行结果:{resultDel}");

            List<Student> list2 = dal.QueryAll<Student>();

            Console.WriteLine($"删除后集合个数:{list2.Count}");

            Console.WriteLine("----------------------------");

            #endregion

            Console.ReadKey();

        }

    }

}

11、结果

注意

在使用SqlDataReader的时候有时会报错:“已有打开的与此Command相关联的DataReader,必须先将它关闭”。

同时打开两个或循环多个sqldatareader会出现以上错误。因为用的是sqldatareader做数据库的数据读取,sqlconnection开启没有关闭。

一个SqlConnection只能执行一次事务,没用一次必须关闭然后再开启。上面我只用了一次没有关闭,直接开启所以会报错。解决方案有如下两种:

1、其实不用多次打开在开启,那样实现起来很麻烦。直接在连接字符串的后面加上MultipleActiveResultSets=true即可。 配置文件定义如下:

1

2

3

4

5

6

7

8

9

10

11

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <connectionStrings>

    <!--<add name="DbConnection" connectionString="Server=.;Initial Catalog=MyDb;User ID=sa;Password=123456;MultipleActiveResultSets=True"/>-->

    <!--配置文件里面添加MultipleActiveResultSets=True-->

    <add name="DbConnection" connectionString="Server=.;Initial Catalog=MyDb;User ID=sa;Password=123456;MultipleActiveResultSets=True"/>

  </connectionStrings>

    <startup>

        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />

    </startup>

</configuration>

2、使用DataTable

在上面是使用的SqlDataReader读取数据,然后转换成List<T>,可以用DataTable代替SqlDataReader,这样就不会报错了,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

/// <summary>

/// 将DataTable转换成List

/// </summary>

/// <typeparam name="T"></typeparam>

/// <param name="dt"></param>

/// <returns></returns>

private List<T> ConvertToList<T>(DataTable dt) where T:BaseModel

{

      Type type = typeof(T);

      List<T> list = new List<T>();

      foreach(DataRow dr in dt.Rows)

      {

          T t = (T)Activator.CreateInstance(type);

          foreach(PropertyInfo prop in type.GetProperties())

          {

               object value = dr[prop.GetColumnName()];

               if(value is DBNull)

               {

                    value = null;

               }

               prop.SetValue(t, value);

          }

          list.Add(t);

        }

        return list;

}

猜你喜欢

转载自blog.csdn.net/u011555996/article/details/129032984