1

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

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

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

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

4

1 回答 1

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调用,这不仅会让人感到无聊,而且感觉就像缺少一个功能——就像包装每个调用一样在/块内,或者如果您熟悉 WPF 中的 MVVM,请为 ViewModel 中的每个属性设置器引发这个烦人的事件。_tweaker.TweakTheQuery()IQueryabletrycatchPropertyChanged

使用 DI 拦截,你将这个要求从你的“正常”代码中分解到一个“拦截器”中:你基本上告诉 IoC 容器,而不是ISomeService直接绑定到SomeService实现,你将用一个拦截器来装饰它,并且发出另一种类型,也许SomeInterceptedService(名称无关紧要,实际类型仅在运行时存在),它将所需的行为“注入”到所需的方法中。简单的?不完全是。

如果您在设计代码时没有考虑 DI(您的依赖项是否“注入”到类的构造函数中?),这可能意味着重大重构。

第一步会破坏您的代码:从所有实现中删除IQueryTweaker依赖项和所有TweakTheQuery调用ISomeService,使它们看起来像这样 - 注意virtual要拦截的方法的性质:

public class SomeService : ISomeService
{
     private readonly DbContext _context

     public SomeService(DbContext context)
     {
         _context = context;
     }

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

下一步是配置 IoC 容器,以便它知道在SomeService类型的构造函数需要时注入实现ISomeService

_kernel.Bind<ISomeService>().To<SomeService>();

此时您已准备好配置拦截 -如果使用 Ninject 这可能会有所帮助

但是在跳进兔子洞之前,你应该阅读这篇文章,它展示了装饰器拦截器是如何相关的。

关键是,您不会拦截LINQ to SQL 或 .NET 框架本身内部的任何内容 -您正在拦截自己的方法调用,用自己的代码包装它们,并得到任何体面的帮助IoC 容器,您将拦截对调用Linq to SQL 的方法的调用,而不是直接调用 Linq to SQL 本身。本质上,IQueryTweaker依赖项变成了拦截器类的依赖项,您只需编写一次它的用法。

关于 DI 拦截的一个有趣的事情是,拦截器可以组合起来,所以你可以在 aExecutionTimerServiceInterceptor之上AuditServiceInterceptor,在 a 之上CircuitBreakerServiceInterceptor......,最好的部分是你可以配置你的 IoC 容器,这样你就可以完全忘记它存在,并且当您向应用程序添加更多服务类时,您需要做的就是遵循您定义的命名约定,,您刚刚编写了一个服务,它不仅可以完成所有与数据严格相关的任务'刚刚编写了代码,但如果数据库服务器关闭,该服务将自行禁用 3 分钟,并在备份之前保持禁用状态;该服务还记录所有插入、更新和删除,并将其执行时间存储在数据库中以进行性能分析。自动魔法这个词似乎合适。

这种技术——拦截——可以用来解决横切关注点;解决这些问题的另一种方法是通过 AOP,尽管一些文章(以及 Mark Seeman在 .NET 中的出色依赖注入)清楚地展示了 AOP 框架如何不是 DI 拦截的理想解决方案。

于 2013-07-26T19:31:10.887 回答