14

I have a legacy system with three databases

  1. Vendor
  2. CustomCode
  3. LogData

Vendor contains control and log data from our Vendors app.

CustomCode contains lots of views and stored procedures that joins to Vendor and LogData

LogData contains results from our CustomCode processes. eg: Daily/Weekly/Monthly summaries and results.

I'm writing a website that will plot data on a map. The list of units is from a view in CustomCode. The Summary record is from LogData, and the individual log points are retrieved from Vendor by a stored proc in CustomCode.

I started with a DbContext for CustomCode, but can't seem to Navigate to properties in a 2nd DbContext to LogData

Can I link navigation properties between objects in different contexts?

Can I have once context with multiple databases connected ?

Please note, this is nothing to do with multi-tenant or multi-schema

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
Hecatonchires
  • 1,009
  • 4
  • 13
  • 42
  • *Can I link navigation properties between objects in different contexts*, i dont think this is possible.. better of create a helper method or something.. inter-connection string relationship is pretty much dangerous i think (nobody can ensure the relation, but your code).. – Bagus Tesa Sep 03 '18 at 23:53
  • 1
    No, each context represents a different connection, you cannot do that. EF is not designed for this use case – Camilo Terevinto Sep 03 '18 at 23:55
  • You would have to load data from each context and then join in-memory. – Brad Sep 04 '18 at 00:13
  • 4
    If the databases can communicate to each other (ie on same server) then create a stored procedure to perform the desired queries (which can join tables from separate databases) From there you should be able to execute the procedure form EF – Nkosi Sep 04 '18 at 00:52
  • Would either of you gentlemen like to post an answer I can (sadly) accept? – Hecatonchires Sep 04 '18 at 03:27

4 Answers4

10

Can I link navigation properties between objects in different contexts?

No.

Can I have one context with multiple databases connected?

No.

Suggestion:

If the databases can communicate to each other (ie on same server), which appears to be already done since

CustomCode contains lots of views and stored procedures that joins to Vendor and LogData

then create a stored procedure to perform the desired queries (which can join tables from separate databases).

From there you should be able to expose and execute the procedure from Entity Framework to perform the desired functionality.

This would avoid have multiple contexts and trying to join the data in memory, which can have adverse effects if the data set is large.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 3
    The only problem with this is that loading the sp into a DbQuery and not DbSet means I can't use them as authoritative entities for navigational properties. I can work around with more monolithic sp's but eh. EF gives, and it takes away ;) – Hecatonchires Sep 06 '18 at 01:47
3

Also answered elsewhere (https://stackoverflow.com/a/54347237/861352), but here's the gist:

This actually appears to be a known issue, with a solution in the pipeline (although it hasn't been prioritised yet):

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

I did however find an interim solution to this problem, and it's based on two sources:

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

And here it is:


How To Do (Same Server) Cross DB Joins With One EF Core DbContext


You'll need to install the Microsoft.Extensions.DiagnosticAdapter Nuget Package

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 = "Users";

            command.CommandText = command.CommandText.Replace($" [{tableName}]", $" [{schemaName}].[{tableName}]")
                                                     .Replace($" [{schemaName}].[{tableName}]", $" [{secondaryDatabaseName}].[{schemaName}].[{tableName}]");
        }
    }
}

Replace 'MyOtherDatabase', 'dbo' and 'Users' with your Database name, table schema and table name, maybe from a config etc.

Then attach that interceptor to your context.

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());

In my case I put the above in MultipleDatabasesExampleDbContextFactory method.

Now you can just use the context as if you were referencing one database.

context.Customers // Default database defined in connection string
context.Users     // MyOtherDatabase (a different database on the same server)
LemonLion
  • 71
  • 1
  • 7
  • I think this is a dirty solution and not very robust. And *writing* to different databases in one transaction is a possible obstacle. A less dirty (but still not robust) solution is use synonyms. – Gert Arnold Jan 24 '19 at 20:52
  • 3
    @GertArnold I didn't say this was an ideal solution, I said it was an interim solution while they fix the issue properly, as stated. The solution above has been tested using basic reads, writes and cross db joins and has held up so far with my usage. If you have a better solution using synonyms please post a complete answer. – LemonLion Jan 24 '19 at 21:04
  • What if database is on different server? – Anonymous Creator Feb 28 '19 at 16:24
3

In EF Core 5.0 new features, It is now easier to create a DbContext instance without any connection or connection string. Also, the connection or connection string can now be mutated on the context instance. This feature allows the same context instance to dynamically connect to different databases.

Reference: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#use-a-c-attribute-to-indicate-that-an-entity-has-no-key

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • 6
    You should provide an example of how this is actually done in EF Core 5.0 -- I'll bet that would get you a lot of votes. – PaulG Aug 05 '20 at 07:03
  • 1
    @vivek That reference doesn't have anything that talks about the same DbContext instance dynamically connecting to different databases. It only shows "new features" but doesn't talk about the one you are mentioning. Can you provide an example or a link to an example? – Rodney S. Foley Aug 31 '21 at 21:37
1

No, You cannot link navigation properties between objects in different contexts. A context represents a particular connection or DB. You can try getting data from multiple contexts (DBs) and join them and use in-memory.

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • Which gave me the idea that.. well in my case they are the same database so maybe I should just use the new Identity tables to join on my old tables even though they are in separate context as the Database was Database-first, and the Identity stuff was Model first.. – Tim Davis May 21 '20 at 04:15