2012-01-16 2 views
6

Рассмотрим следующее требование для построения форум AppКак эффективно загружать данные из себя связанные таблицы

Родитель Сообщение

- Child Post1 

    - Child Post1-1 
    - Child Post1-2 
     - Child Post1-2-1 
- Child Post2 
    - Child Post 

- Child Post3 

Структура таблицы

tblPost -

  • PostId
  • ChildPostId
  • Название
  • Сообщение Содержание
  • UserName

=====================

я могу извлечь это вид данных с использованием рекурсивного CTE. Я не уверен, что это лучший подход.

Вопросы

  • Каков наилучший способ извлечь эти данные с помощью SQL?

  • Есть ли лучший способ загрузить эти данные с помощью ORM?

  • Если идти по пути SQL, что это лучший способ, чтобы загрузить эти данные в класс, как показано ниже:

    public class Post { 
        public int PostId {get;set;} 
        public string PostTitle {get;set;} 
        public string PostContent {get;set;} 
        public string PostedBy {get;set;} 
        public IEnumerable<Post> ChildPosts {get;set;} 
    } 
    
  • Как насчет отображения такого рода данные говорят, что с помощью синтаксиса бритвы для Посмотреть??

+0

Является ли схема таблиц SQL, которую вы указали, или вы открыты для улучшения предложений? –

+0

@ Darin Dimitrov - Я открыт для предложения. Я думал о подсчете количества детей, но тогда мы будем хранить расчетное значение. Возможно, XML? – Perpetualcoder

ответ

9

Согласно вашему комментарию вы открыты к предложениям об улучшении вашей текущей схемы базы данных, в которой вы в основном имеете post_id и А child_post_id столбцов для выполнения иерархических отношений.

Так давайте продолжим:

Каков наилучший способ извлечь эти данные с помощью SQL?

Я бы порекомендовал вам взглянуть на following article, который иллюстрирует очень хороший способ эффективного управления такими иерархическими данными. Он использует Nested Set Model, в котором вы определяете наборы с левыми и правыми узлами, а затем вы можете построить все дерево с помощью одного запроса SQL:

enter image description here

Есть ли лучше способ загрузить эти данные с помощью ORM?

Есть способы сделать это с использованием ORM, такого как NHibernate и EF, но я оставлю это в следующий раз.Вы можете подумать о том, чтобы разделить свои вопросы на несколько вопросов, поскольку тема довольно широкая. Если вы узнаете, как это сделать, используя простой ADO.NET, вы поймете гораздо лучшее понимание основных техник, которые задействованы, чтобы завтра вы решили использовать такой ORM, который вы уже знаете, что искать в порядке эффективных запросов.

Как насчет отображения такого рода данных, используя синтаксис бритвы для ?

Как только вы построили свою иерархическую модель, это очень просто. Все, что вам нужно сделать, это определить настраиваемый шаблон отображения для типа Post, в котором вы будете ссылаться на шаблон отображения для всех дочерних сообщений.

Так если предположить следующую модель:

public class Post 
{ 
    public int PostId { get; set; } 
    public string PostTitle { get; set; } 
    public IEnumerable<Post> ChildPosts { get; set; } 
} 

и следующий контроллер (в котором я, очевидно, жёстко значения, но после прочтения учебника я связан в начале моего поста вы сможете построить эту модель с помощью одного запроса SQL):

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     // Hardcoding the model here, but you could use the 
     // Nested Set Model technique I have linked to 
     // in order to build this model from your database 
     var post = new Post 
     { 
      PostId = 1, 
      PostTitle = "Parent Post", 
      ChildPosts = new[] 
      { 
       new Post 
       { 
        PostId = 2, 
        PostTitle = "Child Post 1", 
        ChildPosts = new[] 
        { 
         new Post 
         { 
          PostId = 3, 
          PostTitle = "Child Post 1-1", 
          ChildPosts = new[] 
          { 
           new Post 
           { 
            PostId = 4, 
            PostTitle = "Child Post 1-2-1" 
           } 
          } 
         }, 
         new Post 
         { 
          PostId = 5, 
          PostTitle = "Child Post 1-2" 
         }, 
        } 
       }, 

       new Post 
       { 
        PostId = 6, 
        PostTitle = "Child Post 2", 
        ChildPosts = new[] 
        { 
         new Post 
         { 
          PostId = 7, 
          PostTitle = "Child Post" 
         } 
        } 
       }, 
       new Post 
       { 
        PostId = 8, 
        PostTitle = "Child Post 3" 
       }, 
      } 
     }; 
     return View(post); 
    } 
} 

и тогда будет иметь вид: ~/Views/Home/Index.cshtml

@model Post 
<ul> 
    @Html.DisplayForModel() 
</ul> 

и конечно шаблон соответствующего дисплея (~/Views/Home/DisplayTemplates/Post.cshtml), который будет рекурсивным в нашем случае оказать полное дерево:

@model Post 
<li> 
    @Html.DisplayFor(x => x.PostTitle) 
    <ul> 
     @Html.DisplayFor(x => x.ChildPosts) 
    </ul> 
</li> 

и, конечно, конечный результат это то, что можно было бы ожидать:

enter image description here


UPDATE:

В ре quested в разделе комментариев - вот пример того, как можно заполнить модель Post. Давайте предположим, что вы следовали nested set model спроектировать таблицу базы данных:

CREATE TABLE posts (id int primary key, left int, right int, title nvarchar(100)); 

и что вы заполнили его посты:

INSERT INTO posts (id, left, right, title) VALUES (1, 1, 16, 'Parent Post'); 
INSERT INTO posts (id, left, right, title) VALUES (2, 2, 9, 'Child Post1'); 
INSERT INTO posts (id, left, right, title) VALUES (3, 3, 4, 'Child Post1-1'); 
INSERT INTO posts (id, left, right, title) VALUES (4, 5, 8, 'Child Post1-2'); 
INSERT INTO posts (id, left, right, title) VALUES (5, 6, 7, 'Child Post1-2-1'); 
INSERT INTO posts (id, left, right, title) VALUES (6, 10, 13, 'Child Post2'); 
INSERT INTO posts (id, left, right, title) VALUES (7, 11, 12, 'Child Post'); 
INSERT INTO posts (id, left, right, title) VALUES (8, 14, 15, 'Child Post3'); 

Теперь вы можете принести их.

Но как всегда раньше фактически делает что-то, что вы описываете, что вы хотите сделать. То есть: вы определяете контракт:

public interface IPostsRepository 
{ 
    Post GetPost(); 
} 

Теперь вы получите в делать. В этом случае мы будем использовать простой ADO.NET для запроса базы данных и построения объекта Post.Мы будем использовать итерационный алгоритм со стеком, чтобы построить дерево, но вы можете также использовать рекурсивный алгоритм:

public class PostsRepositoryAdoNet: IPostsRepository 
{ 
    private readonly string _connectionString; 
    public PostsRepositoryAdoNet(string connectionString) 
    { 
     _connectionString = connectionString; 
    } 

    private class Scalar 
    { 
     public int Depth { get; set; } 
     public Post Post { get; set; } 
    } 

    public Post GetPost() 
    { 
     using (var conn = new SqlConnection(_connectionString)) 
     using (var cmd = conn.CreateCommand()) 
     { 
      conn.Open(); 
      cmd.CommandText = 
      @" 
       SELECT p.id, p.title, (COUNT(parent.title) - 1) AS depth 
       FROM posts AS p, posts AS parent 
       WHERE p.left BETWEEN parent.left AND parent.right 
       GROUP BY p.title 
       ORDER BY p.left; 
      "; 
      using (var reader = cmd.ExecuteReader()) 
      { 
       if (!reader.Read()) 
       { 
        return null; 
       } 

       var nodes = new Stack<Post>(); 
       var scalar = FromDataReader(reader); 
       var rootNode = scalar.Post; 
       int currentDepth = 0; 
       var currentNode = rootNode; 
       while (reader.Read()) 
       { 
        var depth = reader.GetInt32(reader.GetOrdinal("depth")); 
        if (depth > currentDepth) 
        { 
         nodes.Push(currentNode); 
         currentDepth = depth; 
        } 
        else if (depth < currentDepth) 
        { 
         while (depth < currentDepth) 
         { 
          --currentDepth; 
          nodes.Pop(); 
         } 
        } 
        scalar = FromDataReader(reader); 
        currentNode = scalar.Post; 
        var p = nodes.Peek(); 
        if (p.ChildPosts == null) 
        { 
         p.ChildPosts = new List<Post>(); 
        } 
        p.ChildPosts.Add(currentNode); 
       } 
       nodes.Clear(); 
       return rootNode; 
      } 
     } 
    } 

    private Scalar FromDataReader(DbDataReader reader) 
    { 
     return new Scalar 
     { 
      Depth = reader.GetInt32(reader.GetOrdinal("depth")), 
      Post = new Post 
      { 
       PostId = reader.GetInt32(reader.GetOrdinal("id")), 
       PostTitle = reader.GetString(reader.GetOrdinal("title")) 
      } 
     }; 
    } 
} 

Теперь, когда мы имеем этот репозиторий мы могли бы принести куски вместе:

public class HomeController : Controller 
{ 
    private readonly IPostsRepository _repository; 
    public HomeController(IPostsRepository repository) 
    { 
     _repository = repository; 
    } 

    public ActionResult Index() 
    { 
     var post = _repository.GetPost(); 
     return View(post); 
    } 
} 

и последняя часть предназначена для настройки вашей любимой структуры Injection Dependency для внедрения желаемой реализации репозитория, и поскольку у нас есть только одна до сих пор, которая будет PostsRepositoryAdoNet. И если завтра вы решите переключиться на ORM, все, что вам нужно сделать, это написать соответствующий репозиторий, реализующий интерфейс IPostsRepository.

+0

Что вы рекомендуете для загрузки данных в объект после того, как я внедрил модель вложенного набора? Спасибо за статью! Reallyly хороший ответ !! Еще раз спасибо! – Perpetualcoder

+0

@Perpetualcoder, существуют различные алгоритмы, которые вы могли бы использовать для достижения этого. Я не могу сказать, что один лучше другого. Я бы рекомендовал вам книгу Дональда Кнута [* Art of Computer Programming *] (http://en.wikipedia.org/wiki/The_Art_of_Computer_Programming), чтобы узнать о различных структурах данных и алгоритмах. Я обновил свой ответ и отправил один пример в конце. –

+0

Я знаю эту книгу. Его очень похоже на то, что мне нужно загрузить несбалансированное дерево. – Perpetualcoder