今天的内容是通过反射获取类的实体,并且进行赋值,调用方法等。
业务场景:不同的部门要录入不同的数据(①页面字段不同②数据库表不同),所以就为这些部门指定了模板(即xml文件),当部门人员要录入时,只需要选择对应的模板,程序通过模板xml中各个结点中配置的信息,来反射实体对象,并加载配置的字段,进行业务逻辑的操作。
下面开始:
首先,要利用反射来实例化一个对象,要拿到的肯定是①这个类所在的项目(即编译后的dll文件)②这个累的命名空间+类名。
就比如下面我配置xml中的这个节点:
{
"specialField": [
{
"fieldText": [
"姓名",
"全名",
],
"fieldIndex": 0,
"ValueType": "string",
"DllName": "Test.dll",
"TableName": "Test.MyModel.UserClass",
"TableField": "Name",
"iSQueryFiled": 1
},
{
"fieldText": [
"电话号码",
"手机号码",
"联系方式"
],
"fieldIndex": 1,
"ValueType": "string",
"DllName": "Test.dll",
"TableName": "Test.MyModel.UserClass",
"TableField": "Phone",
"iSQueryFiled": 1
},
{
"fieldText": [
"性别",
],
"fieldIndex": 2,
"ValueType": "int",
"DllName": "Test.dll",
"TableName": "Test.MyModel.UserClass",
"TableField": "Sex",
"iSQueryFiled": 1
}
}
解释一下节点含义:
"fieldText":用于显示在输入框旁边的提示,提示用户这里该输入什么信息。
"fieldIndex": 字段顺序,用于给页面上各个输入框排序。
"ValueType": 输入框的类型,比如文本框,或者单选框什么的。
"DllName": 被反射实例化的这个类所在的项目编译后的dll文件名。
"TableName": 被实例化的这个类所在的命名控件+类名。
"TableField": 表实体在数据库表中对应的字段名称。
"iSQueryFiled": 是否作为查询条件。因为我们不可能所有的数据都是insert,我们可以用含有这个标识的字段作为条件去数据库中查询是否有符合条件的数据,来决定是做insert还是update,当然,这里也可以用唯一主键值的方式来做,还是那句话,依自己的业务来决定。
当然,根据自己项目的业务,可以适当地封装能满足业务的xml节点。
下面拿这个结点进入代码部分看一下:
Dictionary<string, List<InputTemp_Model>> m_Dic_Input = new Dictionary<string, List<InputTemp_Model>>();//key:dll+class 用来标识字段要插入哪张表。value:放字段名和值,用来插入数据
foreach (var item in Dic_Input)//Dic_Input
{
string sPath = "";
string sType="";
try
{
sPath=item.Value[0].DllName.Trim().Replace(" ", "");
sType = item.Value[0].TableName.Trim().Replace(" ", "");
}
catch (Exception e)
{
throw new Exception("模板节点不完整!");
}
Assembly assembly = Assembly.LoadFile(System.AppDomain.CurrentDomain.BaseDirectory + sPath);//加载类所在的dll文件,System.AppDomain.CurrentDomain.BaseDirectory是获取当前运行文件所在的文件夹(不包含文件名,只到最内层文件夹名)
Type type = assembly.GetType(sType);//获取类
object Table = Activator.CreateInstance(type);//创建该类型的实例
var props = type.GetProperties().ToList();//拿到类中的所有属性
var queryConditionList = (QueryConditionCollection)type.GetProperty("QueryConditionList", BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance).GetValue(Table);//表实体的查询条件列表
item.Value.ForEach(f =>
{
var field = type.GetProperty(f.TableField, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);//在类属性列表中找到和当前字段同名的属性,第二个参数的意思是忽略大小写
var fieldValue = f.Value;//获取要写入字段的值
var fvalue = TypeDescriptor.GetConverter(field.PropertyType).ConvertFrom(fieldValue);//将值进行强制转换,转为类中属性的类型
if (fieldValue != null)
{
field.SetValue(Table, fvalue);//给表实体赋值
}
if (f.iSQueryFiled == 1)//如果作为查询是否存在的字段,就插入查询条件
{
queryConditionList.Add(fvalue, field.Name); //queryConditionList是我封装在类中的查询条件集合,用于拼装查询数据库表时的where条件
}
});
MethodInfo mf_SelectCount = type.GetMethod("SelectCount");//SelectCount是我封装在实体类中用于查询数据库的方法,返回值是数据行数
int dataCount = (Int32)mf_SelectCount.Invoke(Table, null);//调用方法
if (dataCount > 0)//如果有数据,就做upt
{
Update((IEntity2)Table);//我封装的方法,用来修改数据库数据,觉得这里不好封装的小伙伴,可以将这个方法封装在实体表那个类中
}
else//否则,做insert
{
foreach (PropertyInfo info in props)//循环表属性,给主键赋值
{
var att = info.GetCustomAttribute<ColumnAttribute>();
if (att != null)
{
if (att.PrimaryKey == true)
{
int newId = (Int32)(type.GetMethod("GetNewId_Int", new Type[] { typeof(string) }).Invoke(Table, new object[] { info.Name }));
var field = type.GetProperty(info.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
field.SetValue(Table, newId);//给表实体赋值
break;
}
}
}
Insert((IEntity2)Table);//我封装的方法,用来插入数据库数据,觉得这里不好封装的小伙伴,可以将这个方法封装在实体表那个类中
}
}
上面就是利用反射来实例化实体,并且完成与数据库交互,注释很详细,认真去看就可以,这里不进行赘述。下面展示一下我字典中那个value的封装。
public class InputTemp_Model
{
public string DllName { get; set; }
public string TableName { get; set; }
public string FieldName { get; set; }
public int iSQueryFiled { get; set; }
public string Value { get; set; } //业务表中的字段值
}
因为只是一个demo,所以只简单放了这些基础字段。
也可以用反射来生成各种控件,这个以后得空了讲。