当把常用的特性填写到POCO实体类时,执行数据库操作时,需要根据实体类上的特性信息进行相应的操作,PetaPoco中的TableInfo和ColumnInfo类就是用来保存实体类上的特性信息。
TableInfo用来保存数据库表的信息,包括TableName,PrimaryKey,主键是否自增字段,使用Oracle数据库时的Sequence名称。
ColumnInfo用来保存数据库表的列信息,包括ColumnName,是否结果列(不用更新数据库),数据库列类型是否转换为UTC时间、更新模板和插入模板。
通过POCO实体类获取到其对应的数据库表和数据库列的信息,就可以使用这些信息对数据库表执行增删改查的操作。
TableInfo包括一个静态方法,参数为POCO实体类的Type对象,从该对象上获取自定义特性信息来初始化TableInfo实例。
1 ///2 /// POCO实体类对应的数据库表信息 3 /// 4 public class TableInfo 5 { 6 ///7 /// 数据库表名 8 /// 9 public string TableName { get; set; }10 11 ///12 /// 数据库表主键13 /// 14 public string PrimaryKey { get; set; }15 16 ///17 /// 主键是否自增18 /// 19 public bool AutoIncrement { get; set; }20 21 ///22 /// Oracle数据自增主键对应的Sequence名称23 /// 24 public string SequenceName { get; set; }25 26 ///27 /// 从POCO实体类的特性上初始化TableInfo实例28 /// 29 public static TableInfo FromPoco(Type t)30 {31 TableInfo ti = new TableInfo();32 //从TableNameAttribute上获取数据表名称,若不存在则使用POCO实体类的名称33 var a = t.GetCustomAttributes(typeof(TableNameAttribute), true);34 ti.TableName = a.Length == 0 ? t.Name : (a[0] as TableNameAttribute).Value;35 36 //从PrimaryKeyAttribute上获取数据库表主键名称37 a = t.GetCustomAttributes(typeof(PrimaryKeyAttribute), true);38 ti.PrimaryKey = a.Length == 0 ? null : (a[0] as PrimaryKeyAttribute).Value;39 ti.SequenceName = a.Length == 0 ? null : (a[0] as PrimaryKeyAttribute).SequenceName;40 ti.AutoIncrement = a.Length == 0 ? false : (a[0] as PrimaryKeyAttribute).AutoIncrement;41 //若不存在PrimaryKeyAttribute,则查找实体类属性中名称为“id"或者实体类名称+“id"或者“_id"的类属性。42 if (string.IsNullOrEmpty(ti.PrimaryKey))43 {44 var prop = t.GetProperties().FirstOrDefault(p =>45 {46 if (p.Name.Equals("id", StringComparison.OrdinalIgnoreCase))47 return true;48 if (p.Name.Equals(t.Name + "id", StringComparison.OrdinalIgnoreCase))49 return true;50 if (p.Name.Equals(t.Name + "_id", StringComparison.OrdinalIgnoreCase))51 return true;52 return false;53 });54 if (prop != null)55 {56 ti.PrimaryKey = prop.Name;57 ti.AutoIncrement = prop.PropertyType.IsValueType;58 }59 }60 return ti;61 }62 }
ColumnInfo包括一个静态方法,参数为POCO实体类的属性对象,从对象上获取自定义特性信息来初始化ColumnInfo实例
////// POCO实体类属性对应的数据库列信息 /// public class ColumnInfo { ////// 数据库列名称 /// public string ColumnName { get; set; } ////// 是否结果值列,是的话,插入和更新操作不使用该属性 /// public bool ResultColumn { get; set; } ////// 若对应的数据库类类型是DateTime,是否强制转换为UTC时间 /// public bool ForceToUtc { get; set; } ////// 插入模板(暂未理解使用) /// public string InsertTemplate { get; set; } ////// 更新模板(暂未理解使用) /// public string UpdateTemplate { get; set; } ////// 从POCO实体类属性的特性初始化ColumnInfo /// public static ColumnInfo FromProperty(PropertyInfo propertyInfo) { // 获取属性所属的类实例上是否包括明确表示列信息的标志 var explicitColumns = propertyInfo.DeclaringType.GetCustomAttributes(typeof(ExplicitColumnsAttribute), true).Length > 0; // Check for [Column]/[Ignore] Attributes var colAttrs = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), true); //属性明确指定列信息时,但是该属性没有列特性,返回null if (explicitColumns) { if (colAttrs.Length == 0) return null; } else { //没有明确指定时,该属性没有映射到数据库列,返回null if (propertyInfo.GetCustomAttributes(typeof(IgnoreAttribute), true).Length != 0) return null; } //类具有明确映射信息,则从列特性获取对应的数据信息或者没有明确信息时,则使用属性名称初始化。 var ci = new ColumnInfo(); // Read attribute if (colAttrs.Length > 0) { var colattr = (ColumnAttribute) colAttrs[0]; ci.InsertTemplate = colattr.InsertTemplate; ci.UpdateTemplate = colattr.UpdateTemplate; ci.ColumnName = colattr.Name == null ? propertyInfo.Name : colattr.Name; ci.ForceToUtc = colattr.ForceToUtc; if ((colattr as ResultColumnAttribute) != null) ci.ResultColumn = true; } else { ci.ColumnName = propertyInfo.Name; ci.ForceToUtc = false; ci.ResultColumn = false; } return ci; } } PetaPoco中定义了一个缓存类,内部使用ReaderWriterLockSlim读写锁来控制读取和写入(ReaderWriterLockSlim支持多线程读取和单线程写入), 其公共方法Get的参数为缓存Key和创建函数,当缓存中没有对应的信息时,则使用创建函数创建信息并缓存起来。 方法内部先获取读取锁获取缓存信息,若没有则获取写入锁,获取后再次确认是否存在缓存信息,主要原因:获取写入锁需要释放之前所有的读写锁,防止在时间差创建了缓存信息。
1 ///2 /// 缓存信息类 3 /// 4 internal class Cache5 { 6 /// 7 /// 读写锁 8 /// 9 private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();10 11 ///12 /// 存储数据的字典13 /// 14 private Dictionary_map = new Dictionary ();15 16 /// 17 /// 缓存数据的数目18 /// 19 public int Count20 {21 get { return _map.Count; }22 }23 24 ///25 /// 根据Key获取对应的数据,若没有数据,则创建它并缓存起来26 /// 27 public TValue Get(TKey key, Funcfactory)28 {29 _lock.EnterReadLock();//设置读锁30 TValue val;31 try32 {33 if (_map.TryGetValue(key, out val))34 return val;35 }36 finally37 {38 _lock.ExitReadLock();//释放读锁39 }40 _lock.EnterWriteLock();//没有找到,设置写锁41 try42 {43 //再次检测,避免等待获取写锁的时间差中已创建信息44 if (_map.TryGetValue(key, out val))45 return val;46 //使用传入的创建方法创建信息并缓存起来47 val = factory();48 _map.Add(key, val);49 return val;50 }51 finally52 {53 _lock.ExitWriteLock();//释放写锁54 }55 }56 57 //清空缓存信息58 public void Flush()59 {60 _lock.EnterWriteLock();61 try62 {63 _map.Clear();64 }65 finally66 {67 _lock.ExitWriteLock();68 }69 }70 }