当把常用的特性填写到POCO实体类时,执行数据库操作时,需要根据实体类上的特性信息进行相应的操作,PetaPoco中的TableInfo和ColumnInfo类就是用来保存实体类上的特性信息。
TableInfo用来保存数据库表的信息,包括TableName,PrimaryKey,主键是否自增字段,使用Oracle数据库时的Sequence名称。
ColumnInfo用来保存数据库表的列信息,包括ColumnName,是否结果列(不用更新数据库),数据库列类型是否转换为UTC时间、更新模板和插入模板。
通过POCO实体类获取到其对应的数据库表和数据库列的信息,就可以使用这些信息对数据库表执行增删改查的操作。
TableInfo包括一个静态方法,参数为POCO实体类的Type对象,从该对象上获取自定义特性信息来初始化TableInfo实例。
1 /// <summary> 2 /// POCO实体类对应的数据库表信息 3 /// </summary> 4 public class TableInfo 5 { 6 /// <summary> 7 /// 数据库表名 8 /// </summary> 9 public string TableName { get; set; } 10 11 /// <summary> 12 /// 数据库表主键 13 /// </summary> 14 public string PrimaryKey { get; set; } 15 16 /// <summary> 17 /// 主键是否自增 18 /// </summary> 19 public bool AutoIncrement { get; set; } 20 21 /// <summary> 22 /// Oracle数据自增主键对应的Sequence名称 23 /// </summary> 24 public string SequenceName { get; set; } 25 26 /// <summary> 27 /// 从POCO实体类的特性上初始化TableInfo实例 28 /// </summary> 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实例
/// <summary> /// POCO实体类属性对应的数据库列信息 /// </summary> public class ColumnInfo { /// <summary> /// 数据库列名称 /// </summary> public string ColumnName { get; set; } /// <summary> /// 是否结果值列,是的话,插入和更新操作不使用该属性 /// </summary> public bool ResultColumn { get; set; } /// <summary> /// 若对应的数据库类类型是DateTime,是否强制转换为UTC时间 /// </summary> public bool ForceToUtc { get; set; } /// <summary> /// 插入模板(暂未理解使用) /// </summary> public string InsertTemplate { get; set; } /// <summary> /// 更新模板(暂未理解使用) /// </summary> public string UpdateTemplate { get; set; } /// <summary> /// 从POCO实体类属性的特性初始化ColumnInfo /// </summary> 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 /// <summary> 2 /// 缓存信息类 3 /// </summary> 4 internal class Cache<TKey, TValue> 5 { 6 /// <summary> 7 /// 读写锁 8 /// </summary> 9 private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); 10 11 /// <summary> 12 /// 存储数据的字典 13 /// </summary> 14 private Dictionary<TKey, TValue> _map = new Dictionary<TKey, TValue>(); 15 16 /// <summary> 17 /// 缓存数据的数目 18 /// </summary> 19 public int Count 20 { 21 get { return _map.Count; } 22 } 23 24 /// <summary> 25 /// 根据Key获取对应的数据,若没有数据,则创建它并缓存起来 26 /// </summary> 27 public TValue Get(TKey key, Func<TValue> factory) 28 { 29 _lock.EnterReadLock();//设置读锁 30 TValue val; 31 try 32 { 33 if (_map.TryGetValue(key, out val)) 34 return val; 35 } 36 finally 37 { 38 _lock.ExitReadLock();//释放读锁 39 } 40 _lock.EnterWriteLock();//没有找到,设置写锁 41 try 42 { 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 finally 52 { 53 _lock.ExitWriteLock();//释放写锁 54 } 55 } 56 57 //清空缓存信息 58 public void Flush() 59 { 60 _lock.EnterWriteLock(); 61 try 62 { 63 _map.Clear(); 64 } 65 finally 66 { 67 _lock.ExitWriteLock(); 68 } 69 } 70 }