Skip to content

Commit

Permalink
Jc Orm 添加可空枚举类型支持
Browse files Browse the repository at this point in the history
  • Loading branch information
279328316 committed Aug 26, 2024
1 parent 28db573 commit eccde74
Show file tree
Hide file tree
Showing 40 changed files with 2,617 additions and 869 deletions.
171 changes: 123 additions & 48 deletions Jc.Core.Database/Data/Query/EntityConvertor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,122 +50,197 @@ public static DynamicMethod BuildSetValueMethod<T>()
ILGenerateSetValueMethodContent<T>(generator);
return method;
}

/// <summary>
/// IL生成SetValueMethod内容
/// 独立出来为共用代码
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="il"></param>
public static void ILGenerateSetValueMethodContent<T>(ILGenerator il)
{
{ // 使用变量 : Ldarg_0 dr Ldarg_1 entityConvertResult

// UserDto result = new UserDto();
LocalBuilder result = il.DeclareLocal(typeof(T));
il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Stloc, result);

//取出dr中所有的列名集合
il.DeclareLocal(typeof(DataColumnCollection));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("get_Table"));
il.Emit(OpCodes.Callvirt, typeof(DataTable).GetMethod("get_Columns"));
il.Emit(OpCodes.Stloc_1); //var columns = dr.Table.Columns
il.Emit(OpCodes.Nop);

// var columns = dr.Table.Columns
LocalBuilder columns = il.DeclareLocal(typeof(DataColumnCollection));
il.Emit(OpCodes.Ldarg_0); // dr
il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("get_Table"));
il.Emit(OpCodes.Call, typeof(DataTable).GetMethod("get_Columns"));
il.Emit(OpCodes.Stloc, columns); //

//定义局部变量 curColumn
il.Emit(OpCodes.Nop);

//DataColumn curColumn = null;
LocalBuilder curColumn = il.DeclareLocal(typeof(DataColumn));
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Box, typeof(DataColumn)); // boxing the value type to object
il.Emit(OpCodes.Stloc, curColumn); //curColumn = null;
il.Emit(OpCodes.Stloc, curColumn);

//begin try
Label tryLabel = il.BeginExceptionBlock();

List<FieldMapping> piMapList = EntityMappingHelper.GetPiMapList<T>();
foreach (FieldMapping piMap in piMapList)
for (int i = 0; i < piMapList.Count; i++)
{
FieldMapping piMap = piMapList[i];
if (piMap.IsIgnore || piMap.Pi.SetMethod == null)
{
continue;
}
//去掉关键词转义
string fieldName = piMap.FieldName.Replace("\"", "").Replace("'", "").Replace("[", "").Replace("]", "");
//加入判断条件 if (columns.Contains("Id") && !dataRow.IsNull("Id"))
var endIfLabel = il.DefineLabel();
il.Emit(OpCodes.Ldloc_1); //columns

Type type = piMap.PropertyType; //拆箱

//加入判断条件 if (columns.Contains("Id"))
Label label_EndField = il.DefineLabel();
il.Emit(OpCodes.Ldloc, columns); //columns
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Callvirt, typeof(DataColumnCollection).GetMethod("Contains", new Type[] { typeof(string) }));
il.Emit(OpCodes.Brfalse, endIfLabel); //判断columns.Contains("Id")
il.Emit(OpCodes.Call, typeof(DataColumnCollection).GetMethod("Contains", new Type[] { typeof(string) }));
il.Emit(OpCodes.Brfalse_S, label_EndField); // Branch if "Id" does not exist

il.Emit(OpCodes.Ldarg_0); //dr
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(string) }));
il.Emit(OpCodes.Brtrue, endIfLabel); //判断dr.IsNull("Id")
il.Emit(OpCodes.Nop);

il.Emit(OpCodes.Ldloc_1); //columns
//赋值: curColumn = columns["Id"];
il.Emit(OpCodes.Ldloc, columns); //columns
il.Emit(OpCodes.Ldstr, fieldName);
il.Emit(OpCodes.Callvirt, typeof(DataColumnCollection).GetMethod("get_Item", new Type[] { typeof(string) }));
il.Emit(OpCodes.Stloc, curColumn); //赋值: curColumn = columns["Id"];
il.Emit(OpCodes.Call, typeof(DataColumnCollection).GetMethod("get_Item", new Type[] { typeof(string) }));
il.Emit(OpCodes.Stloc, curColumn); // curColumn

//自DataReader中读取值 调用get_Item方法 dataRow["Id"]
il.Emit(OpCodes.Ldloc, result); //result
il.Emit(OpCodes.Ldarg_0); //dataRow
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Callvirt, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) }));
il.Emit(OpCodes.Nop);

Type type = piMap.PropertyType; //拆箱
// 判断dr.IsNull(curColumn)
Label label_ValueNull = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0); //dr
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(string) }));
il.Emit(OpCodes.Brtrue_S, label_ValueNull); //判断dr.IsNull("Id")

il.Emit(OpCodes.Nop);

//不为空时,直接赋值
// result.Id = (int?)dr[dataColumn]; 之前调试时出现过后面属性值为null情况,不确定是否与使用方式有关
// 先使用 result.Id = (int?)dr["Id"];
if (type.IsValueType)
{ //直接拆箱 可空类型,也可以直接拆箱
Type realType = type;
il.Emit(OpCodes.Ldloc, result); //result
il.Emit(OpCodes.Ldarg_0); //dr
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) }));
//il.Emit(OpCodes.Ldloc, curColumn); //Id
//il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(DataColumn) }));

if (piMap.IsEnum)
{
if (type.GenericTypeArguments.Length > 0)
{ //如果为可空枚举类型,抛出异常
realType = type.GenericTypeArguments[0];
throw new Exception($"对象{typeof(T).Name}属性{piMap.Pi.Name}[{realType.Name}]转换异常.不支持可空枚举类型.");
{ // 如果为可空枚举,需要先拆箱为原类型
Type underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
{
il.Emit(OpCodes.Unbox_Any, underlyingType);
il.Emit(OpCodes.Newobj, type.GetConstructor(new[] { underlyingType }));
}
else
{
il.Emit(OpCodes.Unbox_Any, type);
}
}
else
{
il.Emit(OpCodes.Unbox_Any, type);
}
il.Emit(OpCodes.Unbox_Any, realType);
il.Emit(OpCodes.Call, piMap.Pi.GetSetMethod());
}
else
{ //引用类型
{ //引用类型 直接转换 result.UserName = (string)dataRow[dataColumn];
il.Emit(OpCodes.Ldloc, result); //result
il.Emit(OpCodes.Ldarg_0); //dr
il.Emit(OpCodes.Ldstr, fieldName); //Id
il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) }));
//il.Emit(OpCodes.Ldloc, curColumn); //dataColumn
//il.Emit(OpCodes.Call, typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(DataColumn) }));
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Call, piMap.Pi.GetSetMethod());
}

il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Br_S, label_EndField); // 跳转到 "label_EndFieldSet"

il.Emit(OpCodes.Nop);

il.MarkLabel(label_ValueNull);
if (type.IsValueType)
{
Type underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
{ // 可空值类型 赋值为 null
LocalBuilder nullValue = il.DeclareLocal(type);
il.Emit(OpCodes.Ldloca_S, nullValue); // 加载值类型地址
il.Emit(OpCodes.Initobj, type); // null 初始化

il.Emit(OpCodes.Ldloc, result); //result
il.Emit(OpCodes.Ldloca_S, nullValue); // 使用值类型,需要先加载地址,再读取值
il.Emit(OpCodes.Ldobj, type); // 加载对应类型的值到栈顶
il.Emit(OpCodes.Call, piMap.Pi.GetSetMethod()); // 为属性赋值
il.Emit(OpCodes.Nop);
}
else
{ // 如果非可空类型,数据库中为空 throw new Exception("value is null");
il.Emit(OpCodes.Ldstr, "db value is null"); // 加载字符串 "value is null" 到栈顶
il.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new[] { typeof(string) })); // 创建 Exception 实例
il.Emit(OpCodes.Throw); // 抛出异常
il.Emit(OpCodes.Nop);
}
}
else
{ // 如果为引用类型 直接赋值null
il.Emit(OpCodes.Ldloc, result); //result
il.Emit(OpCodes.Ldnull); //null // 给该属性设置 null
il.Emit(OpCodes.Call, piMap.Pi.GetSetMethod());
il.Emit(OpCodes.Nop);
}
//给该属性设置对应值
il.Emit(OpCodes.Callvirt, piMap.Pi.GetSetMethod());
il.MarkLabel(endIfLabel);
il.MarkLabel(label_EndField);
}

//end try

//begin catch 注意,这个时侯堆栈顶为异常信息ex
il.BeginCatchBlock(typeof(Exception));

il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Nop);
LocalBuilder exception = il.DeclareLocal(typeof(Exception));
il.Emit(OpCodes.Stloc, exception);

il.Emit(OpCodes.Ldarg_1);
// exception.IsException = true;
il.Emit(OpCodes.Ldarg_1); // entityConvertResult
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Call, typeof(EntityConvertResult).GetMethod("set_IsException", new Type[] { typeof(bool) }));

il.Emit(OpCodes.Ldarg_1);
// exception.ColumnName = curColumn.ColumnName;
il.Emit(OpCodes.Ldarg_1); // entityConvertResult
il.Emit(OpCodes.Ldloc, curColumn);
il.Emit(OpCodes.Call, typeof(DataColumn).GetMethod("get_ColumnName"));
il.Emit(OpCodes.Call, typeof(EntityConvertResult).GetMethod("set_ColumnName", new Type[] { typeof(string) }));

il.Emit(OpCodes.Ldarg_1);
// exception.Message = ex.Message;
il.Emit(OpCodes.Ldarg_1); // entityConvertResult
il.Emit(OpCodes.Ldloc, exception);
il.Emit(OpCodes.Call, typeof(Exception).GetMethod("get_Message"));
il.Emit(OpCodes.Call, typeof(EntityConvertResult).GetMethod("set_Message", new Type[] { typeof(string) }));

//il.Emit(OpCodes.Ldarg_1); //不对外输出Exception
//il.Emit(OpCodes.Ldloc, exception);
//il.Emit(OpCodes.Call, typeof(EntityConvertResult).GetMethod("set_Exception", new Type[] { typeof(Exception) }));
il.Emit(OpCodes.Nop);

//end catch
// end catch
il.EndExceptionBlock();

/*给本地变量(result)返回值*/
il.Emit(OpCodes.Ldloc, result);
il.Emit(OpCodes.Ret);
il.Emit(OpCodes.Ret);
}
}
}
17 changes: 17 additions & 0 deletions Jc.Core.Database/Data/Query/FieldMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ public class FieldMapping
/// </summary>
public bool IsIgnore { get => fieldAttribute.IsIgnore; }

/// <summary>
/// 是否可为空
/// </summary>
public bool IsNullable
{
get
{
bool isNullable = true;
if (pi.PropertyType.IsValueType)
{
Type underlyingType = Nullable.GetUnderlyingType(pi.PropertyType);
isNullable = underlyingType != null;
}
return isNullable;
}
}

/// <summary>
/// 属性对象
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Jc.Core.Database/Data/Query/QueryFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ private void Clear()
/// <param name="cmd"></param>
public virtual void AddCustomCmd(string cmd)
{
if (string.IsNullOrEmpty(cmd))
{
return;
}
string paramStr = cmd;
this.ItemList.Add(paramStr);
this.filterSQLString += paramStr;
Expand Down
7 changes: 5 additions & 2 deletions Jc.Core.Database/DbContextQueryExpand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -558,11 +558,14 @@ public DataTable GetDataTable(string sql)
/// 执行查询记录,返回DataTable
/// </summary>
/// <returns></returns>
public DataTable GetDataTable<T>(string filterSql)
public DataTable GetDataTable<T>(string filterSql = null)
{
DataTable dt = null;
QueryFilter filter = new QueryFilter();
filter.AddCustomCmd(filterSql);
if (!string.IsNullOrEmpty(filterSql))
{
filter.AddCustomCmd(filterSql);
}
using (DbCommand dbCommand = this.DbProvider.GetQueryAllFieldDbCommand<T>(filter))
{
try
Expand Down
2 changes: 1 addition & 1 deletion Jc.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jc.Core.Sqlite", "Jc.Core.S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jc.Core.Database", "Jc.Core.Database\Jc.Core.Database.csproj", "{2652ADF3-A997-4E86-8311-48C6FE0CA36C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jc.Util.Excel.LocalTests", "Jc.Util.Excel.LocalTests\Jc.Util.Excel.LocalTests.csproj", "{FC4DDBF5-60C7-4E4F-B68E-3A48B1C14057}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jc.Util.Excel.LocalTests", "Jc.Util.Excel.LocalTests\Jc.Util.Excel.LocalTests.csproj", "{FC4DDBF5-60C7-4E4F-B68E-3A48B1C14057}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
Loading

0 comments on commit eccde74

Please sign in to comment.