外贸网络做推广公司/seo排名培训
使用泛型和反射,打造我们的完美实体基类
背景
我在开发的过程中,实体和数据库的访问是一直要接触和编写的代码,考虑到团队中初学者比较多,我一直希望有一种方式可以改善目前编码的困境:
ADO.Net的代码对于初学者来讲比较麻烦,需要他们更多的对ADO.Net有所了解。
将数据库的值为实体属性赋值是一个比较繁琐的过程,程序员容易厌倦这个重复工作,或数据类型,或属性名称的赋值错误。
对于一对多的实体模型开发中,程序员很容易的将一对多的实体对象相互的关联错误。
我们当然可以使用一些自动化的工具来辅助,或者采用类似Hibernate的框架或VS2008SP1的一些新功能来实现,但我们又需要在我们的实体中有些更灵活的控制。
因此作为开发团队的技术管理人员,我必须要设计一种更好的模式来解决以上的事项。
正文中,我们将逐步的阐述我的设计思路。
一、打造DataProvider
ADO.Net的封装已经有很多的实现了,但我总感觉那些实现还是没有透明化使用者对ADO.Net的了解。比如说很多人推崇的Enterprise Library的DataAccess,我认为就是封装不够彻底。我理想中封装彻底的ADO.Net对象是,使用者不需要(或尽可能的少)了解任何,而DataAccess还是需要使用者直接的处理很多ADO.Net的对象。而我需要的ADO.Net的封装希望使用者,仅给予SQL命令,赋值参数,并获取结果即可。
1.1定义DataProvide
定义SqlDataProvider类
- /// <summary>
- /// SQL数据提供者的实现
- /// </summary>
- public class SqlDataProvider : DataProviders.IDataProvider
- {
- }
1.2定义DataProviders.IDataProvider
DataProviders.IDataProvider是我定义的一个接口,我希望将来所有的数据提供者都能实现该接口,以便利用依赖倒置实现抽象工厂。
定义DataProviders.IDataProvider接口
- public interface IDataProvider
- {
- void AddParameters(string parname, Guid value);
- void AddParameters(string parname, long value);
- void AddParameters(string parname, string value);
- void AddParameters(string parname, string value, DataProviders.StringFamily dataType);
- void AddParameters(string parname, string value, DataProviders.StringFamily dataType, int size);
- void AddParameters(string parname, float value);
- void AddParameters(string parname, decimal value);
- void AddParameters(string parname, DateTime value);
- void AddParameters(string parname, DateTime value, DataProviders.DateFamily dataType);
- void AddParameters(string parname, int value);
- void AddParameters(string parname, object value);
- void AddParameters(string parname, System.Drawing.Bitmap value);
- void AddParameters(string parname, byte[] value);
- void AddParameters(string parname, byte[] value, DataProviders.ByteArrayFamily dataType);
- void AddParameters(string parname, bool value);
- void AddParameters(string parname, short value);
- void AddParameters(string parname, byte value);
- System.Data.CommandType CommandType { get; set; }
- string ConnectionString { get; }
- System.Data.DataSet ExecuteDataSet();
- System.Data.DataTable ExecuteDataTable();
- void ExecuteReader(ReadData readData);
- int ExecuteNonQuery();
- object ExecuteScalar();
- string SQL { get; set; }
- }
- public delegate void ReadData(System.Data.IDataReader dataReadre);
从该接口可以看到,实现的DataProvider封装了关于具体的连接对象,命令对象和参数类型的信息。
1.3实现DataProvider基础部分
SqlDataProvider类实现(基础部分)
- private static System.Data.SqlClient.SqlConnection conn;
- private System.Data.SqlClient.SqlCommand cmd;
- /// <summary>
- /// 默认构造函数
- /// </summary>
- public SqlDataProvider()
- {
- }
- /// <summary>
- /// 接受连接字符串
- /// </summary>
- /// <param name="connstr"></param>
- public SqlDataProvider(string connstr)
- : this(connstr, "")
- {
- }
- /// <summary>
- /// 接受连接字符串和sql字符串
- /// </summary>
- /// <param name="connstr"></param>
- /// <param name="sql"></param>
- public SqlDataProvider(string connstr, string sql)
- {
- conn = new System.Data.SqlClient.SqlConnection(connstr);
- cmd = new System.Data.SqlClient.SqlCommand();
- cmd.Connection = conn;
- cmd.CommandText = sql;
- }
- /// <summary>
- /// 需要执行的SQL命令
- /// </summary>
- public string SQL
- {
- set
- {
- cmd.CommandText = value;
- }
- get
- {
- return cmd.CommandText;
- }
- }
- /// <summary>
- /// 当前的连接字符串
- /// </summary>
- public string ConnectionString
- {
- get
- {
- return conn.ConnectionString;
- }
- }
- /// <summary>
- /// 设置命令的类型
- /// </summary>
- public System.Data.CommandType CommandType
- {
- set
- {
- cmd.CommandType = value;
- }
- get
- {
- return cmd.CommandType;
- }
- }
从上述代码可以观察到,我们的DataProvider只向用户暴露了两个字符串数据:连接字符串和SQL指令字符串。而将ADO.Net特有的Connection和Command封装起来了。由于用户不需要了解到具体实例化的Connection和Command,则为以后对其他数据源提供者的扩展带来了机会。
1.4实现DataProvider数据执行部分
SqlDataProvider类实现(数据执行部分)
- /// <summary>
- /// 将SqlDataReader提交给具体的委托器处理
- /// </summary>
- /// <param name="readData"></param>
- public void ExecuteReader(ReadData readData)
- {
- using (conn)
- {
- conn.Open();
- System.Data.SqlClient.SqlDataReader dr = cmd.ExecuteReader();
- readData(dr);
- conn.Close();
- }
- }
- /// <summary>
- /// 对连接执行 Transact-SQL 语句并返回受影响的行数
- /// </summary>
- /// <returns></returns>
- public int ExecuteNonQuery()
- {
- int result = -1;
- using (conn)
- {
- conn.Open();
- result = cmd.ExecuteNonQuery();
- conn.Close();
- }
- return result;
- }
- /// <summary>
- /// 执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其他列或行
- /// </summary>
- /// <returns></returns>
- public object ExecuteScalar()
- {
- object result = null;
- using (conn)
- {
- conn.Open();
- result = cmd.ExecuteScalar();
- conn.Close();
- }
- return result;
- }
- /// <summary>
- /// 执行查询,并返回查询的DataSet
- /// </summary>
- /// <returns></returns>
- public System.Data.DataSet ExecuteDataSet()
- {
- System.Data.DataSet datadet = new System.Data.DataSet();
- using (conn)
- {
- System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();
- adapter.SelectCommand = cmd;
- conn.Open();
- adapter.Fill(datadet);
- conn.Close();
- }
- return datadet;
- }
- /// <summary>
- /// 执行查询,并返回查询的Table
- /// </summary>
- /// <param name="tableIndex"></param>
- /// <returns></returns>
- public System.Data.DataTable ExecuteDataSet(int tableIndex)
- {
- System.Data.DataSet datadet = ExecuteDataSet();
- if (datadet.Tables.Count > 0 && tableIndex < datadet.Tables.Count)
- {
- return datadet.Tables[tableIndex];
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// 执行查询,并返回查询的Table
- /// </summary>
- /// <returns></returns>
- public System.Data.DataTable ExecuteDataTable()
- {
- System.Data.DataTable table = new System.Data.DataTable();
- using (conn)
- {
- System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter();
- adapter.SelectCommand = cmd;
- conn.Open();
- adapter.Fill(table);
- conn.Close();
- }
- return table;
- }
1.5实现DataProvider参数部分
SqlDataProvider类实现(参数部分)
- /// <summary>
- /// 添加一个Variant类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, object value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Variant).Value = value;
- }
- /// <summary>
- /// 添加一个Bit类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, bool value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Bit).Value = value;
- }
- /// <summary>
- /// 添加一个TinyInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, byte value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.TinyInt).Value = value;
- }
- /// <summary>
- /// 添加一个SmallInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, short value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.SmallInt).Value = value;
- }
- /// <summary>
- /// 添加一个Int类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, int value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Int).Value = value;
- }
- /// <summary>
- /// 添加一个BigInt类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, long value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.BigInt).Value = value;
- }
- /// <summary>
- /// 添加一张图片
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, System.Drawing.Bitmap value)
- {
- System.IO.MemoryStream ms = new System.IO.MemoryStream();
- value.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
- AddParameters(parname, ms.ToArray(), ByteArrayFamily.Image);
- }
- /// <summary>
- /// 添加一个Timestamp类型
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, byte[] value)
- {
- AddParameters(parname, value, ByteArrayFamily.Timestamp);
- }
- /// <summary>
- /// 添加一个字节数组族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- public void AddParameters(string parname, byte[] value, ByteArrayFamily dataType)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType)).Value = value;
- }
- /// <summary>
- /// 添加一个字符类型数据,默认是NVarChar,长度是value.Length
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, string value)
- {
- AddParameters(parname, value, StringFamily.NVarChar, value.Length);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="length"></param>
- public void AddParameters(string parname, string value, int size)
- {
- AddParameters(parname, value, StringFamily.NVarChar, size);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- /// <param name="length"></param>
- public void AddParameters(string parname, string value, StringFamily dataType)
- {
- AddParameters(parname, value,dataType, value.Length);
- }
- /// <summary>
- /// 添加一个字符族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- /// <param name="size"></param>
- public void AddParameters(string parname, string value, StringFamily dataType, int size)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType), size).Value = value;
- }
- /// <summary>
- /// 添加一个SmallDateTime类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, DateTime value)
- {
- AddParameters(parname, value, DateFamily.SmallDateTime);
- }
- /// <summary>
- /// 添加一个日期族类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- /// <param name="dateType"></param>
- public void AddParameters(string parname, DateTime value, DateFamily dataType)
- {
- cmd.Parameters.Add(parname, DataTypeAdapter.ConvertSqlDbType(dataType)).Value = value;
- }
- /// <summary>
- /// 添加一个Decimal类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, decimal value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Decimal).Value = value;
- }
- /// <summary>
- /// 添加Float类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, float value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.Float).Value = value;
- }
- /// <summary>
- /// 添加一个UniqueIdentifier类型数据
- /// </summary>
- /// <param name="parname"></param>
- /// <param name="value"></param>
- public void AddParameters(string parname, System.Guid value)
- {
- cmd.Parameters.Add(parname, System.Data.SqlDbType.UniqueIdentifier).Value = value;
- }
ADO.Net对参数的处理冗长的很,需要很多代码,我们的DataProvider通过重载简单的实现了对参数的处理,使用者在大多数情况下只需要提供两个参数:参数的名称和值。DataProvider和根据值的类型推算应该使用具体的哪个System.Data.SqlDbType。
1.6定义C#和SQL数据类型的关系
不过,另人烦恼的是C#的数据类型和SQL的数据类型不是简单的一对一的关系,而是一对多的复杂关系,我们需要有一个方法来适配数据类型的不同。
数据类型的关系定义
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum StringFamily
- {
- Char,
- NChar,
- NText,
- NVarChar,
- Text,
- VarChar
- }
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum DateFamily
- {
- DateTime,
- SmallDateTime,
- Date,
- Time,
- DateTime2,
- DateTimeOffset
- }
- /// <summary>
- /// C#对于的SQL类型
- /// </summary>
- public enum ByteArrayFamily
- {
- Binary,
- Image,
- Timestamp,
- VarBinary
- }
1.7定义数据类型的适配器
DataTypeAdapter的定义
- /// <summary>
- /// SqlDbType数据类型和.NET Framework数据类型的适配器
- /// </summary>
- public static class DataTypeAdapter
- {
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(StringFamily data)
- {
- switch (data)
- {
- case StringFamily.Char:
- return System.Data.SqlDbType.Char;
- case StringFamily.NChar:
- return System.Data.SqlDbType.NChar;
- case StringFamily.NText:
- return System.Data.SqlDbType.NText;
- case StringFamily.NVarChar:
- return System.Data.SqlDbType.NVarChar;
- case StringFamily.Text:
- return System.Data.SqlDbType.Text;
- default:
- return System.Data.SqlDbType.VarChar;
- }
- }
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(DateFamily data)
- {
- switch (data)
- {
- case DateFamily.Date:
- return System.Data.SqlDbType.Date;
- case DateFamily.DateTime:
- return System.Data.SqlDbType.DateTime;
- case DateFamily.DateTime2:
- return System.Data.SqlDbType.DateTime2;
- case DateFamily.DateTimeOffset:
- return System.Data.SqlDbType.DateTimeOffset;
- case DateFamily.SmallDateTime:
- return System.Data.SqlDbType.SmallDateTime;
- default:
- return System.Data.SqlDbType.Time;
- }
- }
- /// <summary>
- /// 将.NET Framework数据类型适配为SqlDbType数据类型
- /// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
- public static System.Data.SqlDbType ConvertSqlDbType(ByteArrayFamily data)
- {
- switch (data)
- {
- case ByteArrayFamily.Binary:
- return System.Data.SqlDbType.Binary;
- case ByteArrayFamily.Image:
- return System.Data.SqlDbType.Image;
- case ByteArrayFamily.Timestamp:
- return System.Data.SqlDbType.Timestamp;
- default:
- return System.Data.SqlDbType.VarBinary;
- }
- }
- }
通过上述的数据类型适配,我们将使用者和ADO.Net直接的具体关系弱耦合了。
1.8使用DataProvider
使用DataProvider(Select)
- DataProviders.IDataProvider provider = CreateDataProvider();
- provider.SQL = "SELECT CompanyID as [Identity],Name,ShortName,Code,LegalEntity,Address,PostalCode,Type as CompanyType,CityID,Version " +
- "FROM lt_dictionary.Company WHERE CityID=@cityid";
- provider.AddParameters("@cityid", cityID);
- return provider.ExecuteDataTable();
使用DataProvider(Update)
- DataProviders.IDataProvider provider = CreateDataProvider();
- provider.SQL = "UPDATE lt_dictionary.Company " +
- "SET " +
- "Name=@name, " +
- "ShortName=@shortName," +
- "Code=@code," +
- "LegalEntity=@legalEntity," +
- "Address=@address," +
- "PostalCode=@postalCode, " +
- "Type=@type," +
- "CityID=@cityID " +
- "WHERE CompanyID=@id AND Version=@ver";
- provider.AddParameters("@name", company.Name);
- provider.AddParameters("@shortName", company.ShortName);
- provider.AddParameters("@Code", company.Code);
- provider.AddParameters("@LegalEntity", company.LegalEntity);
- provider.AddParameters("@address", company.Address);
- provider.AddParameters("@postalCode", company.PostalCode);
- provider.AddParameters("@type", company.CompanyType.ToString());
- provider.AddParameters("@cityID", company.City.Identity);
- provider.AddParameters("@id", original_company.Identity);
- provider.AddParameters("@ver", original_company.Version, DataProviders.ByteArrayFamily.Timestamp);
- return provider.ExecuteNonQuery() > 0;
使用DataProvider(Insert)
- DataProviders.IDataProvider provider = CreateDataProvider();
- provider.SQL = "INSERT INTO lt_dictionary.City " +
- "([Name],PostalCode,DistanceCode,Province,Longitude,Latitude)" +
- "VALUES " +
- "(@Name,@PostalCode,@DistanceCode,@Province,@Longitude,@Latitude)";
- provider.AddParameters("@name", city.Name);
- provider.AddParameters("@postalCode", city.PostalCode);
- provider.AddParameters("@distanceCode", city.DistanceCode);
- provider.AddParameters("@province", city.Province);
- provider.AddParameters("@longitude", city.Longitude);
- provider.AddParameters("@latitude", city.Latitude);
- return provider.ExecuteNonQuery() > 0;
通过上述的代码,可以发现,使用了我们的DataProvider后,程序员对ADO.Net的了解被降到最低程度,其只要关心具体的SQL指令和参数的赋值,其他内容不再需要其关注。很高程度的提高了程序员的开发效率。
二、打造实体基类
关系型数据表中一般有共性的部分是所有的实体都有ID(但ID的类型不一样),很多业务表都有主从的关系。
2.1表定义
比如下面的表
City定义
- CREATE TABLE [lt_dictionary].[City](
- [CityID] [int] IDENTITY(1,1) NOT NULL,
- [Name] [nvarchar](50) NOT NULL,
- [PostalCode] [dbo].[PostalCodeType] NOT NULL,
- [DistanceCode] [nvarchar](5) NOT NULL,
- [Province] [nvarchar](3) NOT NULL,
- [Longitude] [decimal](5, 2) NOT NULL,
- [Latitude] [decimal](5, 2) NOT NULL,
- [Enable] [dbo].[EnableType] NOT NULL CONSTRAINT [DF_City_Enable] DEFAULT ((1)),
- [LastEditDate] [dbo].[BusinessDateType] NOT NULL CONSTRAINT [DF_City_LastEditDate] DEFAULT (getdate()),
- [UpdateDay] AS (datediff(day,[LastEditDate],getdate())),
- [Version] [timestamp] NOT NULL,
- CONSTRAINT [PK_City] PRIMARY KEY CLUSTERED
- (
- [CityID] ASC
- )
这个城市表的ID是int的。
BusinessOrders定义
- CREATE TABLE [lt_business].[BusinessOrders](
- [BusinessOrderID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_BusinessOrders_BusinessOrderID] DEFAULT (newid()),
- [Number] [dbo].[BusinessOrderType] NOT NULL CONSTRAINT [DF_BusinessOrders_Number] DEFAULT ([dbo].[CreateBusinessOrderNumber]('Bz')),
- [Deadline] [dbo].[BusinessDateType] NOT NULL,
- [PaymentMethod] [nchar](2) NOT NULL,
- [PaymentEnterprise] [dbo].[DescriptionType] NOT NULL,
- [Origin] [dbo].[DescriptionType] NOT NULL,
- [Destination] [dbo].[DescriptionType] NOT NULL,
- [DeliveryType] [nchar](2) NOT NULL,
- [Level] [dbo].[LevelType] NOT NULL,
- [Remark] [dbo].[DescriptionType] NOT NULL,
- [Indicator] [nvarchar](3) NOT NULL,
- [FreightPayable] [dbo].[DescriptionType] NOT NULL,
- [WarehouseID] [int] NOT NULL,
- [OrderID] [uniqueidentifier] NOT NULL,
- [BusinessDate] [dbo].[BusinessDateType] NOT NULL CONSTRAINT [DF_BusinessOrders_BusinessDate] DEFAULT (getdate()),
- [StaffID] [int] NOT NULL,
- [Version] [timestamp] NOT NULL,
- [State] AS ([dbo].[GetBusinessOrderState]([BusinessOrderID])),
- CONSTRAINT [PK_BusinessOrders] PRIMARY KEY CLUSTERED
BusinessOrders的ID是uniqueidentifier类型。
BusinessOrderDetaileds定义
- CREATE TABLE [lt_business].[BusinessOrderDetaileds](
- [BusinessOrderDetailedID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_BusinessOrderDetaileds_BusinessOrderDetailedID] DEFAULT (newid()),
- [BusinessOrderID] [uniqueidentifier] NOT NULL,
- [Serial] [int] NOT NULL,
- [GoodsDescription] [dbo].[DescriptionType] NOT NULL,
- [Packing] [nvarchar](2) NOT NULL,
- [Quantity] [decimal](18, 2) NOT NULL,
- [TotalPackages] [decimal](18, 2) NOT NULL,
- [Weight] [decimal](18, 2) NOT NULL,
- [Measurement] [decimal](18, 2) NOT NULL,
- [Version] [timestamp] NOT NULL,
- [State] AS ([dbo].[GetBusinessOrderItmeState]([BusinessOrderDetailedID])),
- [CompleteQuantity] AS ([dbo].[GetBusinessOrderItmeCompleteQuantity]([BusinessOrderDetailedID])),
- CONSTRAINT [PK_BusinessOrderDetaileds] PRIMARY KEY CLUSTERED
BusinessOrderDetaileds的ID是uniqueidentifier类型,其外键对应的是BusinessOrders实体的ID。
2.2关于实体基类定义的要求
我希望有这个的实体基类,该实体定义了所有的继承者(实体的具体实现类)都必须有ID属性,但ID属性的数据类型由各实体自己定义。我还希望,能在类的定义上看出有主从表的关系,并且能约束主从表的一些行为。而且我还希望基类能自动的实现对属性的赋值。
2.3定义EntityBase
EntityBase定义(继承部分)
- [Serializable]
- public abstract class EntityBase<T,ID> where T : EntityBase<T,ID>
- {
- /// <summary>
- /// 所有的实体都必须有一个唯一标识,具体类型有实体各自实现
- /// </summary>
- [System.ComponentModel.DataObjectField(true, true, false)]
- public virtual ID Identity
- {
- set;
- get;
- }
- }
EntityBase定义了一个ID的泛型,该泛型描述了继承者必须实现具体的ID类型。
2.4定义EntityBase的Undo功能
在没有泛型的年代时,基类无了解子类的类型,因此基类只能实现一些返回或参数是基本数据类型的方法,如果要为子类提供个性化的方法,基类只能以object对象返回,且要求子类实现数据类型的强制转换。但现在,EntityBase还提供了一个T类型,因此我们可以实现Undo的功能。
EntityBase(Undo部分)
- /// <summary>
- /// 实体是否支持撤销
- /// </summary>
- public abstract bool HasUndo
- {
- get;
- }
- /// <summary>
- /// 还可以撤销的次数
- /// </summary>
- public int UndoCount
- {
- get
- {
- return undoStack.Count;
- }
- }
- /// <summary>
- /// 得到实体的副本
- /// </summary>
- /// <returns></returns>
- protected virtual T Clone()
- {
- return (T)this.MemberwiseClone();
- }
- /// <summary>
- /// 将复本入栈
- /// </summary>
- protected void Push()
- {
- if (this.HasUndo)
- {
- this.Push((T)this.Clone());
- }
- }
- /// <summary>
- /// 将复本入栈
- /// </summary>
- /// <param name="obj"></param>
- private void Push(T obj)
- {
- if (this.HasUndo)
- {
- undoStack.Push(obj.Clone());
- }
- }
- private System.Collections.Generic.Stack<T> undoStack = new Stack<T>();
- /// <summary>
- /// 将复本出栈
- /// </summary>
- /// <returns></returns>
- private T Pop()
- {
- if (undoStack.Count > 0)
- {
- return undoStack.Pop();
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// 撤销
- /// </summary>
- /// <returns></returns>
- public T Undo()
- {
- return Pop();
- }
使用了泛型,我们在类的内部提供了泛型队列,然后返回值和参数值都是泛型T,该T将由各个子类来具体实现。
2.5定义EntityBase的数据访问能力
- /// <summary>
- /// 根据给定的连接字符串构造数据提供者
- /// </summary>
- /// <param name="connStr"></param>
- /// <returns></returns>
- protected static DataProviders.IDataProvider CreateDataProvider(string connStr)
- {
- return new DataProviders.SqlDataProvider.SqlDataProvider(connStr);
- }
2.6定义EntityBase的构造函数
EntityBase有一个接受System.Data.DataTable的构造函数,该构造函数将table中指定行的数据和本类的属性作对比,如果名称和数据类型匹配,则自动赋值。
EntityBase构造函数
- /// <summary>
- /// 按table的指定行数据进行属性的初始化
- /// </summary>
- /// <param name="table"></param>
- /// <param name="indexRow"></param>
- public EntityBase(System.Data.DataTable table, int indexRow)
- {
- //遍历table中的每一列
- for (int i = 0; i <= table.Columns.Count - 1; i++)
- {
- //按列的名称,试图从当前对象中获取同名属性
- System.Reflection.PropertyInfo pinfo = this.GetType().GetProperty(table.Columns[i].ColumnName);
- if (pinfo != null)
- {//如果存在该属性
- object value = table.Rows[indexRow][table.Columns[i].ColumnName];//提取列的当前行值
- if (pinfo.PropertyType == table.Columns[i].DataType)//如果对象属性定义的类型和table的列的类型一致
- {
- pinfo.SetValue(this, value, null);//赋值
- }
- else
- {
- if (pinfo.PropertyType.IsEnum)//如果对象属性的值是枚举类型
- {
- if (value.GetType() == typeof(int))//数据库中保存的是int类型,则直接为枚举赋值
- {
- pinfo.SetValue(this, value, null);//赋值
- }
- if (value.GetType() == typeof(string))//如果数据库中保存的是string类型
- {
- pinfo.SetValue(this, Enum.Parse(pinfo.PropertyType, value.ToString(), false), null);//赋值
- }
- }
- //如果对象的属性是Bitmap类型,对应的数据值是byte[]
- if (pinfo.PropertyType==typeof(System.Drawing.Bitmap) && value.GetType()==typeof(byte[]))
- {
- pinfo.SetValue(this, new System.Drawing.Bitmap(new System.IO.MemoryStream((byte[])value)), null);//赋值
- }
- }
- }
- }
- }
2.7定义EntityBase的CreateInstance
虽然EntityBase的构造函数有能力实现对属性的自动赋值,但我们可能要实例对象的集合或决定table中是否有值,应此我们需要实现CreateInstance方法。
定义EntityBase的CreateInstances方法
- /// <summary>
- /// 通过table实例化一组对象
- /// </summary>
- /// <param name="table"></param>
- /// <returns></returns>
- public static List<T> CreateInstances(System.Data.DataTable table, int startRecord, int maxRecords)
- {
- List<T> instances = new List<T>();
- for (int i = startRecord; i <= maxRecords; i++)
- {
- instances.Add(CreateInstance(table, i));
- }
- return instances;
- }
- /// <summary>
- /// 通过table实例化一个对象
- /// </summary>
- /// <param name="table"></param>
- /// <param name="startRecord"></param>
- /// <param name="maxRecords"></param>
- /// <returns></returns>
- public static T CreateInstance(System.Data.DataTable table, int rowIndex)
- {
- if (table.Rows.Count > rowIndex)
- {
- return (T)System.Activator.CreateInstance(typeof(T), table, rowIndex);
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// 默认按table的第一行实例化一个对象
- /// </summary>
- /// <param name="table"></param>
- /// <returns></returns>
- public static T CreateInstance(System.Data.DataTable table)
- {
- return CreateInstance(table, 0);
- }
- /// <summary>
- /// 通过table实例化一组对象
- /// </summary>
- /// <param name="table"></param>
- /// <param name="startRecord"></param>
- /// <returns></returns>
- public static List<T> CreateInstances(System.Data.DataTable table, int startRecord)
- {
- return CreateInstances(table, startRecord, table.Rows.Count - 1);
- }
- /// <summary>
- /// 通过table实例化一组对象
- /// </summary>
- /// <param name="table"></param>
- /// <returns></returns>
- public static List<T> CreateInstances(System.Data.DataTable table)
- {
- return CreateInstances(table, 0, table.Rows.Count - 1);
- }
2.8使用EntityBase
返回单个实例对象
- /// <summary>
- /// 按指定的名字返回城市对象
- /// </summary>
- /// <param name="cityName"></param>
- /// <returns></returns>
- [System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Select, false)]
- public static City SelectByName(string cityName)
- {
- DataProviders.IDataProvider provider = CreateDataProvider();
- provider.SQL = "SELECT CityID as [Identity],Name,PostalCode,DistanceCode,Province,Longitude,Latitude,Version " +
- "FROM lt_dictionary.City WHERE Name=@name";
- provider.AddParameters("@name", cityName);
- return CreateInstance(provider.ExecuteDataTable());
- }
返回集合对象
- /// <summary>
- /// 返回所有禁用状态的城市信息
- /// </summary>
- /// <returns></returns>
- [System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Select, false)]
- public static List<City> SelectIsDisabled()
- {
- DataProviders.IDataProvider provider = CreateDataProvider();
- provider.SQL = "SELECT CityID as [Identity],Name,PostalCode,DistanceCode,Province,Longitude,Latitude,Version " +
- "FROM lt_dictionary.City WHERE Enable=0";
- //返回List<City>
- return CreateInstances(provider.ExecuteDataTable());
- }
三、定义主从实体基类
定义主从实体基类的原因是我希望在类的定义时,可以很明确的了解类之间的主从关系。
3.1定义主表基类
定义PrimaryDataEntityBase
- /// <summary>
- /// 描述主从表的主表的数据实体
- /// </summary>
- /// <typeparam name="ID">主表实体的主键ID</typeparam>
- /// <typeparam name="P">主表</typeparam>
- /// <typeparam name="F">从表</typeparam>
- public abstract lass PrimaryDataEntityBase<ID, P, F> : EntityBase<P, ID>
- where P : PrimaryDataEntityBase<ID, P, F>
- where F : ForeignDataEntityBase<ID, P, F>
- {
- /// <summary>
- /// PrimaryDataEntityBase的默认构造函数
- /// </summary>
- public PrimaryDataEntityBase()
- {
- }
- /// <summary>
- /// 按table的第一行数据进行属性的初始化
- /// </summary>
- /// <param name="table"></param>
- public PrimaryDataEntityBase(System.Data.DataTable table)
- : this(table, 0)
- {
- }
- /// <summary>
- /// 按table的指定行数据进行属性的初始化
- /// </summary>
- /// <param name="table"></param>
- /// <param name="indexRow"></param>
- public PrimaryDataEntityBase(System.Data.DataTable table, int indexRow)
- : base(table, indexRow)
- {
- }
- /// <summary>
- /// 装载当前从表的详细项
- /// </summary>
- protected abstract List<F> LoadDetailedItems();
- /// <summary>
- /// 存放外键表的数据项目的集合
- /// </summary>
- protected List<F> items = new List<F>();
- /// <summary>
- /// 获取外键表数据的集合
- /// </summary>
- public List<F> DetailedItems
- {
- get
- {
- return LoadDetailedItems();
- }
- }
- /// <summary>
- /// 返回外键表的数据项目数量
- /// </summary>
- public int DetailedItemCount
- {
- get
- {
- return items.Count;
- }
- }
- /// <summary>
- /// 将一个外键实体加入集合
- /// </summary>
- /// <param name="item"></param>
- /// <returns></returns>
- public abstract void Add(F item);
- /// <summary>
- /// 从集合中移除一个外键实体
- /// </summary>
- /// <param name="item"></param>
- public abstract void Remove(F item);
- /// <summary>
- /// 从集合中移除一个外键实体
- /// </summary>
- /// <param name="index"></param>
- public abstract void RemoveAt(int index);
- /// <summary>
- /// 返回或设置匹配索引的订单详细项
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- public abstract F this[int index]
- {
- set;
- get;
- }
- }
3.2定义从表基类
定义ForeignDataEntityBase
- /// <summary>
- /// 描述主从表的从表的数据实体
- /// </summary>
- /// <typeparam name="ID">从表实体的主键ID</typeparam>
- /// <typeparam name="P">主表</typeparam>
- /// <typeparam name="F">从表</typeparam>
- public abstract class ForeignDataEntityBase<ID, P, F> : EntityBase<F, ID>
- where P : PrimaryDataEntityBase<ID, P, F>
- where F : ForeignDataEntityBase<ID, P, F>
- {
- /// <summary>
- /// ForeignDataEntityBase的默认构造函数
- /// </summary>
- public ForeignDataEntityBase()
- {
- }
- /// <summary>
- /// 按table的第一行数据进行属性的初始化
- /// </summary>
- /// <param name="table"></param>
- public ForeignDataEntityBase(System.Data.DataTable table)
- : this(table, 0)
- {
- }
- /// <summary>
- /// 按table的指定行数据进行属性的初始化
- /// </summary>
- /// <param name="table"></param>
- /// <param name="indexRow"></param>
- public ForeignDataEntityBase(System.Data.DataTable table, int indexRow)
- : base(table, indexRow)
- {
- }
- /// <summary>
- /// 对应主键实体
- /// </summary>
- [System.ComponentModel.DataObjectField(false, false, false)]
- public P RelationOrder
- {
- set;
- get;
- }
- }
3.3使用主从表基类
- /// <summary>
- /// 客户委托单
- /// </summary>
- [System.ComponentModel.DataObject(true)]
- public class BusinessOrder : LogisticsOrderBase<BusinessOrder, BusinessOrderItem>
- {
- }
- /// <summary>
- /// 委托单详细
- /// </summary>
- [System.ComponentModel.DataObject(true)]
- public class BusinessOrderItem : DetailedItemBase<BusinessOrder, BusinessOrderItem>
- {
- }
现在我们的类在定义的时候,就可以非常明确的描述了主从实体的关系,并拥有了数据自动属性装载的能力。
四、结论
反射可以让我们动态的了解对象的所有成员,通过对tabel的遍历和对本对象的遍历,我们可以很方便的实现对属性的赋值,减少了程序员的重复性工作。而减少代码并不是可以偷懒,而是可以降低错误的产生机会。
而泛型可以在具体的实现之前,定义将来的类型,并且能对类型做出很多约束。使用了泛型以后,我们可以在基类为子类的多态做出更多的多态实现,现在我们的类对于静态方法也可以产生多态了,不是吗?
转载于:https://blog.51cto.com/shyleoking/805762