Skip to content

Commit

Permalink
Made property expression compilation lazy in property profile
Browse files Browse the repository at this point in the history
  • Loading branch information
Basim108 committed Mar 8, 2021
1 parent b6175f5 commit 3c75461
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 103 deletions.
47 changes: 47 additions & 0 deletions src/Hrimsoft.SqlBulk.PostgreSql/Extensions/LongExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Hrimsoft.SqlBulk.PostgreSql
{
public static class LongExtensions
{
/// <summary>
/// Prettify size in bytes to the closest readable Kb or Mb or Gb, etc
/// </summary>
/// <param name="sizeInBytes"></param>
/// <returns>Returns divided size and suffix Kb, Mb, etc</returns>
public static (float, string) PrettifySize(this long sizeInBytes)
{
var suffix = "bytes";
var size = sizeInBytes;
float castedSize = 0;
if (size > 1024)
{
castedSize = size / 1024f;
suffix = "Kb";
}
else
return (size, suffix);

if (castedSize > 1024)
{
castedSize = castedSize / 1024f;
suffix = "Mb";
}
else
return (castedSize, suffix);

if (castedSize > 1024)
{
castedSize = castedSize / 1024f;
suffix = "Gb";
}
else
return (castedSize, suffix);

if (castedSize > 1024)
{
castedSize = castedSize / 1024;
suffix = "Tb";
}
return (castedSize, suffix);
}
}
}
203 changes: 117 additions & 86 deletions src/Hrimsoft.SqlBulk.PostgreSql/Extensions/PropertyProfileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ public static bool IsDynamicallyInvoked(this PropertyProfile profile)
}

/// <summary> Calculates property values of item </summary>
/// <param name="item">item with values</param>
/// <param name="profile">information about which property it is needed to get value</param>
/// <param name="item">item with values</param>
/// <typeparam name="TEntity">Type of entity</typeparam>
/// <typeparam name="TResult">Type of property</typeparam>
/// <returns>
/// Returns string representation of the value.
/// For nullable value returns "null" string.
Expand All @@ -58,91 +57,84 @@ public static string GetPropertyValueAsString<TEntity>(this PropertyProfile prof
propertyName = operand.Member.Name;
else
return null;
try
{
var propInfo = typeof(TEntity).GetProperty(propertyName);
if (propInfo == null)
throw new ArgumentException($"Entity: {typeof(TEntity).FullName} doesn't have property: '{propertyName}'");
switch (profile.DbColumnType)
{
case NpgsqlDbType.Bigint:
if (profile.IsNullable)
{
var longNullableGetter = (Func<TEntity, long?>) Delegate.CreateDelegate(typeof(Func<TEntity, long?>), propInfo.GetGetMethod());
var longNullableValue = longNullableGetter(item);
return longNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var longGetter = (Func<TEntity, long>) Delegate.CreateDelegate(typeof(Func<TEntity, long>), propInfo.GetGetMethod());
var longValue = longGetter(item);
return longValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Integer:
if (profile.IsNullable)
{
var intNullableGetter = (Func<TEntity, int?>) Delegate.CreateDelegate(typeof(Func<TEntity, int?>), propInfo.GetGetMethod());
var intNullableValue = intNullableGetter(item);
return intNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var intGetter = (Func<TEntity, int>) Delegate.CreateDelegate(typeof(Func<TEntity, int>), propInfo.GetGetMethod());
var intValue = intGetter(item);
return intValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Real:
if (profile.IsNullable)
{
var floatNullableGetter = (Func<TEntity, float?>) Delegate.CreateDelegate(typeof(Func<TEntity, float?>), propInfo.GetGetMethod());
var floatNullableValue = floatNullableGetter(item);
return floatNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var floatGetter = (Func<TEntity, float>) Delegate.CreateDelegate(typeof(Func<TEntity, float>), propInfo.GetGetMethod());
var floatValue = floatGetter(item);
return floatValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Double:
if (profile.IsNullable)
{
var doubleNullableGetter = (Func<TEntity, double?>) Delegate.CreateDelegate(typeof(Func<TEntity, double?>), propInfo.GetGetMethod());
var doubleNullableValue = doubleNullableGetter(item);
return doubleNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var doubleGetter = (Func<TEntity, double>) Delegate.CreateDelegate(typeof(Func<TEntity, double>), propInfo.GetGetMethod());
var doubleValue = doubleGetter(item);
return doubleValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Numeric:
if (profile.IsNullable)
{
var decimalNullableGetter = (Func<TEntity, decimal?>) Delegate.CreateDelegate(typeof(Func<TEntity, decimal?>), propInfo.GetGetMethod());
var decimalNullableValue = decimalNullableGetter(item);
return decimalNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var decimalGetter = (Func<TEntity, decimal>) Delegate.CreateDelegate(typeof(Func<TEntity, decimal>), propInfo.GetGetMethod());
var decimalValue = decimalGetter(item);
return decimalValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Boolean:
if (profile.IsNullable)
{
var boolNullableGetter = (Func<TEntity, bool?>) Delegate.CreateDelegate(typeof(Func<TEntity, bool?>), propInfo.GetGetMethod());
var boolNullableValue = boolNullableGetter(item);
return boolNullableValue?.ToString(CultureInfo.InvariantCulture).ToLowerInvariant() ?? "null";
}
var boolGetter = (Func<TEntity, bool>) Delegate.CreateDelegate(typeof(Func<TEntity, bool>), propInfo.GetGetMethod());
var boolValue = boolGetter(item);
return boolValue.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
case NpgsqlDbType.Smallint:
if (profile.IsNullable)
{
var shortNullableGetter = (Func<TEntity, short?>) Delegate.CreateDelegate(typeof(Func<TEntity, short?>), propInfo.GetGetMethod());
var shortNullableValue = shortNullableGetter(item);
return shortNullableValue?.ToString(CultureInfo.InvariantCulture).ToLowerInvariant() ?? "null";
}
var shortGetter = (Func<TEntity, short>) Delegate.CreateDelegate(typeof(Func<TEntity, short>), propInfo.GetGetMethod());
var shortValue = shortGetter(item);
return shortValue.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
}
return null;
}
catch (Exception ex)

var propInfo = typeof(TEntity).GetProperty(propertyName);
if (propInfo == null)
throw new ArgumentException($"Entity: {typeof(TEntity).FullName} doesn't have property: '{propertyName}'");
switch (profile.DbColumnType)
{
var message = $"an error occurred while calculating '{propertyName}' property of {typeof(TEntity).FullName} entity";
throw new SqlGenerationException(SqlOperation.Insert, message, ex);
case NpgsqlDbType.Bigint:
if (profile.IsNullable)
{
var longNullableGetter = (Func<TEntity, long?>) Delegate.CreateDelegate(typeof(Func<TEntity, long?>), propInfo.GetGetMethod());
var longNullableValue = longNullableGetter(item);
return longNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var longGetter = (Func<TEntity, long>) Delegate.CreateDelegate(typeof(Func<TEntity, long>), propInfo.GetGetMethod());
var longValue = longGetter(item);
return longValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Integer:
if (profile.IsNullable)
{
var intNullableGetter = (Func<TEntity, int?>) Delegate.CreateDelegate(typeof(Func<TEntity, int?>), propInfo.GetGetMethod());
var intNullableValue = intNullableGetter(item);
return intNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var intGetter = (Func<TEntity, int>) Delegate.CreateDelegate(typeof(Func<TEntity, int>), propInfo.GetGetMethod());
var intValue = intGetter(item);
return intValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Real:
if (profile.IsNullable)
{
var floatNullableGetter = (Func<TEntity, float?>) Delegate.CreateDelegate(typeof(Func<TEntity, float?>), propInfo.GetGetMethod());
var floatNullableValue = floatNullableGetter(item);
return floatNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var floatGetter = (Func<TEntity, float>) Delegate.CreateDelegate(typeof(Func<TEntity, float>), propInfo.GetGetMethod());
var floatValue = floatGetter(item);
return floatValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Double:
if (profile.IsNullable)
{
var doubleNullableGetter = (Func<TEntity, double?>) Delegate.CreateDelegate(typeof(Func<TEntity, double?>), propInfo.GetGetMethod());
var doubleNullableValue = doubleNullableGetter(item);
return doubleNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var doubleGetter = (Func<TEntity, double>) Delegate.CreateDelegate(typeof(Func<TEntity, double>), propInfo.GetGetMethod());
var doubleValue = doubleGetter(item);
return doubleValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Numeric:
if (profile.IsNullable)
{
var decimalNullableGetter = (Func<TEntity, decimal?>) Delegate.CreateDelegate(typeof(Func<TEntity, decimal?>), propInfo.GetGetMethod());
var decimalNullableValue = decimalNullableGetter(item);
return decimalNullableValue?.ToString(CultureInfo.InvariantCulture) ?? "null";
}
var decimalGetter = (Func<TEntity, decimal>) Delegate.CreateDelegate(typeof(Func<TEntity, decimal>), propInfo.GetGetMethod());
var decimalValue = decimalGetter(item);
return decimalValue.ToString(CultureInfo.InvariantCulture);
case NpgsqlDbType.Boolean:
if (profile.IsNullable)
{
var boolNullableGetter = (Func<TEntity, bool?>) Delegate.CreateDelegate(typeof(Func<TEntity, bool?>), propInfo.GetGetMethod());
var boolNullableValue = boolNullableGetter(item);
return boolNullableValue?.ToString(CultureInfo.InvariantCulture).ToLowerInvariant() ?? "null";
}
var boolGetter = (Func<TEntity, bool>) Delegate.CreateDelegate(typeof(Func<TEntity, bool>), propInfo.GetGetMethod());
var boolValue = boolGetter(item);
return boolValue.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
case NpgsqlDbType.Smallint:
if (profile.IsNullable)
{
var shortNullableGetter = (Func<TEntity, short?>) Delegate.CreateDelegate(typeof(Func<TEntity, short?>), propInfo.GetGetMethod());
var shortNullableValue = shortNullableGetter(item);
return shortNullableValue?.ToString(CultureInfo.InvariantCulture).ToLowerInvariant() ?? "null";
}
var shortGetter = (Func<TEntity, short>) Delegate.CreateDelegate(typeof(Func<TEntity, short>), propInfo.GetGetMethod());
var shortValue = shortGetter(item);
return shortValue.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
}
return null;
}


Expand All @@ -159,7 +151,46 @@ public static object GetPropertyValue<TEntity>(this PropertyProfile profile, TEn
if (profile == null)
throw new ArgumentNullException(nameof(profile));

var value = profile.PropertyExpression.Compile().DynamicInvoke(item);
var propertyName = "";

if (profile.PropertyExpression.Body is MemberExpression memberExpression)
propertyName = memberExpression.Member.Name;
else if (profile.PropertyExpression.Body is ParameterExpression parameterExpression)
propertyName = parameterExpression.Name;
else if (profile.PropertyExpression.Body is UnaryExpression unaryExpression &&
unaryExpression.Operand is MemberExpression operand)
propertyName = operand.Member.Name;
if (!string.IsNullOrEmpty(propertyName))
{
var propInfo = typeof(TEntity).GetProperty(propertyName);
if (propInfo == null)
throw new ArgumentException($"Entity: {typeof(TEntity).FullName} doesn't have property: '{propertyName}'");

switch (profile.DbColumnType)
{
case NpgsqlDbType.Text:
case NpgsqlDbType.Varchar:
var strGetter = (Func<TEntity, string>) Delegate.CreateDelegate(typeof(Func<TEntity, string>), propInfo.GetGetMethod());
return strGetter(item);
case NpgsqlDbType.Timestamp:
if (profile.IsNullable)
{
var dateTimeNullableGetter = (Func<TEntity, DateTime?>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTime?>), propInfo.GetGetMethod());
return dateTimeNullableGetter(item);
}
var dateTimeGetter = (Func<TEntity, DateTime>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTime>), propInfo.GetGetMethod());
return dateTimeGetter(item);
case NpgsqlDbType.TimestampTz:
if (profile.IsNullable)
{
var dateTimeOffsetNullableGetter = (Func<TEntity, DateTimeOffset?>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTimeOffset?>), propInfo.GetGetMethod());
return dateTimeOffsetNullableGetter(item);
}
var dateTimeOffsetGetter = (Func<TEntity, DateTimeOffset>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTimeOffset>), propInfo.GetGetMethod());
return dateTimeOffsetGetter(item);
}
}
var value = profile.PropertyExpressionCompiled.DynamicInvoke(item);
if (value != null && value.GetType().IsEnum)
{
switch (profile.DbColumnType)
Expand Down
7 changes: 7 additions & 0 deletions src/Hrimsoft.SqlBulk.PostgreSql/Options/PropertyProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ public PropertyProfile(string column, LambdaExpression propertyExpression)
/// </summary>
public LambdaExpression PropertyExpression { get; }

private Delegate _compiledPropertyExpression = null;
/// <summary>
/// Compiled expression to access a property
/// </summary>
public Delegate PropertyExpressionCompiled => _compiledPropertyExpression
?? (_compiledPropertyExpression = this.PropertyExpression.Compile());

/// <summary>
/// Column name in database table
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,20 +338,8 @@ public async Task ExecutePortionAsync<TEntity>(
if (sw != null)
{
sw.Stop();
var cmdSize = (float)generatedCommands.Max(x => x.Command.Length) * 2;
var suffix = "bytes";
if (cmdSize > 1024)
{
cmdSize = cmdSize / 1024;
suffix = "Kb";
}
if (cmdSize > 1024)
{
cmdSize = cmdSize / 1024;
suffix = "Mb";
}
_logger.LogInformation(
$"Generated {generatedCommands.Count} sql {operation.ToString().ToLowerInvariant()} commands for {elements.Count} {entityProfile.EntityType.Name} elements in {sw.ElapsedMilliseconds / 1000f} ms, max command size {cmdSize:F2} {suffix}");
$"Generated {generatedCommands.Count} sql {operation.ToString().ToLowerInvariant()} commands for {elements.Count} {entityProfile.EntityType.Name} elements in {sw.ElapsedMilliseconds / 1000f} seconds");
sw = Stopwatch.StartNew();
}
foreach (var commandResult in generatedCommands)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
Parameters = parameters,
IsThereReturningClause = false
});
if (_logger.IsEnabled(LogLevel.Information))
{
var (cmdSize, suffix) = ((long)commandBuilder.Length * 2).PrettifySize();
_logger.LogInformation($"Generated sql simple delete command for {elementIndex} {entityProfile.EntityType.Name} elements, command size {cmdSize:F2} {suffix}");
}
commandBuilder.Clear();
parameters.Clear();
}
Expand Down
Loading

0 comments on commit 3c75461

Please sign in to comment.