gpt4 book ai didi

entity-framework - 当抛出 EntitySqlException 时,我可以看到正在进行的 SQL,或者我可以覆盖 EntityCommandCompilationException 吗?

转载 作者:行者123 更新时间:2023-12-03 21:02:21 25 4
gpt4 key购买 nike

我在尝试在 Entity Framework 中执行窗口函数时遇到了一些麻烦。 (有问题的项目建立在 EF 上,但会从在 SQL Server 上计算 PERCENTILE_DISC 获得很多 yield 。)

我正在创建一个新的约定并将其添加到对象模型中,以便将对某个方法的调用转换为执行 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 的不同问题。我怎样才能完成这些?

最佳答案

我在一个项目中工作,该项目使用一个拦截器来注册慢查询。
下面,我放上拦截器的代码:

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;
}
}
}
要使用此拦截器,您可以编写如下代码:
using System.Data.Entity.Infrastructure.Interception;
DbInterception.Add(NinjectWebCommon.Kernel.Get<SqlQueryCommandInterceptor>());
就我而言,我将此代码放入我的网络应用程序启动:Global.asax.cs。我使用 Ninject 来获取拦截器的一个实例。
我的代码可以做的比你需要的更多......所以你可能需要阅读以更好地理解并适应你的需求。
我希望它能帮助你。

关于entity-framework - 当抛出 EntitySqlException 时,我可以看到正在进行的 SQL,或者我可以覆盖 EntityCommandCompilationException 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56535365/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com