Since reflection on a row by row basis is rather expensive, I've been looking for a faster alternative to build and insert entities. I did some research research on the subject and also found some performance comparisons which seem to indicate that the Expression trees are the way to go. How would I rework the following function to take advantage of this?
public static void InsertTable(IEnumerable<DataTable> chunkedTable)
{
Parallel.ForEach(
chunkedTable,
new ParallelOptions
{
MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
},
chunk =>
{
Realty_Records_ProdEntities entities = null;
try
{
entities = new Realty_Records_ProdEntities();
entities.Configuration.AutoDetectChangesEnabled = false;
foreach (DataRow dr in chunk.Rows)
{
var parcelToInsert = new Parcel();
foreach (DataColumn c in dr.Table.Columns)
{
var propertyInfo = parcelToInsert.GetType()
.GetProperty(
c.ColumnName,
BindingFlags.SetProperty | BindingFlags.IgnoreCase
| BindingFlags.Public | BindingFlags.Instance);
propertyInfo?.SetValue(
parcelToInsert,
TaxDataFunction.ChangeType(
dr[c.ColumnName],
propertyInfo.PropertyType),
null);
}
entities.Parcels.Add(parcelToInsert);
}
entities.SaveChanges();
}
catch (Exception ex)
{
TaxDataError.AddTaxApplicationLog(
TaxDataConstant.CategoryError,
ex.Source,
ex.Message,
ex.StackTrace);
throw;
}
finally
{
entities?.Dispose();
}
});
}
EDIT:
Here is the solution I ended up implementing:
private static readonly ConcurrentDictionary<SetterInfo, Action<object,object>> CachedSetters =
new ConcurrentDictionary<SetterInfo, Action<object, object>>();
private static readonly MethodInfo ChangeTypeMethod =
((Func<object, Type, object>) TaxDataFunction.ChangeType).Method;
private static void SetProperty(object obj, string name, object value)
{
if (obj == null)
return;
var key = new SetterInfo(obj.GetType(), name);
var setter = CachedSetters.GetOrAdd(key, CreateSetter);
setter(obj, value);
}
private static Action<object, object> CreateSetter(SetterInfo info)
{
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
return (s, v) => { };
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
return lambda.Compile();
}
private struct SetterInfo
{
public Type Type { get; }
public string Name { get; }
public SetterInfo(Type type, string name)
{
Type = type;
Name = name;
}
}
Expression trees allow you to build code dynamically at runtime instead of statically typing it in the IDE and using a compiler. They are well explained in the documentation.
Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.
static ConcurrentDictionary<string, Lazy<Action<object, object>>> CachedProperties =
new ConcurrentDictionary<string, Lazy<Action<object, object>>>();
static void SetProperty(object obj, string name, object value)
{
if(obj==null)
throw new ArgumentNullException("obj");
Type objType = obj.GetType();
string key = objType.FullName + ":" + name;
Action<object, object> f =
CachedProperties.GetOrAdd(key, k =>
new Lazy<Action<object,object>>(() => {
PropertyInfo prop = objType.GetProperty(name);
if(prop==null){
return (s,v) => {};
}
ParameterExpression pobj =
Expression.Parameter(typeof(object));
ParameterExpression pval =
Expression.Parameter(typeof(object));
Expression left = Expression.Property(
Expression.TypeAs( pobj, objType), prop);
Expression right = Expression.Convert(pval, prop.PropertyType);
return Expression
.Lambda<Action<object, object>>(
Expression.Assign(left,right), pobj, pval).Compile();
})).Value;
f(obj,value);
}
Usage....
SetProperty(parcelToInsert, c.ColumnName, dr[c.ColumnName])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With