如何在 LINQ to Entities 查询中实现查询拦截?(C#)

4
我正在尝试在EF4中实现加密列,并使用CTP5功能来允许简单地使用POCO查询数据库。很抱歉这是很多的文字,但我希望以下内容足以解释需要和问题!
一些背景信息和我的进展情况:
目的是如果您在不使用我们的数据访问层的情况下查询表格,则数据是垃圾的,但我不希望开发人员担心数据何时如何加密。
为了简化起见,在这个阶段,我假设任何字符串列都将被加密。
现在,我已经成功地实现了使用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()
(在名称和/或OtherString值中具有清晰的字符串数据)
然后调用myDbContext.SaveChanges()在到达数据存储之前加密字符串。

现在,我遇到的问题是在这个查询中:

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

这将导致在数据库中将未加密值与加密值进行比较。显然,我不想从数据库中获取所有记录,将它们实例化,然后根据提供的任何 Where 子句过滤结果...所以我需要做的是:拦截该查询并通过加密 Where 子句中的任何字符串来重写它。 所以我看了一下:
  • 编写一个查询提供程序,但那似乎不是正确的解决方案...(是吗?)
  • 为 DbSet 编写自己的 IQueryable 包装器,该包装器将捕获表达式,使用表达式树访问器对其进行操作,然后将新表达式转发到 DbSet...
两者的尝试都让我有些迷茫!我认为我更喜欢第二种解决方案,因为它感觉更整洁,可能对将来的其他开发人员更清晰。但是我很乐意选择任何一个或另一个更好的选项!!
我主要遇到的问题是 LINQ 表达式何时/如何应用于对象...我想我已经有点混淆了表达式何时在 IQueryable 对象中执行,因此我不确定我需要在我的包装器中实现哪个方法来获取和操纵传递的表达式...
我相信我在这里肯定缺少了一些非常明显的东西,正在等待那个灯泡时刻...但它还没有到来!!
非常感谢您提供的任何帮助!

2
使用SQL Server 2008及其透明数据加密如何? - Ladislav Mrnka
3个回答

2

我想让您知道我的最终解决方案是什么。最终我用一个包装类实现了Where方法,但没有完全实现IQueryable接口。LINQ仍然会执行该类(至少在我想要/需要的范围内),并将表达式从LINQ中调用Where方法。

然后,我遍历这个ExpressionTree,并在将新的ExpressionTree转发到内部DbSet之前用加密值替换我的字符串,然后返回结果。

这很粗糙,并且有其限制,但在我们特定的情况下可以正常工作。

谢谢, Ben


0

你应该使用QueryInterceptor属性,在SO或谷歌中搜索,你会找到如何使用它的示例。

一个片段:

[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
              );
}

嗨,Davide,我确实看了一下QueryInterceptor,但似乎只适用于使用WCF数据服务时。如果实现数据服务是唯一的选择,那么我可以这样做,但如果可能的话,我更愿意避免它,并且我无法看到其他实现QueryInceptor的方法..? - Ben
这是 WCF 数据服务的一个特性。 - Ladislav Mrnka

0
你可以使用David Fowler的查询拦截器:

https://github.com/davidfowl/QueryInterceptor

一个使用示例:

IQueryable q = ...; IQueryable modifed = q.InterceptWith(new MyInterceptor());

而在 MyInterceptor 类中:

protected override Expression VisitBinary(BinaryExpression node) { if (node.NodeType == ExpressionType.Equal) { // 将 == 改为 != return Expression.NotEqual(node.Left, node.Right); } return base.VisitBinary(node); }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接