2010-01-17 7 views
3

Comment est-ce que je convertirais cette requête en SQL inline ou une procédure stockée?Comment puis-je convertir ce code linq en ligne sql

var a = from arow in context.post 
where arow.post_id == id && arow.post_isdeleted == false 
select new 
{ 
    arow.post_id, 
    PostComments = from c in context.comment 
        where c.CommentPostID == arow.post_id 
        select new 
        { 
         c.id, 
         c.title  
        } 
} 


List<PostType> pt; 
foreach (var s in a) 
{ 
    pt = new PostType(); 
    pt.PostID = s.post_id; 

    //how would I use ADO.NET to put this in a custom class? 
    foreach(var ctl in s.PostComments) 
    { 
     ctl.Title = ctl.title; 
     pt.CommentT.Add(ctl); 
    } 
    ptl.Add(pt); 
} 

Une fois la requête en ligne exécutée, comment placer les informations dans une classe personnalisée? PostComments est une sous-requête - alors comment utiliser ADO.NET pour le mettre dans une classe personnalisée?

+0

Quel sql utilisez-vous? Si vous utilisez t-sql, la réponse ne sera pas la même que si vous utilisiez plsql. – ALOToverflow

+0

J'utilise SQL Server – Luke101

Répondre

1

court Explication

Il semble que la partie de votre question qui peut sembler difficile est de savoir comment remplir une classe personnalisée de la même manière que la requête LINQ to SQL ("L2S" à partir de maintenant) pour la classe anonyme.

Sur la base de votre boucle foreach Je devine vos classes personnalisées sont semblables à ceux-ci:

public class PostType 
{ 
    public int PostId { get; set; } 
    public List<PostComment> PostComments { get; set; } 
} 
public class PostComment 
{ 
    public int CommentId { get; set; } 
    public string Title { get; set; } 
} 

La requête LINQ devrait être équivalente à cette instruction T-SQL:

SELECT P.post_id, C.id, C.title 
FROM post As P, comment As C 
WHERE 
    P.post_id = @PostId 
    AND P.post_isdeleted = 0 -- 0 is false 
    AND C.CommentPostID = P.post_id 

Contrairement au L2S version (voir la section Explication détaillée ci-dessous pour plus d'informations), cette instruction renvoie un résultat aplati avec chaque ligne contenant un P.post_id, un C.id et un C.title. Si votre classe PostType représentait une entrée de la même manière, cela aurait été facilement résolu (je ne préconise pas un tel design, je ne fais que commenter la façon dont le design modifie la façon dont il est peuplé). La relation hiérarchique dans les classes change les choses.

En outre, votre code a montré un List<PostType> mais une liste n'est pas nécessaire car il y aura toujours un PostType car vous filtrez sur post_id.Si cette condition est supprimée, puis, vous pouvez obtenir plusieurs correspondances avec des PostIds différents lorsque les autres conditions sont remplies. Si c'est le cas, le code ci-dessous devrait changer. Cela dit, sautons dans ADO.NET et peuplons les classes en utilisant un SqlDataReader.

int postIdInput = 42; // desired post_id to search for 

// PostType delcared prior to getting the results 
PostType postType = new PostType() 
{ 
    PostId = postIdInput, 
    PostComments = new List<PostComment>() 
}; 

// Database interaction starts here... 
// updated SQL statement to use column name aliases for clarity when used by the SqlDataReader 
string sqlStatement = @"SELECT P.post_id As PostId, C.id As CommentId, C.title As Title 
         FROM post As P, comment As C 
         WHERE 
          P.post_id = @PostId 
          AND P.post_isdeleted = 0 -- 0 is false 
          AND C.CommentPostID = P.post_id"; 

string sqlConnectionString = "..."; // whatever your connection is... probably identical to your L2S context.Connection.ConnectionString 
using (SqlConnection conn = new SqlConnection(sqlConnectionString)) 
{ 
    conn.Open(); 
    SqlCommand command = new SqlCommand(sqlStatement, conn); 
    command.Parameters.AddWithValue("@PostId", postIdInput); // use Parameters.Add() for greater specificity 

    SqlDataReader reader = command.ExecuteReader(); 
    while (reader.Read()) 
    { 
     // postId was set based on input, but could be set here as well although it would occur repeatedly 
     // if desired, uncomment the 2 lines below and there's no need to initialize it earlier (it'll be overwritten anyway) 
     //int postId = Int32.Parse(reader["PostId"].ToString()); 
     //postType.PostId = postId; 
     int commentId = Int32.Parse(reader["CommentId"].ToString()); 
     string title = reader["Title"].ToString(); 

     // add new PostComment to the list 
     PostComment postComment = new PostComment 
     { 
      CommentId = commentId, 
      Title = title 
     }; 
     postType.PostComments.Add(postComment); 
    } 

    // done! postType is populated... 
} 

// use postType... 

Cela devrait couvrir votre scénario. Cependant, pour une réponse beaucoup plus détaillée, continuez à lire!


Explication détaillée (alias « Enseignez un homme à pêcher ... »)

Disons que vous ne pourriez pas comprendre comment obtenir l'équivalent instruction SQL. Bien qu'il existe différentes façons de le faire, je vais me concentrer sur le fait que vous utilisez L2S et explorer certaines options connexes.

Étape 1: Conversion de la requête LINQ to SQL (par "tricherie")

Vous avez de la chance car il y a un raccourci. La conversion de votre expression LINQ existante en SQL est une situation un peu plus pratique que de revenir en arrière et de traduire SQL en LINQ.

Vous pouvez obtenir l'instruction T-SQL traduit à partir de votre code en utilisant l'une de ces options DataContext:

NOTE: J'ai dit c'était un raccourci. La connaissance de SQL est bonne à connaître, et pour être clair, je ne suggère pas l'utilisation de la production générée à l'aveuglette. Certes, le SQL peut différer de ce que vous attendez parfois, néanmoins il fournit un point de départ décent. Vous pouvez le modifier si nécessaire.

Utilisez l'une de ces méthodes et copiez le résultat - vous en aurez besoin pour Étape 2.

Exemple DataContext.GetCommand() utilisation:

var query = /* your L2S query here */; 
string sqlStatement = context.GetCommand(query).CommandText; // voila! 

Pour obtenir le résultat soit mis un point d'arrêt et de copier sa valeur, vérifiez dans la fenêtre immédiate, ou l'afficher quelque part (Console.WriteLine etc.).

Exemple d'utilisation DataContext.Log:

context.Log = Console.Out; 

requêtes exécutées sur ce contexte auront leurs instructions SQL sous-évaluées à la fenêtre de la console. Vous pouvez le copier à partir de là.Pour les jeter ailleurs, comme à la fenêtre de sortie de débogage, consultez ces liens:

Etape 2: Avec l'instruction SQL en main , utilisez-le dans ADO.NET

Maintenant que vous avez l'instruction SQL, nous pouvons l'utiliser dans ADO.NET. Bien sûr, vous pouvez aussi utiliser une procédure stockée et il ne devrait pas être difficile de la remplacer.

Avant de l'utiliser, vous souhaiterez probablement nettoyer l'instruction. J'ai utilisé une requête similaire au niveau local pour obtenir cette déclaration et votre produit ressemble probablement ceci:

SELECT [t0].[post_id], [t1].[id], [t1].[title], (
SELECT COUNT(*) 
FROM [comment] AS [t2] 
WHERE [t2].[id] = [t0].[post_id] 
) As [value] 
FROM [post] As [t0] 
LEFT OUTER JOIN [comment] As [t1] ON [t1].[CommentPostID] = [t0].[post_id] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
ORDER BY [t0].[post_id], [t1].[id] 

Notez que le SELECT COUNT intégré (*)? La requête L2S n'a jamais demandé le nombre, mais le résultat demande le nombre d'ID identiques utilisés sur la jointure. Notez également qu'il n'y a pas d'alias pour les colonnes. Vous devez vous référer aux colonnes en fonction de leurs noms réels (par exemple post_id par rapport à PostId). De plus, les paramètres SQL sont nommés @ p0 ... @ pn et un ordre de tri par défaut est appliqué. Vous pouvez copier/coller ceci dans le SqlDataReader utilisé précédemment, mais vous devrez renommer les colonnes et les paramètres à faire correspondre.

A nettoyé la version de ce qui précède est reproduit ci-dessous avec les paramètres rebaptisés et pièces inutiles sont commentées (si cette approche est prise tester pour vous assurer qu'il est équivalent à ce qui est prévu):

SELECT [P].[post_id] As PostId, [C].[id] As CommentId, [C].[title] As Title--, (
-- SELECT COUNT(*) 
-- FROM [comment] AS [t2] 
-- WHERE [t2].[id] = [t0].[post_id] 
--) As [value] 
FROM [post] As [P] 
LEFT OUTER JOIN [comment] As [C] ON [C].[CommentPostID] = [P].[post_id] 
WHERE ([P].[post_id] = @PostId) AND ([P].[post_isdeleted] = 0) 
--ORDER BY [t0].[post_id], [t1].[id] 

ci-dessus peut maintenant être utilisé avec le SqlDataReader de plus tôt.

Une requête plus directe aurait pu être générée si la requête L2S était dans le format d'un SelectMany, tels que:

var query = from arow in context.post 
      from c in context.comment 
      where arow.post_id == id && arow.post_isdeleted == false 
        && c.CommentPostID == arow.post_id 
      select new 
      { 
       arow.post_id, 
       c.id, 
       c.title  
      }; 

La requête SelectMany L2S génère une instruction SQL similaire à ceci:

SELECT [t0].[post_id], [t1].[id], [t1].[title] 
FROM [post] As [t0], [comment] As [t1] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
     AND ([t1].[CommentPostID] = [t0].[post_id]) 

LINQPad

Bien que cette explication détaillée peut sembler écrasante, il y a une moyen facile d'avoir cette information à portée de main. Si vous n'avez pas donné LINQPad un essai alors je le recommande fortement - c'est gratuit aussi! LINQPad vous montrera les résultats de vos requêtes L2S, a un onglet SQL pour voir le SQL généré, et montre également l'expression lambda utilisée (la syntaxe de la requête ci-dessus est représentée par l'équivalent lambda/extension). En plus de cela, c'est un excellent outil pour C#/VB.NET généraliste (y compris LINQ to Objects/XML), et le codage SQL avec support de base de données et bien plus encore.

Voici une capture d'écran minuscule de LINQPad montrant quelques-uns des sujets abordés précédemment:

LINQPad

Je ne voulais pas prendre plus Page Immobiliere que moi déjà si click here to see the image in its original size.


Si vous l'avez fait jusqu'ici, félicitations!:)

+0

C'est juste genious .. Je ne sais pas combien vous remercier. Je voudrais pouvoir voter plusieurs fois. Merci de m'avoir expliqué dans un tel détail ... J'ai téléchargé linqpad et cela m'a énormément aidé. J'aimerais pouvoir vous embaucher pour mon projet. Merci encore – Luke101

+0

@ Luke101 heureux d'avoir aidé et merci :) –

1

Si vous voulez dire qu'il ya une relation entre les postes et les tables PostComments et il y a des colonnes répétées dans les deux tables et un commentaire pourrait être lié à plus d'un poste afin , vous pouvez facilement créer deux commandes:

-Select * from Posts where post_Id = id AND IsDeleted = 0; 
-Select * from Postcomments where id = cid; 

puis exécutez-les à l'aide de Sql Command Adapters sur deux tables de données. puis:

foreach(DataRow dr in PostsTable.Rows) 
{ 
//Fill the Post Custom class 
SecondTable.DefaultView.RowFilter = string.Format("PostID = {0}",dr["postID"]); 
foreach(DataRow r in SecondTable.Rows) 
{ 
    //Fill the Comments Custom class 
} 
} 

Si ce n'est pas votre cas, vous pourriez donc essayer de clarifier la structure de votre base de données?

+0

Je suis désolé. J'ai modifié la requête. Oui, PostComments est lié à chaque article (Post). Similaire à comment commentaires ou liés à des réponses sur SO. – Luke101

+0

Si vous n'obtenez qu'un seul article et tous ses commentaires, vous ne pouvez créer qu'une seule commande et exécuter les deux instructions SQL avec lui. Utilisez plutôt le lecteur de données sql avec la méthode ReadNextResult() pour remplir les commentaires. –

0

Je ne peux pas tester cela, mais quelque chose le long des lignes de:

SELECT 
    p.post_id 
    c.id, 
    c.title 
FROM 
    post p 
WHERE 
    p.id == 'id' and 
    isdeleted = false 
INNER JOIN comment c ON c.commentpostid = p.post_id 

capitalise des mots clés pour que je lisibilité mais pour les dbs que vous utilisez, vous devrez peut-être changer.

1

Utilisez SQL Profiler pour intercepter la requête générée. Copier vers une nouvelle procédure stockée et réparer les paramètres d'entrée. Créer (enregistrer) et l'utiliser :)

0
select post_id, id, title from postcomments pc 
where post_id = @id and exists(
    select post_id form post p where p.post_id = pc.post_id and isdeleted = false 
) 

utiliser un DataReader pour obtenir les données & charger juste dans une liste avec vos classes personnalisées