二、打造实体基类
关系型数据表中一般有共性的部分是所有的实体都有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); } |