2012-11-26 2 views
4

Я ищу способ переопределить метод/процесс SaveChanges в EF. Нам нужно каким-то образом захватить SQL, предотвратить выполнение обычного обновления/удаления/вставки и использовать этот сгенерированный SQL для выполнения нашей пользовательской процедуры.Переопределение EntityFramework SaveChanges - захватить SQL и вызвать пользовательский процесс

  • Позвонить SaveChanges() как обычно. Пусть EF генерирует SQL.
  • Получить SQL
  • Предотвратить, что SQL от выполняется нормальный путь
  • вызов пользовательских хранимых процедур (принимает дополнительные Params и т.д.)
  • Притворитесь, как мы выполнили SaveChanges (или просто возвращает 0)

Единственная реальная проблема, которую я вижу, - это захват SQL изнутри метода SaveChanges. Что мы будем делать что-то вроде этого, в идеале ...

  1. Get поставщика/подключение/и т.д.
  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

Знаете ли вы, что вы можете [сопоставлять красные действия с хранимыми процедурами] (http://weblogs.asp.net/zeeshanhirani/archive/2010/10/14/inserting-updating-and-deleting-entity -Использование сохраненного-procedures.aspx)? Вы можете использовать их для вызова своего центрального sproc. –

+0

@GertArnold Это похоже на то, что стоит опубликовать в качестве ответа. Я бы '+ 1', так или иначе ... –

+0

Я слышал об этом (не использовал его.) Стандарт (и это большой и немного интересный корпус) заключается в использовании этой ОДНОЙ хранимой процедуры, все их многочисленные базы данных. Нам даже не разрешают писать процедуры (и я бы не хотел писать 3 procs для ~ 70 таблиц, а затем вручную их сопоставлять, а также поддерживать их). Теперь, если EF может генерировать те! Вы должны добавить его в качестве ответа, даже если он может оказаться неприменимым для меня (корпоративные ограничения). –

ответ

2

Кажется, что это, возможно, можно получить с помощью SQL EF Tracing Provider ... увидеть последний пост в этом link

+0

Прохладный. Я фактически установил это (и плагин аудита), но пока не использовал его. Я не очень много играл с EF, обычно в других рамках. Если ничего, неплохо найти крючки и, возможно, отправную точку для большего количества настраиваемых вещей :) –

+0

Это дало мне хорошую отправную точку, поэтому я дам ему ответ. Как видно из решения, теперь есть точка для захвата и изменения команды. Я не могу/не изменял тип 'CommandTree', но, похоже, работает независимо от SQL Server. Все мои действия CUD теперь проходят через другую процедуру (и 'sp_executeSql', eww). –

+0

Рад, что это помогло. Поначалу кажется, что немного брючного решения нужно пройти через один SP, честно говоря, я завидую тому, что с системой, над которой я работаю в настоящий момент, которая использует SP, Scalar Functions, представления, триггеры, сильно типизированные наборы данных, встроенный sql и теперь EF4, то есть это беспорядок! –

2

Как я уже говорил в комментарий, вы можете map crud actions to stored procedures (ну, действия CUD). Да, требуется много работы, чтобы писать, сопоставлять и поддерживать эти sprocs, но возможная прибыль - это лучшая производительность и безопасность. Вот почему DBA так им нравится. Возможно, это и основная причина, почему эта центральная монолитная процедура в вашей базе данных была создана. Может быть способ использовать «CRO sprocs» для удовлетворения тех же требований.

+0

Да, это работа :) Я избегал этого в прошлый раз, используя инструмент, который делал все, включая запись хранимых процедур. У каждой области была своя модель, и только те поля, которые они специально использовали, и т. Д. Это было не так уж плохо, даже с огромной моделью. Конечно, общий материал был обычным явлением. –