【问题标题】:Performing projections on multiple databases with only one dbContext仅使用一个 dbContext 对多个数据库执行投影
【发布时间】:2017-04-07 15:33:12
【问题描述】:

我们正在将 EF.Core 用于当前项目。该项目有三个数据库,这是一个无法避免的真正麻烦。数据库具有相同的结构。因此我们能够交换上下文并使用相同的 EF 模型进行 CRUD 操作。

我们有一个特别复杂的查询,我们将从传统的 ADO.NET 原始 SQL 移植过来。问题是它是一个跨数据库查询。为了复制这个问题,我们真正需要的是一种方法,可以从同一个 dbContext 中的三个数据库中获取所有三个用于查询的表。

开始的绝对噩梦,尝试了很多东西。是否在 EF Core 中用于继承的 Table Per Hierarchy (TPH) 内容。我不相信它可以针对多个数据库来完成。这是我们使用的一个有用的网站。 http://learnentityframeworkcore.com/inheritance

所以我们想知道是否有人有任何关于使用 EF 可以实现的体面信息。

【问题讨论】:

    标签: c# database entity-framework


    【解决方案1】:

    所以提问者 (P.Joe) 在这里回答了他自己的问题 (https://stackoverflow.com/a/43326729/861352),但我实际上无法得到这个答案。它不起作用的原因是

    [Table("dbname.scheme.Stock", Schema = "dbo")]
    

    将在 sql 中呈现为

    [dbo].[dbname.scheme.Stock]
    

    什么时候你真正想要的 SQL 是

    [dbname].[dbo].[Stock]
    

    即使您尝试弄乱它也行不通。我尝试了各种组合

    [Table("Stock", Schema = "dbname.dbo")] // SQL => [dbname.dbo].[Stock]
    [Table("Stock", Schema = "[dbname].[dbo]")] // SQL => [[dbname]].[dbo]]].[Stock]
    [Table("Stock", Schema = "dbname].[dbo")] // SQL => [dbname]].[dbo].[Stock]
    [Table("Stock", Schema = "dbname.[dbo")] // SQL => [dbname.[dbo].[Stock]
    

    ...等等。基本问题是您需要将 dbname 与在解析时转义的括号分开。

    这实际上似乎是一个已知问题,正在制定解决方案(尽管尚未确定优先级):

    https://github.com/aspnet/EntityFrameworkCore/issues/4019

    不过,我确实找到了解决此问题的临时解决方案,它基于两个来源:

    https://stackoverflow.com/a/26922902/861352(EF6解决方案) https://weblogs.asp.net/ricardoperes/interception-in-entity-framework-core

    这里是:


    如何使用一个 EF Core DbContext 进行(同一服务器)跨 DB 联接


    您需要安装 Microsoft.Extensions.DiagnosticAdapter Nuget 包

    using System;
    using System.Data.Common;
    using Microsoft.EntityFrameworkCore.Diagnostics;
    using Microsoft.Extensions.DiagnosticAdapter;
    
    namespace Example
    {
        public class CommandInterceptor
        {
            [DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
            public void OnCommandExecuting(DbCommand command, DbCommandMethod executeMethod, Guid commandId, Guid connectionId, bool async, DateTimeOffset startTime)
            {
                var secondaryDatabaseName = "MyOtherDatabase";
                var schemaName = "dbo";
                var tableName = "Stock";
    
                command.CommandText = command.CommandText.Replace($" [{tableName}]", $" [{schemaName}].[{tableName}]")
                                                         .Replace($" [{schemaName}].[{tableName}]", $" [{secondaryDatabaseName}].[{schemaName}].[{tableName}]");
            }
        }
    }
    
    

    用您的数据库名称、表架构和表名替换“MyOtherDatabase”、“dbo”和“Stock”,可能来自配置等。

    然后将该拦截器附加到您的上下文中。

    using System.Diagnostics;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    
    
    
    var context = new MultipleDatabasesExampleDbContext(optionsBuilder.Options);
    
    // Add interceptor to switch between databases
    var listener = context.GetService<DiagnosticSource>();
    (listener as DiagnosticListener).SubscribeWithAdapter(new CommandInterceptor());
    
    

    在我的例子中,我将上面的内容放在 MultipleDatabasesExampleDbContextFactory 方法中。

    现在您可以像引用一个数据库一样使用上下文。

    context.Customers // Default database defined in connection string
    context.Stocks    // MyOtherDatabase (a different database on the same server)
    

    【讨论】:

      【解决方案2】:

      好的,所以我相信我们已经找到了一种使用 EF Core 进行跨数据库查询的方法,该方法在数据库上而不是在共享内存中执行。因此,我将以库存系统为例。我希望它对某人有用。

      首先需要在主表上为 SQLSERVER db 创建同义词,因此关联的表都链接在一起。

      那么我们的EF模型和派生类应该如下。

       public abstract class Stock
       {  
         public int ID{get;set;}
         public string stockName{get;set;}
       }
      
       [Table("dbname.scheme.Stock", Schema = "dbo")]
       public class WinterStock : Stock
       {
         public WinterStock() : base() { }
       }
      
       [Table("dbname.scheme.Stock", Schema = "dbo")]
       public class SummerStock : Stock
       {
        public SummerStock() : base() { }
       }
      

      在我们的测试方法中,我们有...

        var query = _context.Set<WinterStock>().Join(_context.Set<SummerStock>(), winterStock => winterStock.stockName,summerStock => summerStock.stockName, (summerStock,winterStock) => new { summerStock, winterStock });
        var result = query.ToList();
      

      【讨论】:

        【解决方案3】:

        有趣的问题。

        一个上下文不能跨多个数据库,

        使用多个上下文最终会在内存中加载数据并对可能不需要甚至不可能的对象执行 linq。

        第一个选项是在数据库中使用链接表并对其进行查询。我认为这是最简单的。

        我不知道您的查询有多复杂以及建议内存方案的数据有多大,根据您在查询中所做的事情,您可能有一些临时解决方案,例如:

        • 您可以实现一些枚举器,这些枚举器按特定顺序从一个数据库中加载数据,以提高内存过程的效率,然后您可以将它们组合成一个并使用它。
        • 如果您的查询更多是关于大数据的连接,您必须打破它,创建具有最少列数的对象,并在此基础上实现查询,进一步您可以将此查询结果用于其他查询加载缺失的列。

        【讨论】:

        • 感谢您的信息。我们基本上是在尝试避免加载到内存中。试图让数据库去做。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多