我正在寻找一种方法来覆盖EF中的SaveChanges方法/进程。我们需要以某种方式获取SQL,防止正常的更新/删除/插入执行,并使用生成的SQL运行我们的自定义过程。
- 像往常一样调用
SaveChanges()
。让EF生成SQL。 - 获取SQL
- 防止以正常方式执行该SQL
- 调用自定义存储过程(带有其他参数等)
- 假装我们执行了
SaveChanges
(或只返回0)
我唯一真正看到的问题是从 SaveChanges
方法内部获取SQL。理想情况下,我们将采取以下措施...
- 获取提供程序/连接/等
- 设置事件钩子以处理此问题
- 完成,无需更改/覆盖等
我们正在使用MVC4&EF5针对三个字母缩写的数据库。这里的重点是避免在每个更新操作中手动编写SQL,并依赖于EF为我们生成所有内容。由于该过程需要直接SQL
是的,这不是一个好方法(单个过程),但我们在这件事上没有选择。根本没有选择。如果我们不能这样做,那么我们将需要编写自定义SQL。也许还有另一种方法可以强制执行此操作,在其中传递上下文并自己完成工作?然后我们只需审计永远不调用'SaveChanges()' :D
解决方案
我使用EFTracingProvider作为创建自己的提供程序的起点,该提供程序执行此操作(以及其他操作)。您也可以仅使用EFTracingProvider将所有内容放置在实体类中并处理事件。您将看不到修改后的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);
}
}