我正在寻找一种方法来覆盖 EF 中的 SaveChanges 方法/进程。我们需要以某种方式获取 SQL,阻止执行正常的更新/删除/插入,并使用生成的 SQL 来运行我们的自定义过程。
- 正常打电话
SaveChanges()
。让 EF 生成 SQL。 - 获取 SQL
- 防止该 SQL 以正常方式执行
- 调用自定义存储过程(需要额外的参数等)
- 假装我们执行
SaveChanges
(或只是返回 0)
我看到的唯一真正的问题是从方法内部获取 SQL SaveChanges
。我们会做的是这样的事情,理想情况下......
- 获取提供者/连接/等
- 设置事件挂钩来处理这个
- 完成,没有代码更改/覆盖等。
我们正在使用 MVC4 和 EF5 对 3 个字母的首字母缩写词的数据库。这里的重点是避免在每个更新操作中手动编写 SQL,并依靠 EF 为我们生成所有这些。由于该过程采用直接 SQL
是的,这不是一个好方法(单一程序),但我们在这件事上别无选择。没有任何。如果我们不能这样做,那么我们将需要编写自定义 sql。也许我们可以通过另一种方式来执行此操作,即传递上下文并自己完成工作?然后我们可以审计'SaveChanges()'从未被调用:D
解决方案
我使用EFTracingProvider
作为起点来创建我自己的提供程序来执行此操作(以及其他一些事情)。您也可以只使用 EFTracingProvider 将所有内容放在您的 Entities 类中并处理事件。您不会看到修改后的 SQL,因为此事件将在它之后触发,因此您需要进行自己的日志记录。这已被剥离以更好地适应网站:)
public class MyEntities : MyBaseEntities
{
public MyEntities(): this(connectionString: "name=MyBaseEntities") {}
public MyEntities(string connectionString)
: base(MakeConnection(connectionString, "EFTracingProvider")) {}
/// <summary>
/// Insert the wrapped connection by calling the base toolkit.
private static EntityConnection MakeConnection(string connectionString, params string[] providers)
{
var conn = EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
providers
);
//get the tracing connection, so that we can attach event handlers
var us = conn.UnwrapConnection<EFTracingConnection>();
if (us != null)
{
us.CommandExecuting += BeforeExecute;
}
return conn;
}
private static void BeforeExecute(object sender, CommandExecutionEventArgs e)
{
// If an Create/Update/Delete action then we need to wrap it in our custom proc
if (IsCudAction(e.CommandTree))
{
var text = cmd.Parameters.Cast<DbParameter>().Aggregate(
cmd.CommandText,
(current, p) => current.Replace(p.ParameterName, SafeSql.Prepare(p.Value)));
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "[dbo].[ExecuteForMe]";
cmd.Parameters.Clear();
cmd.Parameters.AddRange(new[]
{
new SqlParameter("commandText", text),
new SqlParameter("extraInfo", "logging context")
});
}
}
public static bool IsCudAction(DbCommandTree commandTree)
{
if (commandTree is DbUpdateCommandTree) return true;
if (commandTree is DbDeleteCommandTree) return true;
if (commandTree is DbInsertCommandTree) return true;
if (commandTree is DbQueryCommandTree) return false;
if (commandTree is DbFunctionCommandTree) return false;
throw new InvalidOperationException("Unknown type of CommandTree: " + commandTree.GetType().Name);
}
}