asp.net泛型结合反射的简单Base封装
泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性。泛型为.NET框架引入了类型参数(type parameters)的概念。类型参数使得设计类和方法时,不必确定一个或多个具体参数,其的具体参数可延迟到客户代码中声明、实现。这意味着使用泛型的类型参数T,写一个类MyList
这应该是微软干的一件大好事,关于 T 这个类型,要说的就太多了。在编译时会生成一个`1【相对于一个参数的时候,多个会生成对应的占位符】的占位符,就是说,这个类型参数不确定但是我又具体不知道现在这个参数类型到底是什么类的。那好,我不确定我就先把你放一个占位子的东西放到,在我调用的时候我在传进来。
关于泛型的约束
//指定GenericMethod方法用以执行类型为X的参数 public T GenericMethod(T t) { return default(T); } //泛型方法的泛型参数的约束 public void GenericMethod (T t) where T:IComparable { // } //泛型类的约束, T必须有一个无参的构造函数 public class GenericClass where T: new() { } //结构体约束 public class GenericClass where T : struct { }
下表列出了五类约束:
|
约束 |
描述 |
|
where T: struct |
类型参数必须为值类型。 |
|
where T : class |
类型参数必须为引用类型。 |
|
where T : new() |
类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new()约束必须放在最后。 |
|
where T : |
类型参数必须是指定的基类型或是派生自指定的基类型。 |
|
where T : |
类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。 |
类型参数的约束,增加了可调用的操作和方法的数量。这些操作和方法受约束类型及其派生层次中的类型的支持。因此,设计泛型类或方法时,如果对泛型成员执行任何赋值以外的操作,或者是调用System.Object中所没有的方法,就需要在类型参数上使用约束。
无限制类型参数的一般用法
没有约束的类型参数,如公有类MyClass
l 不能使用运算符 != 和 == ,因为无法保证具体的类型参数能够支持这些运算符。
l 它们可以与System.Object相互转换,也可显式地转换成任何接口类型。
l 可以与null比较。如果一个无限制类型参数与null比较,当此类型参数为值类型时,比较的结果总为false。
无类型约束
当约束是一个泛型类型参数时,它就叫无类型约束(Naked type constraints)。当一个有类型参数成员方法,要把它的参数约束为其所在类的类型参数时,无类型约束很有用。如下例所示:
class List
{
//...
void Add(List items) where U:T {…}
}
在上面的示例中, Add方法的上下文中的T,就是一个无类型约束;而List类的上下文中的T,则是一个无限制类型参数。
无类型约束也可以用在泛型类的定义中。注意,无类型约束一定也要和其它类型参数一起在尖括号中声明:
//naked type constraint
public class MyClass
因为编译器只认为无类型约束是从System.Object继承而来,所以带有无类型约束的泛型类的用途十分有限。当你希望强制两个类型参数具有继承关系时,可对泛型类使用无类型约束。
泛型类
泛型类封装了不针对任何特定数据类型的操作。泛型类常用于容器类,如链表、哈希表、栈、队列、树等等。这些类中的操作,如对容器添加、删除元素,不论所存储的数据是何种类型,都执行几乎同样的操作。
对大多数情况,推荐使用.NET框架2.0类库中所提供的容器类。有关使用这些类的详细信息,请参见基础类库中的泛型。
通常,从一个已有的具体类来创建泛型类,并每次把一个类型改为类型参数,直至达到一般性和可用性的最佳平衡。当创建你自己的泛型类时,需要重点考虑的事项有:
l 哪些类型应泛化为类型参数。一般的规律是,用参数表示的类型越多,代码的灵活性和复用性也就越大。过多的泛化会导致代码难以被其它的开发人员理解。
l 如果有约束,那么类型参数需要什么样约束。一个良好的习惯是,尽可能使用最大的约束,同时保证可以处理所有需要处理的类型。例如,如果你知道你的泛型类只打算使用引用类型,那么就应用这个类的约束。这样可以防止无意中使用值类型,同时可以对T使用as运算符,并且检查空引用。
l 把泛型行为放在基类中还是子类中。泛型类可以做基类。同样非泛型类的设计中也应考虑这一点。泛型基类的继承规则 。
l 是否实现一个或多个泛型接口。例如,要设计一个在基于泛型的容器中创建元素的类,可能需要实现类似IComparable
泛型概述中有一个简单泛型类的例子。
类型参数和约束的规则对于泛型类的行为(behavior)有一些潜在的影响,——尤其是对于继承和成员可访问性。在说明这个问题前,理解一些术语十分重要。对于一个泛型类Node
// concrete type class Node: BaseNode //closed constructed type class Node : BaseNode<int> //open constructed type class Node : BaseNode
非泛型的具体类可以继承自封闭构造基类,但不能继承自开放构造基类。这是因为客户代码无法提供基类所需的类型参数。
//No error. class Node : BaseNode<int> //Generates an error. class Node : BaseNode
泛型的具体类可以继承自开放构造类型。除了与子类共用的类型参数外,必须为所有的类型参数指定类型,如下代码所示:
//错误. class Node: BaseNode {…} //正确. class Node : BaseNode int>{…}
继承自开放结构类型的泛型类,必须指定:
class NodeItemwhere T : IComparable , new() {…} class MyNodeItem : NodeItem where T : IComparable , new(){…}
泛型类型可以使用多种类型参数和约束,如下:
class KeyType{…} class SuperKeyType where U : IComparable, where V : new(){…}
泛型接口
不论是为泛型容器类,还是表示容器中元素的泛型类,定义接口是很有用的。把泛型接口与泛型类结合使用是更好的用法,比如用IComparable
public interface BaseIDALwhere T : new() public partial interface UserIDAL : BaseIDAL
嗯!代码整体结构如下,不管是三层还是MVC,我觉得都一样。我这里就简单的用控制台演示一下调用就可以。只有用法,仁者见仁智者见智
我这里是为了完整搭建完整的框架而已。所有就建立了这么多类库,LambdaAndGeneric.Common,和LambdaAndGeneric.FactoryDAL暂时还没用到。你们可以不用跟着建。
首先新建7个类库,版本我就不多说了!!!!
话不多说。上代码。
1、首先在 LamdbaAndGeneric.HelperDAL类库下新建一个类
名称:SenctionHelper.cs代码如下注意引用一下System.Configuration这个dll动态库
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LamdbaAndGeneric.HelperDAL { public static class SenctionHelper { static string ConnectionStringCustomers; static SenctionHelper() { ConnectionStringCustomers = ConfigurationManager.ConnectionStrings["Customers"].ConnectionString; } #region 公用 #region 获取映射模型的字段 ////// 获得当前对象的所有公共字段 /// /// /// public static string GetModelSenction () { Type type = typeof(T); return string.Join(",", type.GetProperties().Select(p => string.Format("[{0}]", p.Name))); } #endregion #region ExecuteNonQuery +static int ExecuteNonQuery(string cmdText, params SqlParameter[] parameters) /// /// 执行一个非查询的T-SQL语句,返回受影响行数,如果执行的是非增、删、改操作,返回-1 /// /// 要执行的T-SQL语句 /// 参数列表 /// /// 受影响的行数 public static int ExecuteNonQuery(string cmdText, params SqlParameter[] parameters) { return ExecuteNonQuery(cmdText, CommandType.Text, parameters); } #endregion #region ExecuteNonQuery +static int ExecuteNonQuery(string cmdText, CommandType type, params SqlParameter[] parameters) /// /// 执行一个非查询的T-SQL语句,返回受影响行数,如果执行的是非增、删、改操作,返回-1 /// /// 要执行的T-SQL语句 /// 命令类型 /// 参数列表 /// /// 受影响的行数 public static int ExecuteNonQuery(string cmdText, CommandType type, params SqlParameter[] parameters) { using (SqlConnection conn = new SqlConnection(ConnectionStringCustomers)) { using (SqlCommand cmd = new SqlCommand(cmdText, conn)) { if (parameters != null) { cmd.Parameters.Clear(); cmd.Parameters.AddRange(parameters); } cmd.CommandType = type; try { conn.Open(); int res = cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); return res; } catch (System.Data.SqlClient.SqlException e) { conn.Close(); throw e; } } } } #endregion #region ExecuteReader +static SqlDataReader ExecuteReader(string cmdText, params SqlParameter[] parameters) /// /// 执行一个查询的T-SQL语句, 返回一个SqlDataReader对象, 如果出现SQL语句执行错误, 将会关闭连接通道抛出异常 /// ( 注意:调用该方法后,一定要对SqlDataReader进行Close ) /// /// 要执行的T-SQL语句 /// 参数列表 /// /// SqlDataReader对象 public static SqlDataReader ExecuteReader(string cmdText, params SqlParameter[] parameters) { return ExecuteReader(cmdText, CommandType.Text, parameters); } #endregion #region ExecuteReader +static SqlDataReader ExecuteReader(string cmdText, CommandType type, params SqlParameter[] parameters) /// /// 执行一个查询的T-SQL语句, 返回一个SqlDataReader对象, 如果出现SQL语句执行错误, 将会关闭连接通道抛出异常 /// ( 注意:调用该方法后,一定要对SqlDataReader进行Close ) /// /// 要执行的T-SQL语句 /// 命令类型 /// 参数列表 /// /// SqlDataReader对象 public static SqlDataReader ExecuteReader(string cmdText, CommandType type, params SqlParameter[] parameters) { SqlConnection conn = new SqlConnection(ConnectionStringCustomers); using (SqlCommand cmd = new SqlCommand(cmdText, conn)) { if (parameters != null) { cmd.Parameters.Clear(); cmd.Parameters.AddRange(parameters); } cmd.CommandType = type; conn.Open(); try { SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); cmd.Parameters.Clear(); return reader; } catch (System.Data.SqlClient.SqlException ex) { //出现异常关闭连接并且释放 conn.Close(); throw ex; } } } #endregion #region 将一个SqlDataReader对象转换成一个实体类对象 +static T MapEntity (SqlDataReader reader) where T : class,new() /// /// 将一个SqlDataReader对象转换成一个实体类对象 /// /// 实体类型 /// 当前指向的reader /// 实体对象 public static T MapEntity (SqlDataReader reader) { try { Type type = typeof(T); var props = type.GetProperties(); object entity = Activator.CreateInstance(type);//创建返回的单个对象 foreach (var prop in props) { if (prop.CanWrite) { try { var index = reader.GetOrdinal(prop.Name); var data = reader.GetValue(index); if (data != DBNull.Value) { prop.SetValue(entity, Convert.ChangeType(data, prop.PropertyType), null); } } catch (IndexOutOfRangeException) { continue; } } } return (T)entity; } catch(Exception ex) { return default(T); } } #endregion #endregion } }
2、我们在 LambdaAndGeneric.IDAL类库下
新建: BaseIDAL.cs类
代码如下:
using System.Collections.Generic; namespace LambdaAndGeneric.IDAL { ////// 说明: BaseIDAL 的封装 /// 时间:2018年8月15日 /// 作用:用于被继承,减少代码 /// 作者:null /// /// 约束对象 public interface BaseIDAL where T : new() { /// /// 执行一个插入操作 /// /// /// /// bool ADD(T Entity); /// /// 根据ID删除一条数据 /// /// /// bool Del(int ID); /// /// 根据Model更新一条数据 /// /// /// bool Update(T Entity); /// /// 获得单个实体 /// /// /// T GetEntity(int ID); /// /// 根据条件查询,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 参数省略【where】关键字 /// IEnumerable GetEntityList(string where); /// /// 执行传入的Sql语句,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 执行的SQL语句 /// IEnumerable GetEntityListSQl (string sql); } }
3、我们在LambdaAndGeneric.DAL类库下
新建: BaseDAL.cs类用于继承IDAL
代码如下:
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Reflection; using System.Text; using LambdaAndGeneric.IDAL; using LamdbaAndGeneric.HelperDAL; namespace LambdaAndGeneric.DAL { ////// 说明: BaseDAL 的封装 /// 时间:2018年8月15日 /// 作用:用于被继承,减少代码 /// 作者:null /// /// 约束对象 public class BaseDAL : BaseIDAL where T : new() { #region 根据Model更新一条数据 /// /// 根据Model更新一条数据 /// /// /// public bool Update(T Entity) { Type type = typeof(T); object oT = Activator.CreateInstance(type); var oEntity = Entity.GetType().GetProperties();// StringBuilder builder = new StringBuilder(256); StringBuilder builderWhere = new StringBuilder(256); foreach (var item in oEntity) { //字段和值得拼接 if (!item.Name.Equals("ID", StringComparison.InvariantCultureIgnoreCase) && !item.Name.Equals("FID", StringComparison.InvariantCultureIgnoreCase)) { builder.Append(item.Name + "=" + item.GetValue(oT)); } //获取ID条件 if (item.Name.Equals("ID", StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("FID", StringComparison.InvariantCultureIgnoreCase)) { builderWhere.Clear(); builderWhere.Append(item.Name + "=" + item.GetValue(oT)); } } string execSql = $" update {type.Name} {builder.ToString()} where {builderWhere.ToString()}"; return SenctionHelper.ExecuteNonQuery(execSql) > 0; //SenctionHelper .ExecuteNonQuery(execSql) > 0; } #endregion #region 根据ID删除一条数据 ////// 根据ID删除一条数据 /// /// /// public bool Del(int ID) { Type type = typeof(T); string execSql = $" delete from {type.Name} where ID={ID} "; return SenctionHelper.ExecuteNonQuery(execSql) > 0; } #endregion #region 插入一条数据 /// /// 执行一个插入操作 /// /// /// /// public bool ADD(T Entity) { Type type = typeof(T); object oT = Activator.CreateInstance(type); PropertyInfo[] propertys = Entity.GetType().GetProperties(); StringBuilder builder = new StringBuilder(); //获取字段 var section = string.Join(",", type.GetProperties().Where(w => !w.Name.Equals("ID", StringComparison.CurrentCultureIgnoreCase) && !w.Name.Equals("FID", StringComparison.CurrentCultureIgnoreCase)).Select(s => s.Name)); //获取值 var value = "'" + string.Join("','", propertys.Where(w => !w.Name.Equals("ID", StringComparison.CurrentCultureIgnoreCase) && !w.Name.Equals("FID", StringComparison.CurrentCultureIgnoreCase)).Select(p => p.GetValue(Entity))) + "'"; string execSql = $" insert into {type.Name}({section}) values({value}) "; return SenctionHelper.ExecuteNonQuery(execSql) > 0; } #endregion #region 获得单个实体 /// /// 获得单个实体 /// /// /// public T GetEntity(int ID) { Type type = typeof(T); string sql = $" SELECT {SenctionHelper.GetModelSenction ()} from {type.Name} "; try { using (SqlDataReader reader = SenctionHelper.ExecuteReader(sql)) { if (reader.HasRows) { reader.Read(); return SenctionHelper.MapEntity(reader); } else { return default(T); } } } catch (Exception ex) { Console.WriteLine(ex.Message); return default(T); } } #endregion #region 根据传入的条件执行Sql语句,并返回一个IEnumerable 类型的集合 /// /// 根据传入的SQL语句执行查询,并返回一个IEnumable 类型的集合 /// 注意 W 必须约束为 where W : class, new()) /// /// /// public IEnumerable GetEntityListSQl (string sql) { using (SqlDataReader reader = SenctionHelper.ExecuteReader(sql)) { if (reader.HasRows) { while (reader.Read()) { yield return SenctionHelper.MapEntity (reader); } } } } #endregion #region 根据传入的条件执行Sql语句,并返回一个IEnumerable 类型的集合 /// /// 根据传入的条件执行查询,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 类型:【 约束为 where T : class, new() 】 /// 查询的条件,请省略 Where 关键字 /// public IEnumerable GetEntityList(string where) { Type type = typeof(T); //遍历获得字段 //string columnString = string.Join(",", type.GetProperties().Select(p => string.Format("[{0}]", p.Name))); string sql = string.Format("SELECT {0} FROM [{1}] WHere {2} ", SenctionHelper.GetModelSenction (), type.Name, where); using (SqlDataReader reader = SenctionHelper.ExecuteReader(sql)) { if (reader.HasRows) { while (reader.Read()) { yield return SenctionHelper.MapEntity (reader); } } } } #endregion } }
4、我们在LambdaAndGeneric.IBLL类库下
新建: BaseIBLL.cs类
代码如下:
using System.Collections.Generic; namespace LambdaAndGeneric.IBLL { ////// 说明: BaseIBLL 的封装 /// 时间:2018年8月15日11:11:31 /// 作用:用于被继承,减少代码 /// 作者:null /// /// 约束对象 public interface BaseIBLL where T: new() { /// /// 执行一个插入操作 /// /// /// /// bool ADD(T Entity); /// /// 根据ID删除一条数据 /// /// /// bool Del(int ID); /// /// 根据Model更新一条数据 /// /// /// bool Update(T Entity); /// /// 获得单个实体 /// /// /// T GetEntity(int ID); /// /// 根据条件查询,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 参数省略【where】关键字 /// IEnumerable GetEntityList(string where); /// /// 执行传入的Sql语句,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 执行的SQL语句 /// IEnumerable GetEntityListSQl (string sql); } }
5、我们在LambdaAndGeneric.BLL类库下
新建: BaseBLL.cs类 继承 BaseIBLL.cs
代码如下
using System.Collections.Generic; using LambdaAndGeneric.DAL; using LambdaAndGeneric.IBLL; using LambdaAndGeneric.IDAL; namespace LambdaAndGeneric.BLL { ////// 说明: BaseBLL 的封装 /// 时间:2018年8月15日11:11:31 /// 作用:用于被继承,减少代码 /// 作者:null /// /// 约束对象 public class BaseBLL : BaseIBLL where T : new() { private BaseIDAL dao = new BaseDAL (); /// /// 增加一条数据 /// /// 传入的增加的实体Model /// public bool ADD(T Entity) { return dao.ADD(Entity); } /// /// 根据ID删除一条数据 /// /// 删除ID /// public bool Del(int ID) { return dao.Del(ID); } /// ///根据实体更新一条数据 /// /// 传入更新的实体Model /// public bool Update(T Entity) { return dao.Update(Entity); } /// /// 获得单个实体 /// /// 查询ID /// public T GetEntity(int ID) { return dao.GetEntity(ID); } /// /// 根据传入的条件执行查询,并返回一个IEnumerable 类型的集合 /// (注意传入的 T 必须约束为 where T : class, new()) /// /// 查询条件,请省略 where 关键字 /// public IEnumerable GetEntityList(string where) { return dao.GetEntityList(where); } /// /// 根据传入的SQL语句执行查询,并返回一个IEnumable 类型的集合 /// 注意 T 必须约束为 where W : class, new()) /// /// 返回的单个Model实体 /// 执行的SQL语句 /// public IEnumerable GetEntityListSQl (string sql) { return dao.GetEntityListSQl (sql); } } }
6、我们在LambdaAndGeneric.Model类库下
新建: 一个Company 的实体
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LambdaAndGeneric.Model { public class Company { public int ID { get; set; } 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; } } }
数据库新建张表
USE [XXX] GO /****** Object: Table [dbo].[Company] Script Date: 2018-08-16 12:00:11 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Company]( [ID] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NULL, [CreateTime] [datetime] NULL, [CreatorId] [int] NULL, [LastModifierId] [int] NULL, [LastModifyTime] [datetime] NULL, CONSTRAINT [PK_Company] PRIMARY KEY CLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
6、最后我们在控制台测试一下
using LambdaAndGeneric.BLL; using LambdaAndGeneric.DAL; using LambdaAndGeneric.IBLL; using LambdaAndGeneric.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp { class Program { static void Main(string[] args) { { CompanyIBLLcompany = new CompanyBLL(); var xx = company.GetEntity(1); var result = company.ADD(new Company() { CreateTime = System.DateTime.Now, CreatorId = 2, ID = 2, LastModifierId = 2, LastModifyTime = System.DateTime.Now, Name = "xxx" }); Console.WriteLine(result); { Console.WriteLine("*****************************GetEntityListSQl***********************************"); var list = company.GetEntityListSQl (" SELECT * from company "); foreach (var item in list) { Console.WriteLine(item.ID + "***" + item.LastModifierId + "**" + item.LastModifyTime + "***" + item.Name); } } { Console.WriteLine("*****************************GetEntityList***********************************"); var listbll = company.GetEntityList("id>0"); foreach (var item in listbll) { Console.WriteLine(item.ID + "***" + item.LastModifierId + "**" + item.LastModifyTime + "***" + item.Name); } } Console.ReadKey(); } } } }
注意这里执行 GetEntityListSQl