2009-12-15 8 views
2

J'ai une application qui doit joindre des tables provenant de plusieurs bases de données dans une seule requête LINQ-to-SQL. Malheureusement, j'ai une configuration de classe DataContext distincte pour chaque base de données, donc cette requête ne fonctionnera pas. Je reçois une erreur comme ceci: « La requête contient des références à des éléments définis dans un contexte de données différentes »DataContext sur plusieurs bases de données

La solution idéale semble être de créer un seul DataContext pour les trois bases de données. Ils existent tous sur le même serveur, ils peuvent donc utiliser la même chaîne de connexion. J'utilise actuellement un script qui exécute sqlmetal.exe pour générer mes fichiers DBML et CS, ce qui signifie que je n'ai pas besoin de modifier manuellement les fichiers lorsque je change le modèle de données. Je veux maintenir ce niveau d'automatisation, mais sqlmetal.exe ne semble supporter qu'une seule base de données par fichier DBML. Est-ce possible avec sqlmetal ou un autre outil?

Est-ce possible avec sqlmetal ou un autre outil? Ou dois-je envisager une autre solution comme l'utilisation d'une seule base de données pour l'ensemble de l'application?

Voici le script de fichier batch J'utilise:

"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 
+0

J'ai trouvé une réponse qui fonctionnera probablement pour moi, mais je serais toujours intéressé par un correctif de niveau supérieur. En d'autres termes, ubersqlmetal. – MikeWyatt

Répondre

2

Un collègue trouvé a thread on another site [social.msdn.microsoft.com] qui traite de cette même question. Une solution discutée consistait à effectuer toutes les jointures dans les vues de la base de données "primaire" et à exposer ces vues en tant qu'objets dans l'application. Cela fonctionnera probablement dans ma situation, puisque la majorité de mes données sont dans une base de données, et le petit nombre de tables dans les autres bases de données sont en lecture seule.

1

Cela va sembler un peu fou, mais je viens de tester, donc essayez ceci:

  • Créer une classe d'entité personnalisée pour la table « externe » vous voulez rejoindre, ou utiliser SqlMetal à générer la classe;
  • Aller à la TableAttribute, qui devrait dire quelque chose comme [Table(Name="dbo.ExternalTable")], et changer à [Table(Name="DatabaseTwo.dbo.ExternalTable")]
  • Créer une classe partielle avec le même nom que le « DatabaseOne » DataContext, et ajoutez votre propriété là-bas Table<T>, à savoir:

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

Et maintenant, essayez d'exécuter toute votre requête hors de la première 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); 

Incroyablement, cela fonctionne. Il est aussi simple que d'utiliser SqlMetal, mais il suffit d'écrire la classe partielle une fois, puis vous pouvez simplement lancer SqlMetal sur les deux bases de données et changer le TableAttribute de toutes les tables externes pour inclure le nom de la base de données.

Ce n'est pas parfait, mais c'est 95% là, non?

+0

Ceci est intéressant, mais il faudrait encore un peu de travail pour automatiser complètement (comme vous l'avez dit). Je vais tenter le coup si la création de vues dans la base de données principale ne fonctionne pas. – MikeWyatt

+0

Vous pouvez probablement écrire un petit script dans lequel vous venez d'alimenter les deux fichiers CS (générés par SqlMetal) et les noms de tables que vous voulez "importer", et de générer automatiquement la classe et les entités partielles. Le premier est un modèle de texte, le dernier est juste une chaîne.Remplacer, de sorte que vous finiriez avec quelque chose de totalement automatisé.Mais si vous contrôlez les bases de données et que cela ne vous dérange pas de créer des vues inter-bases de données, cela sera plus facile à gérer sur le long terme. – Aaronaught

Questions connexes