Bonjour à toutes et tous,
Je fais l'impasse sur le fait que je n'ai pas alimenté ce blog depuis un certain temps...
Concentrons nous sur le sujet de ce billet : une déconvenue (de plus) avec Linq To Sql.
Dans une de nos applications nous utilisons Linq To Sql pour notre couche Dal (Data Access Layer).
Le problème que j'ai rencontré dernièrement et que j'expose ici, est lié à l'utilisation de Linq to Sql en utilisant des transactions (IDbTransaction), avec une de nos procédures stockées qui fait un update de record en base après avoir fait un select de controle de cohérence.
Exemple :
create procedure UpdateCommande(@idCommande int, @idUtilisateur int, @nomCommande as varchar(20))
begin
-- Vérifit que l'utilisateur à le droit de modifier la commande
declare @isUserAuthorise int
select @isUserAuthorise = id from TablePermissions where idCommande = @idCommande and @idUtilisateur = utilisateur and action = 'Modification'
-- Fait l'update si possible
if @isUserAuthorise != null
begin
update commande set name = @nomCommande where idCommande = @idCommande
end
end
Après avoir écrit ma procédure stockée et l'avoir "drag and droppé" dans le designer Linq To Sql, j'utilise la méthode générée par le designer pour faire mon UpdateCommande() depuis ma couche métier, puis je commit la transaction. Bing ! Ici une exception est levée : "SqlException : "The transaction operation cannot be performed because there are pending requests working on this transaction.".
Ici je n'ai pas compris tout de suite ce qui était en déaut, je dirai même que ça m'a pris une demi journée pour retrouver l'origine du problème.
J'ai finalement pensé qu'une fois de plus le designer linq to Sql avait peut être trouver moyen de générer du code non adapté à ma procédure stockée. En effet le designer m'avait généré un type de retour sur la fonction d'appel à mon update en base. Du coup à chaque appel à ma fonction UpdateCommande(), linq to sql émet une requete du type ExecuteReader() au lieu de ExecuteScalar(). Et comme je ne consomme pas les données renvoyées par le ExecuteReader(), le reader reste ouvert !
Résolution du problème :
1- Modifier la procédure stockée en retirant le select qui induit en erreur le designer
2- Supprimer puis ajouter à nouveau la procédure stockée dans le .dbml avec le designer : constater qu'il n'ya pas de type de retour généré par le designer.
3- Restaurer le select dans la procédure stockée.
Conclusion :
Ce problème lié au designer n'est pas un cas isolé car il arrive fréquemment qu'une procédure stockée un peu complexe (création de table temporaire, if-else trop nombreux) qui renvoi des données, mette en défaut le designer, qui n'est alors pas capable de générer un type de retour à cette procédure stockée. Dans ce cas il faut faire comme vu précédemment, en remplaçant le code complexe par un code simple à partir duquel le designer sera capable d'interpréter les colonnes de retour et ensuite restaurer le code de la procédure stockée.
Encore une chose... J'ai entendu à plusieurs reprises ces temps-ci qu'il était préférable de passer à Linq to Entities et arrêter d'utiliser Linq To Sql car il sera rapidement obsolète chez Microsoft (plus de mise à jour du composant hors gros bugs bien sur)
Voilà j'espère que ça pourra vous aidez sur votre problème.