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;
}
}