想做这个事情的原因:
公司因为升级系统的原因将WCF作为整个系统的中间部分,负责连接数据库传递数据的工作。而这个任务落到了我的头上。
在使用ORM(CHLOE)的过程中,发现一个问题,就是每一个表如果要实现增删改查的话都需要写一个相应的方法。这是一个重复到极点的工作,是我不能忍受的事情。于是想通过ORM实现通用型的增删改查工作,减轻负担。只需要增加表类,不需要增加SQL语句。
这个项目中我用的是chole
先订立一个目标:
增删改查都只需要写一个方法,增加ORM表类的同时立刻能够使用这几个方法完成增删改查的操作!
为了完成这个目标需要解决的问题:
1、如何在不知道类对象是什么的情况下获得属性的值;
2、如何做到传递一个类,让方法知道它的条件和更新字段;
3、如果做到更新时,只更新可以更新的属性
解决问题1:
使用反射获得对象类的属性和值,typeof().getpropertys()
解决问题2:
在类中增加两个List字段,一个存储Where属性,一个存储Update属性,类型为List
解决问题3:
增加一个在程序运行的过程中不变的字符串数组,存储可更新字段
解决的代码如下:
///
/// 所有表的基类,主要用来区分条件和更新的属性
///
public class TableBase
{
[JsonProperty]//让jsonconvert在执行转换的时候,转换这两个内部变量
private List<string> ListWhere { get { return _listW; } set { _listW = value; } }//保存条件的属性名称
private List<string> _listW = new List<string>();
[JsonProperty]
private List<string> ListUpdate { get { return _listU; } set { _listU = value; } }//保存更改的属性名称
private List<string> _listU = new List<string>();
private int _isWhereOrUpdate = 0;//是否条件,为0既不是更新也不是条件
///
/// 设置接下来赋值的属性不写入_listWhere和_listUpdate
///
public void SetNothing()
{
_isWhereOrUpdate = 0;
}
///
/// 设置接下来赋值的属性是条件
///
public void SetWhere()
{
_isWhereOrUpdate = 1;
}
///
/// 设置接下来赋值的属性是更新值
///
public void SetUpdate()
{
_isWhereOrUpdate = 2;
}
///
/// 返回条件属性队列
///
///
public List<string> GetListWhere()
{
return ListWhere;
}
///
/// 返回更新属性队列
///
///
public List<string> GetListUpdate()
{
return ListUpdate;
}
///
/// 将修改的数据增加到List中,注意此方法会将原来保存在LIST中的数据删除
///
/// 属性名称
/// 更新的列名
public void AddP(string value, string[] update)
{
for (int i = 0; i < ListUpdate.Count; i++)//时间复杂度O(2n-index)
{
if (ListUpdate[i] == value)
{
ListUpdate.RemoveAt(i);
}
}
for (int i = 0; i < ListWhere.Count; i++)//时间复杂度O(2n-index)
{
if (ListWhere[i] == value)
{
ListWhere.RemoveAt(i);
}
}
//添加数据到List中
if (_isWhereOrUpdate == 1)
{
ListWhere.Add(value);
}
else if (_isWhereOrUpdate == 2 && update != null && update.Contains(value)) //如果是能更新的字段
{
ListUpdate.Add(value);
}
}
///
/// 清空更新和条件队列
///
public void Clear()
{
ListWhere.Clear();
ListUpdate.Clear();
}
///
/// 获得set方法所在的属性名称
/// 若使用release版本,frame.GetMethod()获取的是执行的方法,不会获取类中的方法。
/// release中会获取如TestSelect*()的方法,Debug获取SetProperity方法
///
/// 返回属性名称
public string GetPN()
{
StackTrace trace = new StackTrace(true);
StackFrame frame = trace.GetFrame(1);//1代表上级,2代表上上级,以此类推
MethodBase method = frame.GetMethod(); //获得当前方法名称
//Console.WriteLine(memberName);
try
{
var property = (from f in method.DeclaringType.GetProperties()
where f.GetSetMethod() == method || f.GetGetMethod() == method
select f).FirstOrDefault();
return property.Name;
}
catch
{
return null;
}
}
}
上面的代码主要解决了获取属性名称、保存更新List和条件List的问题。能够更新的字段放在每一个Table表当中。
数据库表类结构如下图所示:
public class T_TRACK : TableBase
{
private static readonly string[] UPDATE = {"SFZMHM" };//能够更新的字段
public string SFZMHM { get { return _sfzmhm; } set { _sfzmhm = value; AddP(GetPN(), UPDATE); } }
private string _sfzmhm;
public string TESTDATE { get { return _testdate; } set { _testdate = value; AddP(GetPN(), UPDATE); } }
private string _testdate;
public int? KSCS { get { return _kscs; } set { _kscs = value; AddP(GetPN(), UPDATE); } }
private int? _kscs;
public string DATA { get { return _data; } set { _data = value; AddP(GetPN(), UPDATE); } }
private string _data;
}
上面的代码展示的是一个表的结构,其中UPDATE字段存储了此表可以由客户端更新的字段名称,如果为空的话表示客户端不能更新任何字段。
既然获得属性和值得问题解决了,那么现在就可以利用ORM和上面的TABLE内容写通用的更新代码了。如下:
///
/// 获得当前ListWhere或者ListUpdate的对象的属性
///
///
///
/// 返回PropertyInfo的列表
private static List<PropertyInfo> GetPropertys<T>(T value, bool isWhere = true) where T : TableBase
{
//直接利用基类得到listStr
List<string> ListPropertyStr = null;
if (isWhere)
ListPropertyStr = value.GetListWhere();
else
ListPropertyStr = value.GetListUpdate();
//如果不能获得List的话,返回空
if (ListPropertyStr == null || ListPropertyStr.Count == 0)
{
return null;
}
//使用属性方法获得变量值,原来的方法是先获得所有属性,接着判断属性是否在listStr中
List<PropertyInfo> listProperty = new List<PropertyInfo>();//属性
for (int i = 0; i < ListPropertyStr.Count; i++)
{
listProperty.Add(typeof(T).GetProperty(ListPropertyStr[i]));
}
return listProperty;
}
///
/// 通用型数据更新方
///
/// 表类
/// 表类实体
///
public static int UpdateData<T>(T value) where T : TableBase
{
List<PropertyInfo> listWhereP = GetPropertys(value);
List<PropertyInfo> listUpdateP = GetPropertys(value, false);
//如果没有相应的属性的话,返回空
if (listWhereP == null || listUpdateP == null || listWhereP.Count == 0 || listUpdateP.Count == 0)
{
return 0;
}
int updateRes = 0;//返回结果
//更新语句
try
{
OracleContext context = new OracleContext(new OracleConnectionFactory(GetConnectionStr()));
//查询SQL语句与变量
DbParam[] paras = new DbParam[listWhereP.Count + listUpdateP.Count];//SQL变量保存的地方
//update
string sql = "update " + typeof(T).Name + " set ";
for (int i = 0; i < listUpdateP.Count - 1; i++)
{
sql += listUpdateP[i].Name + " = " + " :U" + i + ", ";
paras[i] = DbParam.Create(":U" + i, listUpdateP[i].GetValue(value, null));
}
sql += listUpdateP[listUpdateP.Count - 1].Name + " = " + " :U" + (listUpdateP.Count - 1) + " where ";
paras[listUpdateP.Count - 1] = DbParam.Create(":U" + (listUpdateP.Count - 1), listUpdateP[listUpdateP.Count - 1].GetValue(value, null));
//where
for (int i = 0; i < listWhereP.Count - 1; i++)
{
sql += listWhereP[i].Name + " = " + " :W" + i + " AND ";
paras[i + listUpdateP.Count] = DbParam.Create(":W" + i, listWhereP[i].GetValue(value, null));
}
sql += listWhereP[listWhereP.Count - 1].Name + " = " + " :W" + (listWhereP.Count - 1);
paras[listWhereP.Count - 1 + listUpdateP.Count] = DbParam.Create(":W" + (listWhereP.Count - 1), listWhereP[listWhereP.Count - 1].GetValue(value, null));
//查询SQL语句的执行之处
updateRes = context.Session.ExecuteNonQuery(sql, paras);
context.Dispose();
return updateRes;
}
catch (Exception ex)
{
LogHelper.WriteLogWcf(ex.Message, "UpdateData()");
throw new Exception(ex.Message);
}
}
比较遗憾的地方是通用型的方法依旧使用了SQL语句,并没有利用到ORM的易用性。但目前我还没有想到更符合更简洁的方案。
另外一点说明是关于TableBase类中SetWhere(),SetUpdate(),SetNothing()三个方法。
主要含义和命名方式一个意思,在使用了SetWhere()后,后面赋值的属性自动定义为条件,使用SetUpdate()后,后面赋值的属性定义为更新字段,SetNothing后,后面赋值的属性不做定义,主要用于写入数据。
调用的代码如下:
T_TRACK track=new T_TRACK();
track.SetWhere();
track.KSCS=2;
track.SetUpdate();
track.SFZMHM="1202222211111111233";
UpdateData(track);
这就是通用性更新、查询、删除、插入的思路和方案。