-
-
Notifications
You must be signed in to change notification settings - Fork 90
/
EfExtensions.cs
116 lines (109 loc) · 6.84 KB
/
EfExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace Paillave.EntityFrameworkCoreExtension.Core
{
public static class EfExtensions
{
#region Remove from ef core version 5
// see here: https://blog.oneunicorn.com/2020/01/12/toquerystring/ and https://github.com/dotnet/efcore/issues/6482
public static string ToQueryString<TEntity>(this IQueryable<TEntity> query) where TEntity : class
{
var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
var relationalCommandCache = enumerator.Private("_relationalCommandCache");
var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression");
var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory");
var sqlGenerator = factory.Create();
var command = sqlGenerator.GetCommand(selectExpression);
string sql = command.CommandText;
return sql;
}
private static object Private(this object obj, string privateField) => obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
private static T Private<T>(this object obj, string privateField) => (T)obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
#endregion
public static EntityEntry<TEntity> EntryWithoutDetectChanges<TEntity>(this DbContext context, TEntity entity)
where TEntity : class
{
var entryWithoutDetectChangesMethodInfo = context.GetType().GetMethod("EntryWithoutDetectChanges", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(TEntity) }, null);
return (EntityEntry<TEntity>)entryWithoutDetectChangesMethodInfo.Invoke(context, new object[] { entity });
}
private static Regex regex = new Regex(@"SELECT\s+(?<ref>[[]?.+?[]]?)[.].+?\sFROM", RegexOptions.Singleline & RegexOptions.IgnoreCase);
public static Task DeleteWhereAsync<T>(this DbSet<T> dbSet, Expression<Func<T, bool>> filter) where T : class
=> DeleteWhereAsync(dbSet, filter, CancellationToken.None);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Reviewed")]
public static async Task DeleteWhereAsync<T>(this DbSet<T> dbSet, Expression<Func<T, bool>> filter, CancellationToken cancellationToken) where T : class
{
var query = dbSet.Where(filter);
var enumerator = query.Provider.Execute<IEnumerable<T>>(query.Expression).GetEnumerator();
var relationalCommandCache = enumerator.Private("_relationalCommandCache");
var relationQueryContext = enumerator.Private<RelationalQueryContext>("_relationalQueryContext");
var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression");
var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory");
var sqlGenerator = factory.Create();
var command = sqlGenerator.GetCommand(selectExpression);
var parameterValues = relationQueryContext.ParameterValues;
var dbCtx = relationQueryContext.Context.Database.GetDbConnection();
var dbCommand = dbCtx.CreateCommand();
if (dbCtx.State == ConnectionState.Closed)
{
dbCtx.Open();
}
string deleteSql = regex.Replace(command.CommandText, "DELETE $1 FROM", 1);
dbCommand.CommandText = deleteSql;
dbCommand.CommandType = CommandType.Text;
dbCommand.CommandTimeout = 3000;
foreach (var parameter in command.Parameters)
parameter.AddDbParameter(dbCommand, parameterValues[parameter.InvariantName]);
await dbCommand.ExecuteNonQueryAsync(cancellationToken);
}
public static Expression<Func<T1, TResult>> ApplyPartialRight<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, Expression expressionValue)
{
var parameterToBeReplaced = expression.Parameters[1];
var visitor = new ReplacementVisitor(parameterToBeReplaced, expressionValue);
var newBody = visitor.Visit(expression.Body);
return Expression.Lambda<Func<T1, TResult>>(newBody, expression.Parameters[0]);
}
public static Expression<Func<T1, TResult>> ApplyPartialRight<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, T2 value)
{
var parameterToBeReplaced = expression.Parameters[1];
var constant = Expression.Constant(value, parameterToBeReplaced.Type);
return ApplyPartialRight(expression, constant);
}
public static Expression<Func<T2, TResult>> ApplyPartialLeft<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, Expression expressionValue)
{
var parameterToBeReplaced = expression.Parameters[0];
var visitor = new ReplacementVisitor(parameterToBeReplaced, expressionValue);
var newBody = visitor.Visit(expression.Body);
return Expression.Lambda<Func<T2, TResult>>(newBody, expression.Parameters[1]);
}
public static Expression<Func<T2, TResult>> ApplyPartialLeft<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, T1 value)
{
var parameterToBeReplaced = expression.Parameters[0];
var constant = Expression.Constant(value, parameterToBeReplaced.Type);
return ApplyPartialLeft(expression, constant);
}
public static Expression<Func<TResult>> ApplyPartial<T1, TResult>(this Expression<Func<T1, TResult>> expression, Expression expressionValue)
{
var parameterToBeReplaced = expression.Parameters[0];
var visitor = new ReplacementVisitor(parameterToBeReplaced, expressionValue);
var newBody = visitor.Visit(expression.Body);
return Expression.Lambda<Func<TResult>>(newBody, expression.Parameters[1]);
}
public static Expression<Func<TResult>> ApplyPartial<T1, TResult>(this Expression<Func<T1, TResult>> expression, T1 value)
{
var parameterToBeReplaced = expression.Parameters[0];
var constant = Expression.Constant(value, parameterToBeReplaced.Type);
return ApplyPartial(expression, constant);
}
}
}