refactor built query with less mem allocation.

This commit is contained in:
Фролов 2021-11-17 10:52:03 +05:00
parent db0b430d93
commit 361aceed96
2 changed files with 51 additions and 28 deletions

View File

@ -13,7 +13,7 @@ namespace AsbCloudDb
{ {
static Dictionary<Type, IQueryStringFactory> QueryFactories { get; set; } = new Dictionary<Type, IQueryStringFactory>(); static Dictionary<Type, IQueryStringFactory> QueryFactories { get; set; } = new Dictionary<Type, IQueryStringFactory>();
static IQueryStringFactory GetQueryStringFactory<T>(DbSet<T> dbset) static QueryStringFactory<T> GetQueryStringFactory<T>(DbSet<T> dbset)
where T : class where T : class
{ {
var t = typeof(T); var t = typeof(T);
@ -30,8 +30,9 @@ namespace AsbCloudDb
public static Task<int> ExecInsertOrUpdateAsync<T>(this Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade database, DbSet<T> dbset, IEnumerable<T> items, CancellationToken token) public static Task<int> ExecInsertOrUpdateAsync<T>(this Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade database, DbSet<T> dbset, IEnumerable<T> items, CancellationToken token)
where T : class where T : class
{ {
var factory = (QueryStringFactory<T>)GetQueryStringFactory(dbset); var factory = GetQueryStringFactory(dbset);
var query = factory.MakeInsertOrUpdateSql(items); var query = factory.MakeInsertOrUpdateSql(items);
return database.ExecuteSqlRawAsync(query, token); return database.ExecuteSqlRawAsync(query, token);
} }
} }
@ -41,10 +42,11 @@ namespace AsbCloudDb
class QueryStringFactory<T> : IQueryStringFactory class QueryStringFactory<T> : IQueryStringFactory
where T : class where T : class
{ {
private readonly string insertHeader;
private readonly string pk; private readonly string pk;
private readonly string tableName; private readonly string tableName;
private readonly string colunmsString; private readonly string conflictBody;
private readonly string conflictUpdateSet;
private readonly IEnumerable<IClrPropertyGetter> getters; private readonly IEnumerable<IClrPropertyGetter> getters;
public QueryStringFactory(DbSet<T> dbset) public QueryStringFactory(DbSet<T> dbset)
@ -56,8 +58,11 @@ namespace AsbCloudDb
tableName = dbset.EntityType.GetTableName(); tableName = dbset.EntityType.GetTableName();
getters = ps.Select(p => p.GetGetter()); getters = ps.Select(p => p.GetGetter());
var colNames = ps.Select(p => $"\"{p.GetColumnBaseName()}\""); var colNames = ps.Select(p => $"\"{p.GetColumnBaseName()}\"");
colunmsString = $"({string.Join(", ", colNames)})"; var colunmsString = $"({string.Join(", ", colNames)})";
conflictUpdateSet = string.Join(", ", colNames.Select(n => $"{n} = excluded.{n}"));
insertHeader = $"INSERT INTO {tableName} {colunmsString} VALUES ";
var excludedUpdateSet = string.Join(", ", colNames.Select(n => $"{n} = excluded.{n}"));
conflictBody = $" ON CONFLICT {pk} DO UPDATE SET {excludedUpdateSet};";
} }
public string MakeInsertOrUpdateSql(IEnumerable<T> items) public string MakeInsertOrUpdateSql(IEnumerable<T> items)
@ -69,37 +74,35 @@ namespace AsbCloudDb
SET column_1 = excluded.column_1, SET column_1 = excluded.column_1,
column_2 = excluded.column_2; column_2 = excluded.column_2;
*/ */
var sqlBuilder = new StringBuilder("INSERT INTO ", 7); var builder = new StringBuilder(insertHeader, 7);
sqlBuilder.Append(tableName); BuildRows(builder, items);
sqlBuilder.Append(colunmsString);
sqlBuilder.AppendLine(" VALUES ");
sqlBuilder.Append(MakeQueryValues(items));
sqlBuilder.AppendLine(" ON CONFLICT ");
if (string.IsNullOrEmpty(pk)) if (string.IsNullOrEmpty(pk))
{ builder.Append(" ON CONFLICT DO NOTHING;");
sqlBuilder.Append("DO NOTHING;");
}
else else
builder.AppendLine(conflictBody);
return builder.ToString();
}
private StringBuilder BuildRows(StringBuilder builder, IEnumerable<T> items)
{ {
sqlBuilder.Append(pk); var list = items.ToList();
sqlBuilder.Append(" DO UPDATE SET "); for (int i = 0; i < list.Count; i++)
sqlBuilder.AppendLine(conflictUpdateSet);
sqlBuilder.Append(';');
}
return sqlBuilder.ToString();
}
private string MakeQueryValues(IEnumerable<T> items)
{ {
var rows = items.Select(item => MakeRow(item)); if(i > 0)
return string.Join(",", rows); builder.Append(',');
BuildRow(builder, list[i]);
}
return builder;
} }
private string MakeRow(T item) private StringBuilder BuildRow(StringBuilder builder, T item)
{ {
var values = getters.Select(getter => FormatValue(getter.GetClrValue(item))); var values = getters.Select(getter => FormatValue(getter.GetClrValue(item)));
return $"({string.Join(",", values)})"; builder.Append('(');
builder.AppendJoin(",", values);
builder.Append(')');
return builder;
} }
private static string FormatValue(object v) private static string FormatValue(object v)

View File

@ -19,6 +19,26 @@ namespace ConsoleApp1
{ {
static void Main(/*string[] args*/) static void Main(/*string[] args*/)
{ {
//var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
// .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True")
// .Options;
//var context = new AsbCloudDbContext(options);
//var sett = context.Set<TelemetryDataSaub>();
//var r = context.Database.ExecInsertOrUpdateAsync(sett, new List<TelemetryDataSaub> {
// new TelemetryDataSaub
// {
// Date = DateTime.Now,
// IdTelemetry = 3,
// AxialLoad = 100.1f,
// },
// new TelemetryDataSaub
// {
// Date = DateTime.Now.AddSeconds(2),
// IdTelemetry = 3,
// AxialLoad = 100.2f,
// },
//}, System.Threading.CancellationToken.None).Result;
Console.ReadKey(); Console.ReadKey();
} }
} }