Domanda

Considera i seguenti requisiti per creare un'app per forum

Posta genitore

- Child Post1

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

- Child Post3

Struttura della tabella

tblpost -

  • ID postale
  • ChildPostId
  • Titolo
  • Pubblica contenuto
  • Nome utente

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

Posso recuperare questo tipo di dati utilizzando un CTE ricorsivo.Non sono sicuro che questo sia l'approccio migliore.

Domande

  • Qual è il modo migliore per recuperare questi dati utilizzando SQL?

  • Esiste un modo migliore per caricare questi dati utilizzando un ORM?

  • Se seguiamo il percorso SQL, qual è il modo migliore per caricare questi dati in una classe come mostrato di seguito:

    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;}
    }
    
  • Che ne dici di visualizzare questo tipo di dati, ad esempio utilizzando la sintassi del rasoio per una vista??

È stato utile?

Soluzione

Secondo il tuo commento sei aperto a suggerimenti su come migliorare il tuo attuale schema di database in cui fondamentalmente hai un file post_id e un child_post_id colonne per eseguire la relazione gerarchica.

Procediamo quindi:

Qual è il modo migliore per recuperare questi dati utilizzando SQL?

Ti consiglierei di dare un'occhiata a seguente articolo che illustra una tecnica molto interessante per gestire tali dati gerarchici in modo molto efficiente.Utilizza il The Modello di insieme annidato in cui definisci insiemi con nodi sinistro e destro e poi puoi costruire l'intero albero con una singola query SQL:

enter image description here

Esiste un modo migliore per caricare questi dati utilizzando un ORM?

Esistono modi per farlo utilizzando un ORM come NHibernate ed EF, ma lo lascerò per la prossima volta.Potresti considerare di dividere le tue domande in più domande SO poiché l'argomento è piuttosto ampio.Se impari come farlo utilizzando il semplice ADO.NET, acquisirai una comprensione molto migliore delle tecniche sottostanti coinvolte in modo che domani deciderai di utilizzare un tale ORM saprai già cosa cercare in ordine di query efficienti.

Che ne dici di visualizzare questo tipo di dati, diciamo utilizzando la sintassi del rasoio per una vista ??

Una volta costruito il tuo modello gerarchico è estremamente semplice.Tutto quello che devi fare è definire un modello di visualizzazione personalizzato per il file Post digitare in cui richiameresti il ​​modello di visualizzazione per tutti i post secondari.

Quindi assumendo il seguente modello:

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

e il seguente controller (in cui ovviamente ho codificato i valori ma dopo aver letto il tutorial a cui ho collegato all'inizio del mio post sarai in grado di costruire questo modello con una singola query 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);
    }
}

e poi avresti un ~/Views/Home/Index.cshtml visualizzazione:

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

e ovviamente un modello di visualizzazione corrispondente (~/Views/Home/DisplayTemplates/Post.cshtml) che sarà ricorsivo nel nostro caso per rendere l'albero completo:

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

e ovviamente il risultato finale è quello che ci si potrebbe aspettare:

enter image description here


AGGIORNAMENTO:

Come richiesto nella sezione commenti, ecco un esempio di come si potrebbe popolare il modello Post.Supponiamo che tu abbia seguito il modello di insieme annidato per progettare la tabella del database:

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

e che lo hai riempito con i post:

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

Ora potresti andarli a prendere.

Ma come sempre prima in realtà facendo qualcosa in cui descrivi cosa vuoi fare.Questo è:definisci un contratto:

public interface IPostsRepository
{
    Post GetPost();
}

Ora arrivi al facendo.In questo caso utilizzeremo ADO.NET semplice per interrogare il database e creare l'oggetto Post.Utilizzeremo un algoritmo iterativo con uno stack per costruire l'albero ma potresti anche utilizzare un algoritmo ricorsivo:

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

Ora che abbiamo questo repository potremmo mettere insieme i pezzi:

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

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

e l'ultima parte è configurare il tuo framework Dependency Injection preferito per iniettare l'implementazione desiderata del repository e poiché finora ne abbiamo solo uno sarebbe PostsRepositoryAdoNet.E se domani decidessi di passare ad un ORM non dovrai fare altro che scrivere il repository corrispondente implementando il file IPostsRepository interfaccia.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top