有没有办法拦截所有Linq to SQL查询?

时间:2013-07-26 14:30:15

标签: linq linq-to-sql interception

我已经构建了一些可以重建表达式树的代码,因此我可以避免触发不支持的转换为SQL异常,只要我调用我的函数来替换iqueryable就可以正常工作。问题是我希望它能自动应用于我项目中的所有查询,而不必担心单独调用每个查询。有什么方法可以拦截一切吗?

我尝试使用Reflection.Emit创建一个包装提供程序,并使用反射在数据上下文中替换它,结果发现即使使用Reflection.Emit,我也无法实现内部IProvider接口。

我也尝试用基于RealProxy的类替换提供程序,这适用于非编译查询,但CompiledQuery.Execute方法抛出异常,因为它不会强制转换为SqlProvider类。我尝试用另一个代理替换提供者上的Compile方法的响应,这样我就可以拦截Execute调用,但是检查返回类型是否正确。

我对使用我已经尝试过的任何其他想法或方法持开放态度?

1 个答案:

答案 0 :(得分:0)

如果没有看到您的代码,很难判断这是否是一个适用的解决方案,但如果您有一个 DI友好的应用程序架构,您可以实现一个拦截器并拥有您的最喜欢的IoC容器在运行时为您发出适当的类型。

神秘?一点。考虑这样的界面:

public interface ISomeService
{
     IEnumerable<SomeEntity> GetSomeEntities();
     // ...
}

这个界面可能是这样实现的:

public class SomeService : ISomeService
{
     private readonly DbContext _context // this is a dependency!
     private readonly IQueryTweaker _tweaker; // this is a dependency!

     public SomeService(DbContext context, IQueryTweaker tweaker) // this is constructor injection!
     {
         _context = context;
         _tweaker = tweaker;
     }

     public IEnumerable<SomeEntity> GetSomeEntities()
     {
         return _tweaker.TweakTheQuery(_context.SomeEntities).ToList();
     }
}

每次实现ISomeService接口的方法时,始终会调用_tweaker.TweakTheQuery()来包裹IQueryable,这不仅会让人觉得无聊,而且还会感觉像是缺少一个功能 - 通过将每个调用包装在try / catch块中,或者如果您熟悉WPF中的MVVM,通过提高这个令人讨厌的{{{ 1}}事件,用于ViewModel中的每个属性设置器。

使用DI拦截,您可以将此要求从“正常”代码中分解为“拦截器”:您基本上告诉IoC容器,而不是直接将PropertyChanged绑定到ISomeService实现,您将使用拦截器进行装饰,并发出另一种类型,可能SomeService(名称无关紧要,实际类型仅在运行时存在)“注入”期望的行为到期望的方法。简单?不完全是。

如果你没有设计你的代码并考虑到DI(你的依赖“注入”你的类的构造函数?),那么它可能意味着重大的重构。

第一步是破坏您的代码:删除所有SomeInterceptedService实施中的IQueryTweaker依赖关系和所有TweakTheQuery次调用,使它们看起来像这样 - 请注意ISomeService截获方法的问题:

virtual

下一步是配置IoC容器,以便在类型的构造函数需要public class SomeService : ISomeService { private readonly DbContext _context public SomeService(DbContext context) { _context = context; } public virtual IEnumerable<SomeEntity> GetSomeEntities() { return _context.SomeEntities.ToList(); } } 时知道注入SomeService实现:

ISomeService

此时您已准备好配置拦截 - if using Ninject this could help

但是在进入那个兔子洞之前,你应该read this article显示 decorator 拦截器是如何相关的。

关键是,你不是拦截 LINQ to SQL或.NET框架本身内部的任何内容 - 你正在拦截你自己的方法调用,用你的方法包装它们自己的代码,并且从任何体面的IoC容器获得一点帮助,你将拦截调用方法调用 Linq to SQL,而不是直接调用Linq到SQL本身。本质上,_kernel.Bind<ISomeService>().To<SomeService>(); 依赖项成为拦截器类的依赖项,并且您只需对其使用次数进行一次编码。

关于DI拦截的一个有趣的事情是拦截器可以组合在一起,所以你可以在IQueryTweaker之上ExecutionTimerServiceInterceptor之上AuditServiceInterceptor ...最好的部分是你可以配置你的IoC容器,以便你可以完全忘记它存在,当你向应用程序添加更多服务类时,你需要做的就是遵循你定义的命名约定和voilà ,您刚刚编写了一项服务,它不仅可以完成您刚刚编写的所有严格的数据相关任务,而且还可以在数据库服务器关闭时自动禁用3分钟的服务,并且将保留禁用直到它恢复;该服务还记录所有插入,更新和删除,并将其执行时间存储在数据库中以进行性能分析。 automagical 一词似乎合适。

这种技术 - 拦截 - 可用于解决跨领域的问题;解决这些问题的另一种方法是通过AOP,尽管some articles(和Mark Seeman的优秀Dependency Injection in .NET)清楚地证明了AOP框架对DI拦截的解决方案是不太理想的。