Attribute,特性(之所以不称作属性,是为了跟Property相区分),就是我们在程序中经常碰到的用[]括起来的那个东西,没错,他就是Attributes。
Attributes在.NET中的应用非常广泛,例如我们序列化的时候,需要用到Serializable,定义枚举的时候可能会用到FlagAttribute,定义自定义控件的时候用到的BrowsableAttribute等等,这些都是对Attribute的应用,有关Attribute的更多信息请参考MSDN(http://msdn.microsoft.com/en-us/library/system.attribute.aspx)
在这篇文章中,我们用下面的例子来说明如何在.NET中应用自定义的Attribute
我们在做任何系统的时候,数据库中都会有些Master表,这些表的操作基本都比较简单,只是单表的增删改查
在这个例子中的做法,是给每个表都创建一个Entity的对象,表中的字段就是相应的Filed,然后给每个Entity以及Filed定义好相应的Attribute,这样就可以根据定义的属性自动生成SQL文以简化操作。
好了,下面就开始我们的准备工作。
首先我们需要建两个Attribute:
一个是定义表名的Attribute
- [AttributeUsage(AttributeTargets.Class)]
- internal class DBTableAttribute : Attribute
- {
- private string tableName = String.Empty;
- public DBTableAttribute(string tableName)
- {
- this.tableName = tableName;
- }
- public string TableName
- {
- get { return tableName; }
- set { tableName = value; }
- }
- }
这里需要注意一下AttributeTargets,它表示当前的Attribute的使用权限,有关AttributeTargets的更多信息,请参考MSDN(http://msdn.microsoft.com/zh-cn/library/system.attributetargets.aspx)
还有一个定义字段的Attribute
- [AttributeUsage(AttributeTargets.Field)]
- internal class DBColumnAttribute : Attribute
- {
- private string columnName;//对应数据库的字段名
- private bool isPK;//是否主键
- private bool isSequence;;//是否自增列
- private bool isDelFlag;;//是否删除标记
- public DBColumnAttribute(string columnName)
- : this(columnName, false, false, false)
- {
- }
- public DBColumnAttribute(string columnName, bool isPK, bool isSequence, bool isDelFlag)
- {
- this.columnName = columnName;
- this.isPK = isPK;
- this.isSequence = isSequence;
- this.isDelFlag = isDelFlag;
- }
- public string ColumnName
- {
- get { return columnName; }
- set { columnName = value; }
- }
- public bool IsPK
- {
- get { return isPK; }
- set { isPK = value; }
- }
- public bool IsSequence
- {
- get { return isSequence; }
- set { isSequence = value; }
- }
- public bool IsDelFlag
- {
- get { return isDelFlag; }
- set { isDelFlag = value; }
- }
- }
这里的删除标记的意思是,在操作表的时候我们有时候并不是彻底删除数据,而是修改一个标志位来决定此条记录是否有效。
接下来我们定义两个辅助类,以便在程序中把Attribute的信息读到这两个类中
- internal class EntityAttributeInfo
- {
- private string tableName = String.Empty;
- private Dictionary<string, EntityColumnInfo> columnInfo = new Dictionary<string, EntityColumnInfo>();
- public string TableName
- {
- get { return tableName; }
- set { tableName = value; }
- }
- public Dictionary<string, EntityColumnInfo> ColumnInfo
- {
- get { return columnInfo; }
- set { columnInfo = value; }
- }
- }
- internal class EntityColumnInfo
- {
- private string dbColumnName = String.Empty;
- private string dalColumnName = String.Empty;//数据库的字段对应程序中的Filed的名称,以便使用反射来赋值
- private bool isPK;
- private bool isSequence;
- private bool isDelFlag;
- public string DbColumnName
- {
- get { return dbColumnName; }
- set { dbColumnName = value; }
- }
- public string DALColumnName
- {
- get { return dalColumnName; }
- set { dalColumnName = value; }
- }
- public bool IsPK
- {
- get { return isPK; }
- set { isPK = value; }
- }
- public bool IsSequence
- {
- get { return isSequence; }
- set { isSequence = value; }
- }
- public bool IsDelFlag
- {
- get { return isDelFlag; }
- set { isDelFlag = value; }
- }
- }
然后我们建一个接口,供其他的Entity来继承
- //目前只是空的接口,如果有需要可以增加自己的业务逻辑
- public interface IEntity
- {
- }
好了,准备工作已经完毕,现在来看看我们的表吧
假设我们有表名为tbTest,有如下字段
ColumnName ColumnType IsPK IsSequence IsNullable
Column1 int true true false
Column2 string false false false
Column3 string false false true
Column4 int false false false
那么我们的Entity应该有如下定义
- [DBTableAttribute("tbTest")]
- public class TestEntity : IEntity
- {
- [DBColumn("Column1", IsPK = true, IsSequence = true, IsDelFlag = false)]
- private int column1;
- public int Column1
- {
- get { return column1; }
- set { column1 = value; }
- }
- [DBColumn("Column2", IsPK = false, IsSequence = false, IsDelFlag = false)]
- private string column2;
- public string Column2
- {
- get { return column2; }
- set { column2 = value; }
- }
- [DBColumn("Column3", IsPK = false, IsSequence = false, IsDelFlag = false)]
- private string? column3;
- public string? Column3
- {
- get { return column3; }
- set { column3 = value; }
- }
- [DBColumn("Column4", IsPK = true, IsSequence = false, IsDelFlag = true)]
- private int column4;
- public int Column4
- {
- get { return column4; }
- set { column4 = value; }
- }
- }
然后我们建一个AttributeReader来读取自定义的Attribute的信息
- internal class AttributeReader
- {
- public static EntityAttributeInfo GetAttributeInfo(IEntity etity)
- {
- Type type = etity.GetType();
- return GetAttributeInfo(type);
- }
- public static EntityAttributeInfo GetAttributeInfo(Type type)
- {
- EntityAttributeInfo info = new EntityAttributeInfo();
- info.TableName = GetTableName(type);
- foreach (FieldInfo field in
- type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
- {
- EntityColumnInfo item = GetColumnInfo(field);
- if (item != null)
- {
- info.ColumnInfo.Add(item.DbColumnName, item);
- }
- }
- return info;
- }
- public static string GetTableName(Type type)
- {
- object[] oo = type.GetCustomAttributes(typeof(DBTableAttribute), false);
- if (oo.Length > 0)
- {
- return (oo[0] as DBTableAttribute).TableName;
- }
- return null;
- }
- private static EntityColumnInfo GetColumnInfo(FieldInfo field)
- {
- EntityColumnInfo info = null;
- object[] oo = field.GetCustomAttributes(typeof(DBColumnAttribute), false);
- if (oo.Length > 0)
- {
- DBColumnAttribute columnAttr = oo[0] as DBColumnAttribute;
- info = new EntityColumnInfo();
- info.DbColumnName = columnAttr.ColumnName;
- info.DALColumnName = field.Name;
- info.IsPK = columnAttr.IsPK;
- info.IsSequence = columnAttr.IsSequence;
- info.IsDelFlag = columnAttr.IsDelFlag;
- }
- return info;
- }
- }
最后我们创建一个EntityManager来实现最终的增删改查的SQL文
- public class EntityManager
- {
- public void Insert(IEntity entity)
- {
- try
- {
- EntityAttributeInfo attInfo = AttributeReader.GetAttributeInfo(entity);
- if (String.IsNullOrEmpty(attInfo.TableName))
- {
- throw new Exception(entity.GetType().Name + " cannot be insert to DB,because tablename is null.");
- }
- StringBuilder sb = new StringBuilder();
- StringBuilder sbColumn = new StringBuilder();
- StringBuilder sbValue = new StringBuilder();
- List<SqlParameter> parameters = new List<SqlParameter>();
- Dictionary<string, EntityColumnInfo> columnInfos = attInfo.ColumnInfo;
- string sequenceField = String.Empty;
- foreach (EntityColumnInfo item in columnInfos.Values)
- {
- if (item.IsDelFlag)
- {
- SetObjectMemberValue(entity, item.DALColumnName, "1");
- }
- if (!item.IsSequence)
- {
- sbColumn.AppendFormat("{0},", item.DbColumnName);
- string parameterName = "@" + item.DbColumnName;
- sbValue.AppendFormat("{0},", parameterName);
- object parameterValue = GetObjectMemberValue(entity, item.DALColumnName);
- parameters.Add(new SqlParameter(parameterName, GetDBValue(parameterValue)));
- }
- else
- {
- sequenceField = item.DALColumnName;
- }
- }
- sb.AppendFormat("INSERT INTO {0} ", attInfo.TableName);
- sb.Append("(");
- sb.Append(sbColumn.ToString().TrimEnd(','));
- sb.Append(")");
- sb.Append(" VALUES ");
- sb.Append("(");
- sb.Append(sbValue.ToString().TrimEnd(','));
- sb.Append(")");
- if (!String.IsNullOrEmpty(sequenceField))
- {
- sb.Append(";SELECT @@identity");
- object sequenceValue = DBService.Instance.ExecuteScalar(sb.ToString(), parameters);
- SetObjectMemberValue(entity, sequenceField, Convert.ToInt32(sequenceValue));
- }
- else
- {
- DBService.Instance.ExecuteNonQuery(sb.ToString(), parameters);
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- public void Update(IEntity entity)
- {
- try
- {
- EntityAttributeInfo attInfo = AttributeReader.GetAttributeInfo(entity);
- if (String.IsNullOrEmpty(attInfo.TableName))
- {
- throw new Exception(entity.GetType().Name + " cannot be update to DB,because tablename is null.");
- }
- StringBuilder sbUpdate = new StringBuilder();
- StringBuilder sbWhere = new StringBuilder();
- List<SqlParameter> parameters = new List<SqlParameter>();
- Dictionary<string, EntityColumnInfo> columnInfos = attInfo.ColumnInfo;
- foreach (EntityColumnInfo item in columnInfos.Values)
- {
- if (item.IsPK)
- {
- object parameterValue = GetObjectMemberValue(entity, item.DALColumnName);
- string parameterName = "@" + item.DbColumnName;
- parameters.Add(new SqlParameter(parameterName, GetDBValue(parameterValue)));
- sbWhere.AppendFormat("AND {0} = @{0} ", item.DbColumnName);
- }
- else
- {
- sbUpdate.AppendFormat("{0} = @{0},", item.DbColumnName);
- }
- }
- string sql = String.Format("UPDATE {0} SET {1} WHERE 1=1 {2}",
- attInfo.TableName, sbUpdate.ToString().TrimEnd(','), sbWhere.ToString());
- DBService.Instance.ExecuteNonQuery(sql, parameters);
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- public void Delete(IEntity entity)
- {
- try
- {
- EntityAttributeInfo attInfo = AttributeReader.GetAttributeInfo(entity);
- if (String.IsNullOrEmpty(attInfo.TableName))
- {
- throw new Exception(entity.GetType().Name + " cannot be delete to DB,because tablename is null.");
- }
- string sql = String.Empty;
- StringBuilder sbWhere = new StringBuilder();
- List<SqlParameter> parameters = new List<SqlParameter>();
- Dictionary<string, EntityColumnInfo> columnInfos = attInfo.ColumnInfo;
- string delFlagColumn = String.Empty;
- foreach (EntityColumnInfo item in columnInfos.Values)
- {
- if (item.IsDelFlag)
- {
- delFlagColumn = item.DbColumnName;
- }
- if (item.IsPK)
- {
- object parameterValue = GetObjectMemberValue(entity, item.DALColumnName);
- string parameterName = "@" + item.DbColumnName;
- parameters.Add(new SqlParameter(parameterName, GetDBValue(parameterValue)));
- sbWhere.AppendFormat("AND {0} = @{0} ", item.DbColumnName);
- }
- }
- if (String.IsNullOrEmpty(delFlagColumn))
- {
- sql = String.Format("DELETE FROM {0} WHERE 1=1 {1}", attInfo.TableName, sbWhere.ToString());
- }
- else
- {
- sql = String.Format("UPDATE {0} SET {1} = 0 WHERE 1=1 {2} ",
- attInfo.TableName, delFlagColumn, sbWhere.ToString());
- }
- DBService.Instance.ExecuteNonQuery(sql, parameters);
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- public List<IEntity> GetEntities(Type type)
- {
- return GetEntities(type, null, null);
- }
- public List<IEntity> GetEntities(Type type, string where, List<SqlParameter> parameters)
- {
- List<IEntity> entities = new List<IEntity>();
- StringBuilder sb = new StringBuilder();
- string tableName = AttributeReader.GetTableName(type);
- if (!String.IsNullOrEmpty(tableName))
- {
- sb.AppendFormat("SELECT * FROM {0} ", tableName);
- if (!String.IsNullOrEmpty(where) && parameters != null && parameters.Count > 0)
- {
- sb.AppendFormat("WHERE {0}", where);
- }
- DataTable table = DBService.Instance.ExecuteDataTable(sb.ToString(), tableName, parameters);
- if (table != null)
- {
- foreach (DataRow row in table.Rows)
- {
- IEntity entity = RowMapping2Entity(type, row);
- entities.Add(entity);
- }
- }
- }
- return entities;
- }
- public IEntity GetEntity(Type type, string where, List<SqlParameter> parameters)
- {
- StringBuilder sb = new StringBuilder();
- string tableName = AttributeReader.GetTableName(type);
- IEntity entity = null;
- if (!String.IsNullOrEmpty(tableName))
- {
- sb.AppendFormat("SELECT TOP 1 * FROM {0} ", tableName);
- if (!String.IsNullOrEmpty(where))
- {
- sb.AppendFormat("WHERE {0}", where);
- }
- DataTable table = DBService.Instance.ExecuteDataTable(sb.ToString(), tableName, parameters);
- if (table != null && table.Rows.Count > 0)
- {
- entity = RowMapping2Entity(type, table.Rows[0]);
- }
- }
- return entity;
- }
- private object GetObjectMemberValue(IEntity entity ,string memberName)
- {
- FieldInfo field = GetField(entity, memberName);
- return field.GetValue(entity);
- }
- private void SetObjectMemberValue(IEntity entity, string memberName, object memberValue)
- {
- FieldInfo field = GetField(entity, memberName);
- field.SetValue(entity, GetObjValue(memberValue));
- }
- private FieldInfo GetField(IEntity entity, string memberName)
- {
- return entity.GetType().GetField(memberName, BindingFlags.Instance | BindingFlags.NonPublic);
- }
- private object GetDBValue(object obj)
- {
- if (obj == null)
- {
- return DBNull.Value;
- }
- return obj;
- }
- private object GetObjValue(object obj)
- {
- if (obj == DBNull.Value)
- {
- return null;
- }
- return obj;
- }
- private IEntity RowMapping2Entity(Type type, DataRow row)
- {
- IEntity entity = Activator.CreateInstance(type) as IEntity;
- EntityAttributeInfo attInfo = AttributeReader.GetAttributeInfo(entity);
- foreach (EntityColumnInfo columninfo in attInfo.ColumnInfo.Values)
- {
- SetObjectMemberValue(entity, columninfo.DALColumnName, row[columninfo.DbColumnName]);
- }
- return entity;
- }
- }
这里的DBService替换成你的方法就可以了
好了,这个例子到这里就结束了