开发者

How do i convert this linq code to inline sql

开发者 https://www.devze.com 2022-12-16 10:40 出处:网络
How would I covert this query to inline sql or a stored procedure? var a = from arow in context.post where arow.post_id == id && arow.post_isdeleted == false

How would I covert this query to inline sql or a stored procedure?

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     
开发者_如何学C                    }
}


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);
}

Once the inline query has been executed how would I put the information in a custom class? PostComments is a subquery -- so how would I use ADO.NET to put it in a custom class?


Short Explanation

It seems the portion of your question that may seem tricky is how to populate a custom class in the same fashion that the LINQ to SQL ("L2S" from here on out) query does for the anonymous class.

Based on your foreach loop I am guessing your custom classes are similar to these:

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; }
}

The LINQ query should be equivalent to this T-SQL statement:

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

Unlike the L2S version (see the Detailed Explanation section below for more info), this statement returns a flattened result with each row containing a P.post_id, C.id, and C.title. If your PostType class represented an entry in the same way this would've been easily solved (I'm not advocating such a design; I'm merely commenting on how the design alters how it gets populated). The hierarchical relationship in the classes changes things.

Also, your code showed a List<PostType> but a list is not required since there will always be one PostType because you're filtering on post_id. If that condition is removed, then you might get multiple matches with different PostIds where the other conditions are satisfied. If that's the case the code below would need to change.

That said, let's jump into some ADO.NET and populate the classes using a 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...

That should cover your scenario. However, for a much more detailed answer, keep reading!


Detailed Explanation (aka "Teach a man to fish...")

Let's say you couldn't figure out how to get the equivalent SQL statement. While there are different ways to do so, I will concentrate on the fact that you are using L2S and explore some related options.

Step 1: Converting the LINQ query to SQL (by "cheating")

You're in luck since there's a shortcut. Converting your existing LINQ expression to SQL is a slightly more convenient situation to be in than going backwards and translating SQL to LINQ.

You can get the translated T-SQL statement from your code by using either of these DataContext options:

  • DataContext.GetCommand() method
  • DataContext.Log property

NOTE: I did say this was a shortcut. Knowledge of SQL is good to know, and to be clear I am not suggesting the use of generated output blindly. Granted, the SQL may differ from what you expect sometimes, nonetheless it provides a decent starting point. You may tweak it if needed.

Use either of these methods and copy the result - you'll need it for Step 2.

Example DataContext.GetCommand() usage:

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

To get the result either set a breakpoint and copy its value, check it out in the Immediate Window, or display it somewhere (Console.WriteLine etc.).

Example DataContext.Log usage:

context.Log = Console.Out;

Queries executed on that context will have their SQL statements dumped to the console window. You may copy it from there. To dump them elsewhere, such as to the Debug output window, check out these links:

  • Damien Guard's blog post: LINQ to SQL log to debug window, file, memory or multiple writers
  • Sending the LINQ To SQL log to the debugger output window

Step 2: With the SQL statement in hand, use it in ADO.NET

Now that you have the SQL statement we can use it in ADO.NET. Of course you could use a Stored Procedure as well and it shouldn't be hard to substitute it.

Before using it though, you'll probably want to clean the statement up. I used a similar query locally to get this and your generated statement probably resembles this:

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]

Notice the embedded SELECT COUNT(*)? The L2S query never requested the count, yet the result requests the count of the equal IDs used on the join. Also notice that there are no aliases for the columns. You would refer to the columns based on their actual names (ie. post_id versus PostId). In addition, the SQL parameters are named @p0...@pn and a default sort order is applied. You could copy/paste this into the SqlDataReader used earlier, but you would need to rename the columns and parameters to match.

A cleaned up version of the above is reproduced below with renamed parameters and unnecessary parts commented out (if this approach is taken test it out to ensure it's equivalent to what is expected):

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]

The above can now be used with the SqlDataReader from earlier.

A more direct query could've been generated if the L2S query was in the format of a SelectMany, such as:

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    
            };

The SelectMany L2S query generates a SQL statement similar to this:

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

While this detailed explanation might seem overwhelming, there's an easy way to have this information at your fingertips. If you haven't given LINQPad a try then I highly recommend it - it's free too! LINQPad will show you the results of your L2S queries, has a SQL tab to view the generated SQL, and also shows the lambda expression used (the above query syntax is shown as the lambda/extension equivalent). On top of that, it's a great tool for general purpose C#/VB.NET (including LINQ to Objects/XML), and SQL coding with database support and much more.

Here's a tiny screenshot of LINQPad showing some of the topics discussed earlier:

How do i convert this linq code to inline sql

I didn't want to take up more page real estate than I already have so click here to see the image in its original size.


If you made it this far, congrats! :)


If you mean that there is a relation between Posts and PostComments tables and there are repeated columns in both tables and one comment could be related to more than one post so you can easily create two commands:

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

and then execute them using Sql Command Adapters on two data tables. and then:

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
 } 
}

If this is not your case, so could you try to clarify your database structure?


Use SQL Profiler to catch the generated query. Copy to new stored procedure and repair input parameters. Create (save) and use it :)


I can't test this but something along the lines of:

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

I capitalize keywords for readability but for the dbs you're using you might need to change that.


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
)

use a DataReader to get the data & just load it in a list with your custom classes

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号