2010-01-21 6 views
3

J'ai deux tables avec lesquelles j'utilise Linq to SQL. Les tables ont une association de 1 à plusieurs. La partie importante du schéma de base de données est la suivante:LINQ To SQL peut-il générer un SQL non valide?

Camera: 
    Id (int) 
    SerialNumber (string) 
    ... 

CameraCalibration 
    Id (int) 
    CameraFk (int) 
    ... 

LINQ to SQL que je peux obtenir une liste de tous les calibrations de l'appareil photo pour les caméras avec 10 caractères numéros de série comme suit

var query = from n in db.CameraCalibrations 
    where n.Camera.SerialNumber.Length == 10 
    select n 

De ma compréhension de LINQ la requête suivante devrait fonctionner également (même si par c'est plutôt comparision brutale ...)

var query = from n in db.CameraCalibrations 
where db.Cameras.Where(c => c.Id == n.CameraFk).SingleOrDefault() 
       .SerialNumber.Length == 10 
select n 

Cependant quand j'exécute cette deuxième requête contre la Databa Je reçois une exception SQL indiquant "Impossible d'appeler des méthodes sur nvarchar". Quand je regarde le SQL généré, il semble assez clair pourquoi l'exception est générée:

SELECT t0.* 
FROM CameraCalibration AS t0 
WHERE (
    SELECT t1.serialNumber.Length 
    FROM Camera AS t1 
    WHERE t1.id = t0.cameraFk 
    ) = 10 

Remarquez comment le SQL généré utilise l'expression t1.serialNumber.Length? Je m'attendais à ce que cela soit traduit en LEN (t1.serialNumber), et en effet, avec cette modification, la requête fonctionne.

Est-ce que je fais quelque chose de mal ici? Ou est-ce une erreur dans la façon dont j'ai structuré ma requête et/ou une limitation de LINQ to SQL?

Bien que je puisse facilement restructurer la requête que j'ai utilisée pour démontrer le problème, dans mon scénario réel, cela sera beaucoup plus difficile (en partie en raison de l'implication dynamique de LINQ).

Comment puis-je écrire une requête similaire à la seconde (avec une recherche effectuée dans la clause where) que LINQ to SQL sera heureux d'exécuter?

+0

Assez bizarre - on dirait que vous avez trouvé un bug. Cependant, il est presque toujours préférable d'utiliser une jointure plutôt qu'une sous-requête, alors j'accepte la réponse de Robert Harvey. – Aaronaught

Répondre

1

Dans ce cas particulier, je pense que vous devez joindre vos deux tables dans votre instruction Linq. Cela vous permettra d'obtenir une instance de SerialNumber sans utiliser l'association .SerialNumber.Length. Quelque chose comme ça (non testé):

var query = from n in db.CameraCalibrations 
      join c in db.Cameras on c.Id equals n.CameraFk 
      where c.SerialNumber.Length == 10 
      select n; 
+0

Votre solution fonctionne. Une autre façon est d'utiliser db.Cameras.Where (c => c.Id == n.CameraFk) .SingleOrDefault (z => z.SerialNumber.Length == 10)! = Null comme la clause where. Mon problème est dans mon scénario réel le cadre générant dynamiquement les requêtes LINQ ne me permettra pas facilement de réécrire les requêtes pour utiliser une jointure etc. C'est pourquoi j'essaie de comprendre pourquoi les requêtes de la structure existante lancent des exceptions SQL. –