Как реализовать перехват запросов в запросе LINQ to Entities? (C #) - PullRequest
2 голосов
/ 09 марта 2011

Я пытаюсь реализовать зашифрованные столбцы в EF4 и использовать функции CTP5, чтобы позволить простое использование POCO для запросов к базе данных.Извините, что это много слов, но я надеюсь, что ниже приведено достаточно, чтобы объяснить необходимость и проблему!

Итак, немного предыстории, и мой прогресс до сих пор:

Намерениев том, что если вы запрашиваете таблицы без использования нашего DAL, то данные являются мусором, но я не хочу, чтобы разработчики беспокоились о том, когда / когда / как данные будут зашифрованы.

Для простоты на данном этапеЯ работаю в предположении, что любой строковый столбец будет зашифрован.

Теперь я успешно реализовал это для возврата данных с использованием события Objectmaterialized и для фиксации данных с помощью события SavingChanges.

Итак, учитывая следующий класс:

public class Thing
{

    public int ID { get; set; }
    [Required]
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public string OtherString { get; set; }
}

Приведенный ниже запрос возвращает все необходимые значения, и материализованный POCO содержит четкие данные.

var things = from t in myDbContext.Things
             select t;

, где myDbContext.Things является DbSet<Thing>

Аналогично, передача экземпляра Thing в Things.Add()
(с чистыми строковыми данными в значениях Name и / или OtherString)
и затем calling myDbContext.SaveChanges() шифрует строки перед тем, как попасть в хранилище данных.

Теперь проблема, с которой я столкнулся, заключается в следующем запросе:

var things = from t in myDbContext.Things
             where t.Name == "Hairbrush"
             select t;

Это приводит к тому, что незашифрованное значение сравнивается с зашифрованным значением в БД.Очевидно, я не хочу получать все записи из базы данных, материализовать их, а затем фильтровать результаты на основе любого предоставленного предложения Where ... так что мне нужно сделать: перехватить этот запрос и переписать его с помощьюшифрование любых строк в предложении Where. Итак, я рассмотрел:

  • написание поставщика запросов, но это не похоже на правильное решение ... (isэто?)
  • написание собственной оболочки IQueryable для DbSet, которая будет захватывать выражение, запускать его с помощью посетителя дерева выражений и затем пересылать новое выражение в DbSet ...

Попытки и того и другого оставили меня несколько потерянными!Я предпочитаю второе решение, которое мне кажется, так как оно выглядит немного аккуратнее и, вероятно, будет понятнее другим разработчикам в будущем.Но я рад, что выбрал один или другой вариант лучше !!

Главное, с чем я борюсь, это когда / как выражение LINQ применяется к объекту ... Iмне кажется, я немного запутался в том, где выполняется выражение в объекте IQueryable, поэтому я не уверен, какой метод мне нужно реализовать в моей оболочке, чтобы затем захватывать и манипулировать выражением, передаваемым в ...

Я уверен, что мне здесь не хватает чего-то достаточно очевидного, и я жду того момента с лампочкой ... но он не наступит !!

Любая помощь будет с благодарностью получена!

Ответы [ 3 ]

2 голосов
/ 24 марта 2011

Думаю, я дам вам знать, каким было мое окончательное решение.В конце концов я перешел к классу-оболочке, который реализует метод Where, но не вдаваясь в подробности реализации IQueryable полностью.LINQ все равно будет выполняться для класса (по крайней мере, в той степени, в которой я этого хочу / нуждаюсь) и вызовет метод Where с выражением из LINQ.

Затем я обхожу это ExpressionTree и заменяю мои строки зашифрованными значениями перед пересылкой нового дерева выражений во внутренний DbSet.и затем возвращает результат.

Это довольно грубо, и имеет свои ограничения, но работает для наших конкретных обстоятельств без проблем.

Спасибо, Бен

0 голосов
/ 14 июля 2012

Вы можете использовать перехватчик запросов Дэвида Фаулера:

https://github.com/davidfowl/QueryInterceptor

Один из примеров его использования:

IQueryable q = ...; IQueryable modifed = q.InterceptWith (новый MyInterceptor ());

А на классе MyInterceptor:

выражение защищенного переопределения VisitBinary (узел BinaryExpression) { if (node.NodeType == ExpressionType.Equal) { // Изменить == на! = return Expression.NotEqual (node.Left, node.Right); } возврат base.VisitBinary (узел); }

0 голосов
/ 09 марта 2011

Вы должны использовать атрибут QueryInterceptor, поискать здесь в SO или в Google, и вы найдете примеры того, как его использовать.

фрагмент:

[QueryInterceptor("Orders")]
public Expression<Func<Order, bool>> FilterOrders() 
{
    return o => o.Customer.Name == /* Current principal name. */;
} 

// Insures that the user accessing the customer(s) has the appropriate
// rights as defined in the QueryRules object to access the customer
// resource(s).

[QueryInterceptor ("Customers")]
public Expression<Func<Customer, bool>> FilterCustomers() 
{
  return c => c.Name == /* Current principal name. */ &&
              this.CurrentDataSource.QueryRules.Contains(
                rule => rule.Name == c.Name &&
                        rule.CustomerAllowedToQuery == true
              );
}
...