仅使用一个dbContext在多个数据库上执行投影

时间:2017-04-07 15:33:12

标签: c# database entity-framework

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

我们有一个特别复杂的查询,我们将从传统的ADO.NET原始SQL移植。问题是它是一个跨数据库查询。要复制此问题,我们真正需要的是从同一个dbContext中的三个数据库中获取所讨论查询的所有三个表的方法。

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

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

3 个答案:

答案 0 :(得分:2)

因此,发问者(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连接


您需要安装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)

答案 1 :(得分:1)

好的,所以我相信我们找到了一种使用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();

答案 2 :(得分:0)

有趣的问题。

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

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

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

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

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