在日常进行数据库操作的过程中,我的数据层使用的是微软企业库,但对于多字段的数据的插入与更新时写sql语句就会显得特别费时间,还会经常出现错误耗费时间排查,所以决定基于微软企业库封装一个轻量级的ORM框架(基于mysql),来简化数据库操作。
首先解决的问题就是实体类与数据库表的字段映射,这里使用的是反射,先上个代码
public T ConvertDataToEntity<T>(DataRow row) where T:TModel
{
Dictionary<string, object> dic = new Dictionary<string, object>();
DataColumnCollection columns = row.Table.Columns;
//获取对象类型
Type tp = typeof(T);
//获取空构造函数
ConstructorInfo ct = tp.GetConstructor(System.Type.EmptyTypes);
//new 对象
T entity = (T)ct.Invoke(null);
//获取所有公有字段
PropertyInfo[] infos = tp.GetProperties();
//遍历字段进行查询结果的逐个赋值
foreach (PropertyInfo info in infos)
{
dic.Add(info.Name, null);
if (columns.Contains(info.Name))
{
object fieldvalue = null;
//判断查出的值是否为null,不为null才能反射赋值
if (!Convert.IsDBNull(row[info.Name]))
{
fieldvalue = row[info.Name];
}
else
{
fieldvalue = info.PropertyType.IsValueType ? Activator.CreateInstance(info.PropertyType) : null;
}
info.SetValue(entity, fieldvalue);
if (dic.Keys.Contains(info.Name))
{
dic[info.Name] = fieldvalue;
}
entity.FieldValueMapper = dic;
}
}
return entity;
}
具体的步骤就是首先获取传入对象类型的Type,获取空的构造函数,调用构造函数new一个对象出来,获取实体类的所有字段和取数据库中的值进行字段匹配赋值,封装的方法中都是使用了泛型,是为了更好的通用性,传入什么对象,映射返回就是什么对象,强类型引用避免了强制转换装箱拆箱的过程。
关于数据库的操作是基于微软企业库的,都封装在了Command对象中,先来看下代码结构
public class Command
{
public Database DB { set; get; }
public string CommandText { set; get; }
public List<Param> ParamDic { set; get; }
public ORMEnum.OptionType CommandType { set; get; }
public DbTransaction CommandTransition { set; get; }
public MapperUtil Mapper { set; get; }
public Command()
{
Mapper = new MapperUtil();
}
public void ExecuteCommand()
{
string strSql = (DB.DbProviderFactory.ToString() != "System.Data.OracleClient.OracleClientFactory") ?
CommandText :
CommandText;
DbCommand cmd = DB.GetSqlStringCommand(strSql);
if (null != ParamDic)
{
foreach (Param param in ParamDic)
{
DB.AddInParameter(cmd, param.ParamName, param.ParamType, param.ParamValue);
}
}
DB.ExecuteNonQuery(cmd);
if (null != CommandTransition)
{
DB.ExecuteNonQuery(cmd, CommandTransition);
}
else
{
DB.ExecuteNonQuery(cmd);
}
}
public List<T> QueryCommand<T>() where T : TModel
{
List<T> list = new List<T>();
string strSql = (DB.DbProviderFactory.ToString() != "System.Data.OracleClient.OracleClientFactory") ?
CommandText :
CommandText;
DbCommand cmd = DB.GetSqlStringCommand(strSql);
if (null != ParamDic)
{
foreach (Param param in ParamDic)
{
DB.AddInParameter(cmd, param.ParamName, param.ParamType, param.ParamValue);
}
}
DataView result = DB.ExecuteDataView(cmd);
foreach (DataRow row in result.Table.Rows)
{
T entity = Mapper.ConvertDataToEntity<T>(row);
list.Add(entity);
}
return list;
}
里面封装了两个比较常用类型的方法,一个是执行数据库操作,例如插入和更新等,一个是查询操作,用来返回映射后的数据对象List。初始化Command对象的工作封装在了BaseCommand类中,返回一个Command对象,再执行Comadn对象的Execute的方法,主要是为了封装事物的执行,耦合度太高会影响后续扩展,下面来看下ORM框架事物是如何实现的。
1.在Command中已经可以看到定义了Transition对象,会根据对象属性判断是否执行事物
2.在对外访问的类DBUtil中我们会封装一个Transition对象,在执行所有方法时会判断这个事物对象是否为空,不为空复制给Command对象的事物成员对象
3.ExecuteTransition方法以Deletegate委托的形式接收用户的操作,并对事物对象初始化,所有的操作就会关联同一个事物
看下ExecuteTransition的代码与用法
public void ExecuteTransition(TransitionHandler handler, out string exceptionStr)
{
exceptionStr = string.Empty;
using (DbConnection connection = db.CreateConnection())
{
connection.Open();
transaction = connection.BeginTransaction();
try
{
handler.Invoke();
transaction.Commit();
}
catch (Exception e)
{
exceptionStr = e.Message + e.StackTrace;
transaction.Rollback();
throw e;
}
finally
{
connection.Close();
transaction = null;
}
}
}
最后说一下对实体类的定义,非常简单,只要和数据库表一致就可以,另外给主键标志为自定义的Attribute,从而可以智能的实现主键查找与删除方法的实现
public class frame_user:TModel
{
[Key]
public string userguid { set; get; }
public string loginid { set; get; }
public string displayname { set; get; }
public string password { set; get; }
}
注:框架中还封装了一些常用方法,例如简单的分页查找,批量插入,根据主键查找,直接执行sql,其中事物同时支持sql与ORM操作的混合使用,希望大家多多提意见与交流,我可以进一步完善这个框架,感谢!
更新:最近FastORM加入弱引用类型的简单增删改查对象,对反射使用表达式树优化选项,加入支持lamda表达式的泛型查找方法,框架的具体使用说明已在项目ReadMe中添加