DataContext across multiple databases
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 s开发者_如何学Goeems 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
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.
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?
精彩评论