我尝试在Entity Framework中执行窗口功能时遇到了麻烦。 (有问题的项目是基于EF构建的,但是通过在SQL Server上计算PERCENTILE_DISC会获得很多好处。)
我正在创建一个新的Convention,并将其添加到对象模型中,以将对某个方法的调用转换为执行PERCENT_DISC窗口函数的SQL。
创建的EdmFunction的CommandText为:
PERCENTILE_DISC (0.8) WITHIN GROUP (ORDER BY o.ExpectedPayment) OVER (PARTITION BY o.OrderType)
但是当我执行此功能时:
var medians = context.Set<UserLocation>().Select(x => CustomFunction.Percentile()).ToList();
这会引发EntityCommandCompilationException并显示以下消息:
System.Data.Entity.Core.EntityCommandCompilationException:
'An error occurred while preparing definition of the function 'ConsoleApp5.Percentile'. See the inner exception for details.'
Inner Exception:
EntitySqlException: The query syntax is not valid. Near identifier 'WITHIN', line 1, column 35.
但是此直接查询获得了预期的结果:
var p80s = context.Database.SqlQuery<decimal>("SELECT DISTINCT PERCENTILE_DISC (0.8) WITHIN GROUP (ORDER BY o.ExpectedPayment) OVER (PARTITION BY o.OrderType) from Orders o").ToList();
我怀疑这是由于未构建EF解析器来处理窗口功能。因此,我希望能够重写EntityCommandCompilationException并使EF尝试以任何方式执行查询。失败了,我至少希望看到到目前为止生成的SQL,看看是否存在导致真正无效的SQL的其他问题。我该怎么做?
答案 0 :(得分:0)
我在一个使用一个拦截器注册慢查询的项目中工作。 下面,我放置了拦截器的代码:
class Player:
def __init__(self, name, tank_name):
self.name = name
self.tank_name = tank_name
def new_player():
name = input('What is your name >>> ')
tank_name = input('What is your tanks name >>> ')
p1 = Player(name, tank_name)
new_player()
要使用此拦截器,您可以编写如下代码:
using System;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Nash.Data.Interceptors.SlowQuery
{
public sealed class SqlQueryCommandInterceptor : IDbCommandInterceptor
{
private readonly ISlowQueryLogger _slowQueryLogger;
public SqlQueryCommandInterceptor(ISlowQueryLogger slowQueryLogger)
{
_slowQueryLogger = slowQueryLogger;
}
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
=> interceptionContext.UserState = Stopwatch.StartNew();
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
=> LogSlowQuery(interceptionContext, command);
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
=> interceptionContext.UserState = Stopwatch.StartNew();
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
=> LogSlowQuery(interceptionContext, command);
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
=> interceptionContext.UserState = Stopwatch.StartNew();
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
=> LogSlowQuery(interceptionContext, command);
private void LogSlowQuery<T>(DbCommandInterceptionContext<T> interceptionContext, DbCommand dbCommand)
{
var debugText = GetDbCommandDebugText(dbCommand);
var userState = (Stopwatch)interceptionContext.UserState;
userState.Stop();
var elapsed = userState.Elapsed;
if (elapsed > TimeSpan.FromSeconds(2.6))
_slowQueryLogger.LogSlowQuery(debugText, elapsed);
}
private static string GetDbCommandDebugText(DbCommand dbCommand)
{
var debugText = dbCommand.CommandText;
if (dbCommand is SqlCommand && debugText.Contains("@"))
{
var matches = Regex.Matches(debugText, @"(\@[\w\.]+)").Cast<Match>().ToArray();
var paramDict = dbCommand.Parameters.Cast<SqlParameter>()
.Select(x => new
{
ParameterName = x.ParameterName.StartsWith("@") ? x.ParameterName : "@" + x.ParameterName,
Value = x.Value,
})
.ToDictionary(x => x.ParameterName, x => x.Value);
var buffer = new StringBuilder();
var i = 0;
foreach (var m in matches)
{
if (m.Index > i)
{
buffer.Append(debugText.Substring(i, m.Index - i));
i = m.Index;
}
var paramName = m.Groups[1].Value;
if (paramDict.TryGetValue(paramName, out var paramVal))
if (paramVal == null || DBNull.Value.Equals(paramVal))
buffer.Append($"NULL");
else
buffer.Append($"'{paramVal}'");
else
buffer.Append(paramName);
i += m.Length;
}
if (i < debugText.Length)
buffer.Append(debugText.Substring(i, debugText.Length - i));
debugText = buffer.ToString();
}
return debugText;
}
}
}
就我而言,我将此代码放入Web应用程序启动程序:Global.asax.cs。我使用Ninject来获取拦截器的一个实例。
我的代码可以做比您需要的更多的工作...因此您可能需要阅读以更好地理解并使其适应您的需求。
我希望它能为您提供帮助。