DataContext across multiple databases

2020-03-06 06:28发布

问题:

I have an application that needs to join tables from multiple databases into a single LINQ-to-SQL query. Unfortunately, I have a separate DataContext class setup for each database, so this query won't work. I get an error like this: "The query contains references to items defined on a different data context"

The ideal solution seems to be to create a single DataContext for all three databases. They all exist on the same server, so they can use the same connection string. I currently use a script that runs sqlmetal.exe to generate my DBML and CS files, meaning that I don't need to manually edit any files when I change the data model. I want to maintain that level of automation, but sqlmetal.exe only seems to support a single database per DBML file.

Is that possible with sqlmetal or another tool? Or, do I need to consider another solution like using a single database for the entire application?

Here's the batch file script I'm using:

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseOne /views /functions /sprocs /dbml:DatabaseOne.dbml /namespace:Model.Domain.DatabaseOne /context:DatabaseOneDataContext /pluralize
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseOne /views /functions /sprocs /code:DatabaseOne.designer.cs /language:C# /namespace:Model.Domain.DatabaseOne /context:DatabaseOneDataContext /pluralize

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseTwo /views /functions /sprocs /dbml:DatabaseTwo.dbml /namespace:Model.Domain.DatabaseTwo /context:DatabaseTwoDataContext /pluralize
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseTwo /views /functions /sprocs /code:DatabaseTwo.designer.cs /language:C# /namespace:Model.Domain.DatabaseTwo /context:DatabaseTwoDataContext /pluralize

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseThree /views /functions /sprocs /dbml:DatabaseThree.dbml /namespace:Model.Domain.DatabaseThree /context:DatabaseThreeDataContext /pluralize
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe" /server:MYSERVER /database:DatabaseThree /views /functions /sprocs /code:DatabaseThree.designer.cs /language:C# /namespace:Model.Domain.DatabaseThree /context:DatabaseThreeDataContext /pluralize

回答1:

A co-worker found a thread on another site [social.msdn.microsoft.com] that discusses this same issue. One discussed solution was to perform all joins in views in the "primary" database, and expose those views as objects in the application. That will probably work in my situation, since the majority of my data is in one database, and the small number of tables in the other databases are read-only.



回答2:

This is going to sound a little crazy, but I just tested it, so try this:

  • Create a custom entity class for the "external" table you want to join to, or use SqlMetal to generate the class;
  • Go to the TableAttribute, which should say something like [Table(Name="dbo.ExternalTable")], and change it to [Table(Name="DatabaseTwo.dbo.ExternalTable")]
  • Create a partial class with the same name as the "DatabaseOne" DataContext, and add your Table<T> property there, i.e.:

    partial class DatabaseOneDataContext
    {
        public Table<ExternalTableRow> ExternalTable
        {
            get { return GetTable<ExternalTableRow>(); }
        }
    }
    

And now try running your entire query off of the first DataContext:

DatabaseOneDataContext context = new DatabaseOneDataContext();
var query = from s in context.RealTable
            join t in context.ExternalTable
              on s.ID equals t.ID
            select new { s, t };
Console.WriteLine(query.ToList().Count);

Unbelievably, it works. It's not quite as simple as just using SqlMetal, but you only need to write the partial class once, then you can just run SqlMetal on both databases and change the TableAttribute of any external tables to include the database name.

It's not perfect, but it's 95% there, no?