Extensions for the SimpleExpressionToSQL class
public static class Extensions { private static readonly MethodInfo _deleteMethod; private static readonly MethodInfo _deleteMethodAsync; private static readonly MethodInfo _toSqlStringMethod; private static readonly MethodInfo _updateMethod; private static readonly MethodInfo _updateMethodAsync; static Extensions() { Type extensionType = typeof(Extensions); _deleteMethod = extensionType.GetMethod(nameof(Extensions.Delete), BindingFlags.Static | BindingFlags.Public); _updateMethod = extensionType.GetMethod(nameof(Extensions.Update), BindingFlags.Static | BindingFlags.Public); _deleteMethodAsync = extensionType.GetMethod(nameof(Extensions.DeleteAsync), BindingFlags.Static | BindingFlags.Public); _updateMethodAsync = extensionType.GetMethod(nameof(Extensions.Update), BindingFlags.Static | BindingFlags.Public); _toSqlStringMethod = extensionType.GetMethod(nameof(Extensions.ToSqlString), BindingFlags.Static | BindingFlags.Public); } public static bool CanConvertToSqlDbType(this Type type) => type.ToSqlDbTypeInternal().HasValue; public static int Delete<T>(this IQueryable<T> queryable) { var simpleExpressionToSQL = new SimpleExpressionToSQL(queryable.AppendCall(_deleteMethod)); return simpleExpressionToSQL.ExecuteNonQuery(); } public static async Task<int> DeleteAsync<T>(this IQueryable<T> queryable) { var simpleExpressionToSQL = new SimpleExpressionToSQL(queryable.AppendCall(_deleteMethodAsync)); return await simpleExpressionToSQL.ExecuteNonQueryAsync(); } public static string GetTableName<TEntity>(this DbSet<TEntity> dbSet) where TEntity : class { DbContext context = dbSet.GetService<ICurrentDbContext>().Context; IModel model = context.Model; IEntityType entityTypeOfFooBar = model .GetEntityTypes() .First(t => t.ClrType == typeof(TEntity)); IAnnotation tableNameAnnotation = entityTypeOfFooBar.GetAnnotation("Relational:TableName"); return tableNameAnnotation.Value.ToString(); } public static string GetTableName(this IQueryable query, Type entity) { QueryCompiler compiler = query.Provider.GetValueOfField<QueryCompiler>("_queryCompiler"); IModel model = compiler.GetValueOfField<IModel>("_model"); IEntityType entityTypeOfFooBar = model .GetEntityTypes() .First(t => t.ClrType == entity); IAnnotation tableNameAnnotation = entityTypeOfFooBar.GetAnnotation("Relational:TableName"); return tableNameAnnotation.Value.ToString(); } public static SqlDbType ToSqlDbType(this Type type) => type.ToSqlDbTypeInternal() ?? throw new InvalidCastException($"Unable to cast from '{type}' to '{typeof(DbType)}'."); public static string ToSqlString<T>(this IQueryable<T> queryable) => new SimpleExpressionToSQL(queryable.AppendCall(_toSqlStringMethod)); public static int Update<TSource, TResult>(this IQueryable<TSource> queryable, Expression<Func<TSource, TResult>> selector) { var simpleExpressionToSQL = new SimpleExpressionToSQL(queryable.AppendCall(_updateMethod, selector)); return simpleExpressionToSQL.ExecuteNonQuery(); } public static async Task<int> UpdateAsync<TSource, TResult>(this IQueryable<TSource> queryable, Expression<Func<TSource, TResult>> selector) { var simpleExpressionToSQL = new SimpleExpressionToSQL(queryable.AppendCall(_updateMethodAsync, selector)); return await simpleExpressionToSQL.ExecuteNonQueryAsync(); } internal static DbContext GetDbContext(this IQueryable query) { QueryCompiler compiler = query.Provider.GetValueOfField<QueryCompiler>("_queryCompiler"); RelationalQueryContextFactory queryContextFactory = compiler.GetValueOfField<RelationalQueryContextFactory>("_queryContextFactory"); QueryContextDependencies dependencies = queryContextFactory.GetValueOfField<QueryContextDependencies>("_dependencies"); return dependencies.CurrentContext.Context; } internal static string Join(this IEnumerable<string> values, string separator) => string.Join(separator, values); internal static bool RequiresQuotes(this SqlDbType sqlDbType) { switch (sqlDbType) { case SqlDbType.Char: case SqlDbType.Date: case SqlDbType.DateTime: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: case SqlDbType.NChar: case SqlDbType.NText: case SqlDbType.Time: case SqlDbType.SmallDateTime: case SqlDbType.Text: case SqlDbType.UniqueIdentifier: case SqlDbType.Timestamp: case SqlDbType.VarChar: case SqlDbType.Xml: case SqlDbType.Variant: case SqlDbType.NVarChar: return true; default: return false; } } internal static unsafe string ToCamelCase(this string value) { if (value == null || value.Length == 0) { return value; } string result = string.Copy(value); fixed (char* chr = result) { char valueChar = *chr; *chr = char.ToLowerInvariant(valueChar); } return result; } private static IQueryable<TResult> AppendCall<TSource, TResult>(this IQueryable<TSource> queryable, MethodInfo methodInfo, Expression<Func<TSource, TResult>> selector) { MethodInfo methodInfoGeneric = methodInfo.MakeGenericMethod(typeof(TSource), typeof(TResult)); MethodCallExpression methodCallExpression = Expression.Call(methodInfoGeneric, queryable.Expression, selector); return new EntityQueryable<TResult>(queryable.Provider as IAsyncQueryProvider, methodCallExpression); } private static IQueryable<T> AppendCall<T>(this IQueryable<T> queryable, MethodInfo methodInfo) { MethodInfo methodInfoGeneric = methodInfo.MakeGenericMethod(typeof(T)); MethodCallExpression methodCallExpression = Expression.Call(methodInfoGeneric, queryable.Expression); return new EntityQueryable<T>(queryable.Provider as IAsyncQueryProvider, methodCallExpression); } private static T GetValueOfField<T>(this object obj, string name) { FieldInfo field = obj .GetType() .GetField(name, BindingFlags.NonPublic | BindingFlags.Instance); return (T)field.GetValue(obj); } [SuppressMessage("Style", "IDE0011:Add braces", Justification = "Easier to read than with Allman braces")] private static SqlDbType? ToSqlDbTypeInternal(this Type type) { if (Nullable.GetUnderlyingType(type) is Type nullableType) return nullableType.ToSqlDbTypeInternal(); if (type.IsEnum) return Enum.GetUnderlyingType(type).ToSqlDbTypeInternal(); if (type == typeof(long)) /**/ return SqlDbType.BigInt; if (type == typeof(byte[])) /**/ return SqlDbType.VarBinary; if (type == typeof(bool)) /**/ return SqlDbType.Bit; if (type == typeof(string)) /**/ return SqlDbType.NVarChar; if (type == typeof(DateTime)) /**/ return SqlDbType.DateTime2; if (type == typeof(decimal)) /**/ return SqlDbType.Decimal; if (type == typeof(double)) /**/ return SqlDbType.Float; if (type == typeof(int)) /**/ return SqlDbType.Int; if (type == typeof(float)) /**/ return SqlDbType.Real; if (type == typeof(Guid)) /**/ return SqlDbType.UniqueIdentifier; if (type == typeof(short)) /**/ return SqlDbType.SmallInt; if (type == typeof(object)) /**/ return SqlDbType.Variant; if (type == typeof(DateTimeOffset)) /**/ return SqlDbType.DateTimeOffset; if (type == typeof(TimeSpan)) /**/ return SqlDbType.Time; if (type == typeof(byte)) /**/ return SqlDbType.TinyInt; return null; } }