5

我正在寻找一种方法来覆盖 EF 中的 SaveChanges 方法/进程。我们需要以某种方式获取 SQL,阻止执行正常的更新/删除/插入,并使用生成的 SQL 来运行我们的自定义过程。

  • 正常打电话SaveChanges()。让 EF 生成 SQL。
  • 获取 SQL
  • 防止该 SQL 以正常方式执行
  • 调用自定义存储过程(需要额外的参数等)
  • 假装我们执行SaveChanges(或只是返回 0)

我看到的唯一真正的问题是从方法内部获取 SQL SaveChanges。我们会做的是这样的事情,理想情况下......

  1. 获取提供者/连接/等
  2. 设置事件挂钩来处理这个
  3. 完成,没有代码更改/覆盖等。

我们正在使用 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);
    }
}
4

2 回答 2

2

似乎有可能使用EF Tracing Provider获取 SQL ...请参阅此链接中的最后一篇文章

于 2012-11-26T09:25:08.620 回答
2

正如我在评论中所说,您可以将 crud 操作映射到存储过程(嗯,CUD 操作)。是的,编写、映射和维护这些存储过程需要大量工作,但可能的收益是更好的性能和安全性。这就是 DBA 如此喜欢它们的原因。这甚至可能是在您的数据库中创建这个中央整体过程的主要原因。可能有一种方法可以使用“CUD sprocs”来满足相同的要求。

于 2012-11-26T21:34:12.887 回答