diff --git a/src/CommandGenerator/CommandGenerator.cs b/src/CommandGenerator/CommandGenerator.cs index 98a6284..ac13944 100644 --- a/src/CommandGenerator/CommandGenerator.cs +++ b/src/CommandGenerator/CommandGenerator.cs @@ -1,4 +1,5 @@ -using Kros.KORM.Converter; +using Kros.Caching; +using Kros.KORM.Converter; using Kros.KORM.Metadata; using Kros.KORM.Properties; using Kros.KORM.Query; @@ -8,6 +9,8 @@ using System.Collections.Generic; using System.Data.Common; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using System.Text; namespace Kros.KORM.CommandGenerator @@ -31,12 +34,14 @@ internal class CommandGenerator : ICommandGenerator #region Private Fields - private TableInfo _tableInfo; - private KORM.Query.IQueryProvider _provider; - private IQueryBase _query; + private readonly TableInfo _tableInfo; + private readonly KORM.Query.IQueryProvider _provider; + private readonly IQueryBase _query; private List _columnsInfo = null; private int _maxParametersForDeleteCommandsInPart = DEFAULT_MAX_PARAMETERS_FOR_DELETE_COMMANDS_IN_PART; - private Lazy _outputStatement; + private readonly Lazy _outputStatement; + private readonly ICache _delegatesCache = new Cache(); + private delegate object GetColumnValueDelegate(T item); #endregion @@ -280,7 +285,9 @@ private void AddParametersToCommand(DbCommand cmd, IEnumerable colum /// public object GetColumnValue(ColumnInfo columnInfo, T item) { - var value = columnInfo.PropertyInfo.GetValue(item, null); + GetColumnValueDelegate invokeDelegate = GetDelegate(item, columnInfo); + var value = invokeDelegate(item); + if (value != null) { var converter = ConverterHelper.GetConverter(columnInfo, value.GetType()); @@ -368,6 +375,36 @@ private string GetDeleteCommandText(IEnumerable columns) return string.Format(DELETE_QUERY_BASE, _tableInfo.Name, paramWherePart.ToString()); } + private GetColumnValueDelegate GetDelegate(T item, ColumnInfo columnInfo) + { + var key = $"{columnInfo.Name}-{typeof(T).FullName}".GetHashCode(); + + return _delegatesCache.Get(key, () => CreateDelegate(columnInfo)) as GetColumnValueDelegate; + } + + private GetColumnValueDelegate CreateDelegate(ColumnInfo columnInfo) + { + var dynamicMethodArgs = new Type[] { typeof(T) }; + var dynamicMethod = new DynamicMethod("GetColumnValue", typeof(object), dynamicMethodArgs); + ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); + + ilGenerator.Emit(OpCodes.Ldarg_0); + + MethodInfo fnGetValue = typeof(T).GetProperty( + columnInfo.PropertyInfo.Name, BindingFlags.Public | BindingFlags.Instance).GetGetMethod(); + + ilGenerator.Emit(OpCodes.Callvirt, fnGetValue); + + if (columnInfo.PropertyInfo.PropertyType.IsValueType) + { + ilGenerator.Emit(OpCodes.Box, columnInfo.PropertyInfo.PropertyType); + } + + ilGenerator.Emit(OpCodes.Ret); + + return dynamicMethod.CreateDelegate(typeof(GetColumnValueDelegate)) as GetColumnValueDelegate; + } + #endregion } } diff --git a/src/Query/DbSet.cs b/src/Query/DbSet.cs index baa3159..db1fbc5 100644 --- a/src/Query/DbSet.cs +++ b/src/Query/DbSet.cs @@ -1,4 +1,5 @@ -using Kros.KORM.CommandGenerator; +using Kros.Caching; +using Kros.KORM.CommandGenerator; using Kros.KORM.Data; using Kros.KORM.Exceptions; using Kros.KORM.Metadata; @@ -10,6 +11,8 @@ using System.Data; using System.Data.Common; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using System.Threading.Tasks; namespace Kros.KORM.Query @@ -22,13 +25,15 @@ public class DbSet : IDbSet { #region Private fields - private ICommandGenerator _commandGenerator; - private IQueryProvider _provider; - private IQueryBase _query; - private HashSet _addedItems = new HashSet(); - private HashSet _editedItems = new HashSet(); - private HashSet _deletedItems = new HashSet(); + private readonly ICommandGenerator _commandGenerator; + private readonly IQueryProvider _provider; + private readonly IQueryBase _query; + private readonly HashSet _addedItems = new HashSet(); + private readonly HashSet _editedItems = new HashSet(); + private readonly HashSet _deletedItems = new HashSet(); private readonly TableInfo _tableInfo; + private readonly ICache _delegatesCache = new Cache(); + private delegate void SetIdentityPrimaryKeyDelegate(T item, object id); #endregion @@ -296,7 +301,8 @@ private async Task CommitChangesAddedItemsAsync(HashSet items, bool useAsync) if (hasIdentity) { var id = await ExecuteScalarAsync(command, useAsync); - _tableInfo.IdentityPrimaryKey.SetValue(item, id); + SetIdentityPrimaryKeyDelegate invokeDelegate = GetDelegate(item, _tableInfo.IdentityPrimaryKey); + invokeDelegate(item, id); } else { @@ -468,6 +474,36 @@ private async Task BulkUpdateCoreAsync( } } + private SetIdentityPrimaryKeyDelegate GetDelegate(T item, ColumnInfo columnInfo) + { + var key = $"{columnInfo.Name}-{typeof(T).FullName}".GetHashCode(); + + return _delegatesCache.Get(key, () => CreateDelegate(columnInfo)) as SetIdentityPrimaryKeyDelegate; + } + + private SetIdentityPrimaryKeyDelegate CreateDelegate(ColumnInfo columnInfo) + { + var dynamicMethodArgs = new Type[] { typeof(T), typeof(object) }; + var dynamicMethod = new DynamicMethod("IdentityPrimaryKey_SetValue", typeof(void), dynamicMethodArgs); + ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); + + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Ldarg_1); + + if (columnInfo.PropertyInfo.PropertyType.IsValueType) + { + ilGenerator.Emit(OpCodes.Unbox_Any, columnInfo.PropertyInfo.PropertyType); + } + + MethodInfo fnGetIdentity = typeof(T).GetProperty( + columnInfo.Name, BindingFlags.Public | BindingFlags.Instance).GetSetMethod(); + + ilGenerator.Emit(OpCodes.Callvirt, fnGetIdentity); + ilGenerator.Emit(OpCodes.Ret); + + return dynamicMethod.CreateDelegate(typeof(SetIdentityPrimaryKeyDelegate)) as SetIdentityPrimaryKeyDelegate; + } + #endregion #region IEnumerator