逻辑青橙 發表於 2025-4-21 22:51:00

关于EFCore插件API使用中踩过的坑

<p>本文基于efcore8.0,参考文档。<br>
假设需要重写efcore或者特定数据库的方法的SQL转换,按上面文档及其示例代码,本例中我们大概就是提供下面几个接口的实现。</p>
<ul>
<li>IMethodCallTranslator</li>
<li>IMethodCallTranslatorPlugin</li>
<li>IDbContextOptionsExtension</li>
<li>以及特定数据库的DbContextOptionsBuilder(如SqlServerDbContextOptionsBuilder)扩展方法</li>
</ul>
<p>后面再说说踩过的坑(总结)!</p>
<h1 id="示例">示例</h1>
<pre><code class="language-csharp">public class MyMethodCallTranslator:IMethodCallTranslator
{
    private readonly SqlServerSqlExpressionFactory _sqlExpressionFactory;
    public MyMethodCallTranslator(SqlServerSqlExpressionFactory sqlExpressionFactory)
    {
      _sqlExpressionFactory=sqlExpressionFactory
    }
    public virtual SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList&lt;SqlExpression&gt; arguments,IDiagnosticsLogger&lt;DbLoggerCategory.Query&gt; logger)
    {
      //你需要的转换代码,并返回你的SqlExpression
      return null;
    }
}
public class MyMethodCallTranslatorPlugin:IMethodCallTranslatorPlugin
{
    public IEnumerable&lt;IMethodCallTranslator&gt; Translators{get;}
    //坑1:这里注入时不能用SqlServerSqlExpressionFactory,无法解析SqlServerSqlExpressionFactory,只能ISqlExpressionFactory注入后再强转成SqlServerSqlExpressionFactory
    public MyMethodCallTranslatorPlugin(ISqlExpressionFactory sqlExpressionFactory)
    {
      Translators=new List&lt;IMethodCallTranslator&gt;()
      {
            new MyMethodCallTranslator((SqlServerSqlExpressionFactory)sqlExpressionFactory)
      }
    }
}
public class MyDbContextOptionsExtension:IDbContextOptionsExtension
{
    private DbContextOptionsExtensionInfo? _info;
    public virtual void ApplyServices(IServiceCollection services)
    {
      //坑2:必须用new EntityFrameworkRelationalServicesBuilder(services)再添加服务,不能直接用services添加
      new EntityFrameworkRelationalServicesBuilder(services)
            .TryAdd&lt;IMethodCallTranslatorPlugin, MyMethodCallTranslatorPlugin&gt;();
    }
    public virtual DbContextOptionsExtensionInfo Info
      =&gt; _info ??= new ExtensionInfo(this);
    public virtual void Validate(IDbContextOptions options)
    {
    }
    private sealed class ExtensionInfo(IDbContextOptionsExtension extension) : DbContextOptionsExtensionInfo(extension)
    {
      public override bool IsDatabaseProvider =&gt; false;

      public override int GetServiceProviderHashCode() =&gt; 0;

      public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
            =&gt; other is ExtensionInfo;

      public override void PopulateDebugInfo(IDictionary&lt;string, string&gt; debugInfo)
      {
      }

      public override string LogFragment =&gt; "";
    }
}
public static class MySqlServerDbContextOptionsBuilder Extensions
{
    //坑3:这里要用具体数据库的...DbContextOptionsBuilder,用DbContextOptionsBuilder也行,但是就无法注入具体的ISqlExpressionFactory
    //关键是SqlServerDbContextOptionsBuilder不是从DbContextOptionsBuilder继承的,看名称似乎是继承的,其实不是
    public static SqlServerDbContextOptionsBuilder Use...(this SqlServerDbContextOptionsBuilder optionsBuilder)
    {
      var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;

      var extension = coreOptionsBuilder.Options.FindExtension&lt;MyDbContextOptionsExtension&gt;()
            ?? new MyDbContextOptionsExtension();

      ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);

      return optionsBuilder;
    }
}
</code></pre>
<h1 id="总结">总结</h1>
<h2 id="1-isqlexpressionfactory">1. ISqlExpressionFactory</h2>
<p>注入时不能用SqlServerSqlExpressionFactory,无法解析SqlServerSqlExpressionFactory,只能ISqlExpressionFactory注入后再强转成SqlServerSqlExpressionFactory</p>
<h2 id="2-entityframeworkrelationalservicesbuilder">2. EntityFrameworkRelationalServicesBuilder</h2>
<p>必须用new EntityFrameworkRelationalServicesBuilder(services)再添加服务,不能直接用services添加</p>
<h2 id="3-dbcontextoptionsbuilder">3. DbContextOptionsBuilder</h2>
<p>这里要用具体数据库的...DbContextOptionsBuilder,用DbContextOptionsBuilder也行,但是就无法注入具体的ISqlExpressionFactory<br>
关键是SqlServerDbContextOptionsBuilder不是从DbContextOptionsBuilder继承的,看名称似乎是继承的,其实不是</p>


</div>
<div id="MySignature" role="contentinfo">
    <div id="AllanboltSignature">
<p id="PSignature" style="border-top-color: #e0e0e0; border-top-width: 1px; border-top-style: dashed; border-right-color: #e0e0e0; border-right-width: 1px; border-right-style: dashed; border-bottom-color: #e0e0e0; border-bottom-width: 1px; border-bottom-style: dashed; border-left-color: #e0e0e0; border-left-width: 1px; border-left-style: dashed; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 80px; background-image: url(https://images.cnblogs.com/cnblogs_com/pains/109838/r_copyright.png); background-attachment: initial; background-origin: initial; background-clip: initial; font-family: 微软雅黑; font-size: 11px; background-color: #e5f1f4; background-position: 1% 50%; background-repeat: no-repeat no-repeat; ">
作者:Rick Carter
<br />
出处:http://pains.cnblogs.com/
<br />
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
</p>
</div><br><br>
来源:https://www.cnblogs.com/pains/p/18839128
頁: [1]
查看完整版本: 关于EFCore插件API使用中踩过的坑