Краткое объяснение
Кажется, что часть вашего вопроса, которая может показаться сложной, состоит в том, как заполнить пользовательский класс таким же образом, как запрос LINQ to SQL ("L2S" с этого момента), для анонимного класса.
Основываясь на вашем foreach
цикле, я предполагаю, что ваши пользовательские классы похожи на эти:
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; }
}
Запрос LINQ должен быть эквивалентен этому выражению 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
В отличие от версии L2S (дополнительную информацию см. В разделе «Подробное объяснение» ниже), этот оператор возвращает плоский результат, где каждая строка содержит P.post_id
, C.id
и C.title
. Если бы ваш класс PostType
представлял запись таким же образом, это было бы легко решить (я не защищаю такой дизайн; я просто комментирую, как дизайн меняет то, как он заполняется). Иерархические отношения в классах меняют вещи.
Кроме того, ваш код показал List<PostType>
, но список не требуется, поскольку всегда будет один PostType
, потому что вы фильтруете по post_id
. Если это условие будет удалено, , а затем вы можете получить несколько совпадений с разными идентификаторами PostId, если выполняются другие условия. В этом случае приведенный ниже код необходимо будет изменить.
Тем не менее, давайте перейдем к некоторому ADO.NET и заполним классы с помощью 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...
Это должно охватывать ваш сценарий. Однако, для более подробного ответа, продолжайте читать!
Подробное объяснение (он же "Научите человека ловить рыбу ..." )
Допустим, вы не могли понять, как получить эквивалентный оператор SQL. Хотя для этого есть разные способы, я сосредоточусь на том факте, что вы используете L2S, и рассмотрю некоторые связанные варианты.
Шаг 1. Преобразование запроса LINQ в SQL (путем «обмана»)
Вам повезло, потому что есть ярлык. Преобразование существующего выражения LINQ в SQL является несколько более удобной ситуацией, чем переход назад и перевод SQL в LINQ.
Вы можете получить переведенный оператор T-SQL из своего кода, используя любую из следующих опций DataContext:
ПРИМЕЧАНИЕ: Я сказал, что это был ярлык. Знание SQL полезно знать, и, чтобы быть ясным, я не предлагаю использовать сгенерированный вывод вслепую. Конечно, SQL иногда может отличаться от того, что вы ожидаете, тем не менее, он обеспечивает достойную отправную точку. При необходимости вы можете настроить его.
Используйте любой из этих методов и скопируйте результат - он понадобится вам для Шаг 2 .
Пример использования DataContext.GetCommand ():
var query = /* your L2S query here */;
string sqlStatement = context.GetCommand(query).CommandText; // voila!
Чтобы получить результат, либо установите точку останова и скопируйте ее значение, проверьте его в «Немедленном окне» или где-нибудь отобразите (Console.WriteLine и т.
Пример использования DataContext.Log:
context.Log = Console.Out;
Запросы, выполненные в этом контексте, будут выгружать свои операторы SQL в окно консоли. Вы можете скопировать его оттуда. Чтобы вывести их в другое место, например, в окно вывода отладки, проверьте следующие ссылки:
Шаг 2: Имея в руках оператор SQL, используйте его в ADO.NET
Теперь, когда у вас есть оператор SQL, мы можем использовать его в ADO.NET. Конечно, вы также можете использовать хранимую процедуру, и ее не составит труда заменить.
Однако, прежде чем использовать его, вы, вероятно, захотите убрать утверждение. Я использовал подобный запрос локально, чтобы получить это, и ваш сгенерированный оператор, вероятно, похож на это:
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]
Обратите внимание на встроенный SELECT COUNT (*)? Запрос L2S никогда не запрашивал количество, но результат запрашивает количество равных идентификаторов, используемых в соединении. Также обратите внимание, что для столбцов нет псевдонимов. Вы должны ссылаться на столбцы на основе их фактических имен (т. Е. post_id
против PostId
). Кроме того, параметры SQL называются @ p0 ... @ pn и применяется порядок сортировки по умолчанию. Вы можете скопировать / вставить это в SqlDataReader, использованный ранее, но вам нужно будет переименовать столбцы и параметры для соответствия.
Ниже приведена очищенная версия вышеупомянутого с переименованными параметрами и закомментированными ненужными частями (если этот подход выбран, проверьте его, чтобы убедиться, что он эквивалентен ожидаемому):
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]
Выше можно теперь использовать с SqlDataReader от ранее.
Более прямой запрос мог бы быть сгенерирован, если бы запрос L2S был в формате SelectMany , например:
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
};
Запрос SelectMany L2S генерирует оператор SQL, подобный следующему:
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
Хотя это подробное объяснение может показаться ошеломляющим, существует простой способ получить эту информацию у вас под рукой. Если вы еще не попробовали LINQPad , тогда я настоятельно рекомендую - это тоже бесплатно! LINQPad покажет вам результаты ваших запросов L2S, будет иметь вкладку SQL для просмотра сгенерированного SQL, а также отобразит используемое лямбда-выражение (приведенный выше синтаксис запроса показан как лямбда / эквивалент расширения). Кроме того, это отличный инструмент для C # / VB.NET общего назначения (включая LINQ to Objects / XML), а также для кодирования SQL с поддержкой баз данных и многим другим.
Вот небольшой скриншот LINQPad, показывающий некоторые темы, обсуждавшиеся ранее:
Я не хотел занимать больше страниц, чем у меня уже есть, поэтому нажмите здесь, чтобы посмотреть изображение в оригинальном размере .
Если вы сделали это так далеко, поздравляю! :)