From 6cfd875a097ecf042fd52c025ba32a6bc24644d5 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 12 May 2026 13:13:32 -0700 Subject: [PATCH 1/2] Optimize `SqlDml.Literal()/.Array()` by using `ConstructorInvoker` --- Orm/Xtensive.Orm/Sql/SqlDml.cs | 49 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index 2ae028466f..c02642d7a2 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -2,16 +2,11 @@ // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.Concurrent; using System.Reflection; -using Xtensive.Collections; -using Xtensive.Core; using Xtensive.Reflection; using Xtensive.Sql.Dml; using Xtensive.Sql.Model; -using System.Linq; namespace Xtensive.Sql { @@ -20,16 +15,15 @@ namespace Xtensive.Sql /// public static class SqlDml { - private static readonly Type - SqlArrayOfTType = typeof(SqlArray<>), - SqlLiteralOfTType = typeof(SqlLiteral<>); - public static readonly SqlDefaultValue DefaultValue = new SqlDefaultValue(); public static readonly SqlNull Null = new SqlNull(); public static readonly SqlBreak Break = new SqlBreak(); public static readonly SqlContinue Continue = new SqlContinue(); public static readonly SqlNative Asterisk = Native("*"); + private static readonly ConcurrentDictionary LiteralConstructorByType = new(); + private static readonly ConcurrentDictionary ArrayConstructorByType = new(); + #region Aggregates public static SqlAggregate Count() @@ -144,6 +138,12 @@ public static SqlBinary Modulo(SqlExpression left, SqlExpression right) #region Array + private static ConstructorInvoker GetArrayConstructorInvoker(Type type) => + ArrayConstructorByType.GetOrAdd(type, static t => + ConstructorInvoker.Create(typeof(SqlArray<>).CachedMakeGenericType(t) + .GetConstructor(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, [typeof(object[])], null)!) + ); + public static SqlArray Array(IEnumerable values) { var valueList = values.ToArray(); @@ -154,12 +154,7 @@ public static SqlArray Array(IEnumerable values) if (!itemType.IsAssignableFrom(t)) throw new ArgumentException(Strings.ExTypesOfValuesAreDifferent); } - var resultType = SqlArrayOfTType.CachedMakeGenericType(itemType); - var result = Activator.CreateInstance( - resultType, - BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, - null, new object[] {valueList}, null); - return (SqlArray) result; + return (SqlArray) GetArrayConstructorInvoker(itemType).Invoke([valueList]); } public static SqlArray Array(object[] values) @@ -172,12 +167,7 @@ public static SqlArray Array(object[] values) throw new ArgumentException(Strings.ExTypesOfValuesAreDifferent); } - var resultType = SqlArrayOfTType.CachedMakeGenericType(itemType); - var result = Activator.CreateInstance( - resultType, - BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, - null, new object[] { values }, null); - return (SqlArray) result; + return (SqlArray) GetArrayConstructorInvoker(itemType).Invoke([values]); } public static SqlArray Array(params bool[] value) @@ -1175,16 +1165,11 @@ public static SqlLiteral Literal(Guid value) return new SqlLiteral(value); } - public static SqlLiteral Literal(object value) - { - var valueType = value.GetType(); - var resultType = SqlLiteralOfTType.CachedMakeGenericType(valueType); - var result = Activator.CreateInstance( - resultType, - BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, - null, new[] {value}, null); - return (SqlLiteral) result; - } + public static SqlLiteral Literal(object value) => + (SqlLiteral) LiteralConstructorByType.GetOrAdd(value.GetType(), static t => + ConstructorInvoker.Create(typeof(SqlLiteral<>).CachedMakeGenericType(t) + .GetConstructor(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, [t], null)!) + ).Invoke(value); public static SqlExpression LiteralOrContainer(object value) { From 00770839ba0709a50dc02a660e700a09c6d61433 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 12 May 2026 13:35:26 -0700 Subject: [PATCH 2/2] Simplify --- Orm/Xtensive.Orm/Sql/SqlDml.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Orm/Xtensive.Orm/Sql/SqlDml.cs b/Orm/Xtensive.Orm/Sql/SqlDml.cs index c02642d7a2..038338d443 100644 --- a/Orm/Xtensive.Orm/Sql/SqlDml.cs +++ b/Orm/Xtensive.Orm/Sql/SqlDml.cs @@ -138,11 +138,11 @@ public static SqlBinary Modulo(SqlExpression left, SqlExpression right) #region Array - private static ConstructorInvoker GetArrayConstructorInvoker(Type type) => - ArrayConstructorByType.GetOrAdd(type, static t => + private static SqlArray ConstructSqlArrayOfType(Type type, object[] values) => + (SqlArray) ArrayConstructorByType.GetOrAdd(type, static t => ConstructorInvoker.Create(typeof(SqlArray<>).CachedMakeGenericType(t) .GetConstructor(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, [typeof(object[])], null)!) - ); + ).Invoke([values]); public static SqlArray Array(IEnumerable values) { @@ -154,7 +154,7 @@ public static SqlArray Array(IEnumerable values) if (!itemType.IsAssignableFrom(t)) throw new ArgumentException(Strings.ExTypesOfValuesAreDifferent); } - return (SqlArray) GetArrayConstructorInvoker(itemType).Invoke([valueList]); + return ConstructSqlArrayOfType(itemType, valueList); } public static SqlArray Array(object[] values) @@ -167,7 +167,7 @@ public static SqlArray Array(object[] values) throw new ArgumentException(Strings.ExTypesOfValuesAreDifferent); } - return (SqlArray) GetArrayConstructorInvoker(itemType).Invoke([values]); + return ConstructSqlArrayOfType(itemType, values); } public static SqlArray Array(params bool[] value)