2009-09-11 3 views
9

Мне было интересно, есть ли способ перехватить и изменить sql, сгенерированный из linq в Sql, до отправки запроса?Как перехватить и изменить SQL-запрос в Linq to SQL

В принципе, у нас есть слой записи безопасности, что дан запрос типа «SELECT * из записей» будет изменить запрос, чтобы быть чем-то вроде "выберите * из записей WHERE [somesecurityfilter]

Я пытаюсь чтобы найти лучший способ перехватить и изменить sql до его выполнения провайдером linq to sql.

+6

Есть ли какая-то конкретная причина, по которой фильтр должен быть реализован в точке Linq to SQL, генерирует SQL? Возможно, было бы проще, если бы фильтры были либо: а) реализованы через представления в вашей БД или б) путем моделирования модели объекта безопасности в вашем приложении и реализации фильтров в точке, где вы определяете выражения выражения linq? –

ответ

2

Если вы хотите перехватить SQL, сгенерированный L2S, и с этим поиграть, лучше всего создать классы-оболочки для SqlConnection, SqlCommand, DbProviderFactory и т. Д. Дайте обернутый экземпляр SqlConnection для перегрузки конструктора L2S datacontext, который принимает соединение db. В завернутом соединении вы можете заменить DbProviderFactory своим собственным производным DbProviderFactory классом, который возвращает завернутые версии SqlCommand и т. Д.

E.g.:

//sample wrapped SqlConnection: 
public class MySqlConnectionWrapper : SqlConnection 
{ 
    private SqlConnecction _sqlConn = null; 
    public MySqlConnectionWrapper(string connectString) 
    { 
    _sqlConn = new SqlConnection(connectString); 
    } 

    public override void Open() 
    { 
    _sqlConn.Open(); 
    } 

    //TODO: override everything else and pass on to _sqlConn... 

    protected override DbProviderFactory DbProviderFactory 
    { 
    //todo: return wrapped provider factory... 
    } 
} 

При использовании:

using (SomeDataContext dc = new SomeDataContext(new MySqlConnectionWrapper("connect strng")) 
{ 
    var q = from x in dc.SomeTable select x; 
    //...etc... 
} 

Это говорит, вы действительно хотите идти по этому пути? Вы должны будете иметь возможность анализировать SQL-запросы и запросы, созданные L2S, чтобы правильно их редактировать. Если вы можете изменить запросы linq, чтобы добавить все, что вы хотите добавить к ним, это, вероятно, лучшая альтернатива.

Помните, что запросы Linq являются составными, поэтому вы можете добавлять «дополнительные» в отдельный метод, если у вас есть что-то, что вы хотите добавить ко многим запросам.

+0

Это маршрут, по которому я спустился. К счастью, у нас уже есть SQL Parser! – mrwayne

+0

@mrwayne Я надеялся, что вы придете, чтобы выбрать ответ, который вам нужен. Спасибо, что приняли для меня жесткое решение; O) – Keng

+6

Как вам удалось наследовать от SqlConnection, когда он запечатан? Класс не будет компилироваться подобным образом. – chrishey

2

первая вещь приходит мне на ум, чтобы изменить запрос и возвращает результат в формате Non-LINQ

//Get linq-query as datatable-schema 
     public DataTable ToDataTable(System.Data.Linq.DataContext ctx, object query) 
     { 
      if (query == null) 
      { 
       throw new ArgumentNullException("query"); 
      } 

      IDbCommand cmd = ctx.GetCommand((IQueryable)query); 
      System.Data.SqlClient.SqlDataAdapter adapter = new System.Data.SqlClient.SqlDataAdapter(); 
      adapter.SelectCommand = (System.Data.SqlClient.SqlCommand)cmd; 
      DataTable dt = new DataTable("sd"); 

      try 
      { 
       cmd.Connection.Open(); 
       adapter.FillSchema(dt, SchemaType.Source); 
       adapter.Fill(dt); 
      } 
      finally 
      { 
       cmd.Connection.Close(); 
      } 
      return dt; 
     } 

попробовать добавить свое состояние на SelectCommand и посмотреть, если это помогает.

1

Попробуйте настроить представление в БД, которое при необходимости применяет фильтр безопасности к записям, а затем при извлечении записей через L2S. Это гарантирует, что требуемые записи не будут возвращены.

В качестве альтернативы добавьте запрос Where() перед его отправкой, в котором будет применен фильтр безопасности. Это позволит вам применять фильтр программно (в случае его изменения в зависимости от сценария).

3

Хорошо, сначала, чтобы ответить на ваш вопрос (но читайте для слов предостережения;)), есть способ, хотя и фиктивный, делать то, что вы хотите.

// IQueryable<Customer> L2S query definition, db is DataContext (AdventureWorks) 
var cs = from c in db.Customers 
     select c; 
// extract command and append your stuff 
DbCommand dbc = db.GetCommand(cs); 
dbc.CommandText += " WHERE MiddleName = 'M.'"; 
// modify command and execute letting data context map it to IEnumerable<T> 
var result = db.ExecuteQuery<Customer>(dbc.CommandText, new object[] { }); 

Теперь, оговорки.

  1. Вы должны знать, какой запрос сгенерирован, чтобы вы знали, как его модифицировать, это продлевает развитие.
  2. Он выпадает из рамок L2S и, таким образом, создает возможное отверстие для устойчивого развития, если кто-то модифицирует Linq, это повредит.
  3. Если Linq вызывает параметры (где имеет или другое расширение, вызывая WHERE раздел появляться с константами), что усложняет ситуацию, вам придется извлечь и передать эти параметры в ExecuteQuery

в целом, возможно, но очень хлопотно. При этом вам следует рассмотреть возможность использования расширения .Where() как Yaakov. Если вы хотите централизованно Controll безопасности на уровне объекта, используя этот подход, вы можете создать расширение для обработки ее для вас

static class MySecurityExtensions 
{ 
    public static IQueryable<Customer> ApplySecurity(this IQueryable<Customer> source) 
    { 
     return source.Where(x => x.MiddleName == "M."); 
    } 
} 

//... 
// now apply it to any Customer query 
var cs = (from c in db.Customers select c).ApplySecurity(); 

так, если вы изменяете ApplySecurity он будет автоматически применяться ко всем Linq запросов на объекте клиента.

+0

Нужно ли принудительно linq применять «applysecurity», где расширение для всех запросов перед их выполнением автоматически - поэтому с точки зрения разработчика они будут писать: var cs = (from c in db.Customers select c); , но до того, как запрос попадет в db, он применит условие безопасности where. – Mustafakidd

+0

Насколько мне известно, код построения дерева генерируется во время компиляции. – mmix

Смежные вопросы