Frage

Berücksichtigen Sie die folgende Anforderung zum Erstellen einer Forum-App

Elternbeitrag

- Child Post1

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

- Child Post3

Tabellenstruktur

tblPost -

  • PostId
  • ChildPostId
  • Titel
  • Inhalte posten
  • Nutzername

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

Ich kann diese Art von Daten mithilfe eines rekursiven CTE abrufen.Ich bin nicht sicher, ob dies der beste Ansatz ist.

Fragen

  • Was ist der beste Weg, diese Daten mit SQL abzurufen?

  • Gibt es eine bessere Möglichkeit, diese Daten mithilfe eines ORM zu laden?

  • Wenn wir den SQL-Weg gehen, wie können diese Daten am besten in eine Klasse geladen werden, wie unten gezeigt:

    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;}
    }
    
  • Wie wäre es mit der Anzeige dieser Art von Daten, beispielsweise mit der Razor-Syntax für eine Ansicht?

War es hilfreich?

Lösung

Ihrem Kommentar zufolge sind Sie offen für Vorschläge zur Verbesserung Ihres aktuellen Datenbankschemas, in dem Sie im Grunde eins haben post_id und ein child_post_id Spalten, um die hierarchische Beziehung auszuführen.

Also lasst uns fortfahren:

Was ist der beste Weg, diese Daten mit SQL abzurufen?

Ich würde Ihnen empfehlen, einen Blick darauf zu werfen folgenden Artikel Dies veranschaulicht eine sehr schöne Technik zur Verwaltung solcher hierarchischer Daten auf sehr effiziente Weise.Es verwendet das The Verschachteltes Mengenmodell in dem Sie Mengen mit linken und rechten Knoten definieren und dann den gesamten Baum mit einer einzigen SQL-Abfrage erstellen können:

enter image description here

Gibt es eine bessere Möglichkeit, diese Daten mithilfe eines ORM zu laden?

Es gibt Möglichkeiten, dies mit einem ORM wie NHibernate und EF zu erreichen, aber ich werde dies für das nächste Mal aufheben.Sie könnten erwägen, Ihre Fragen in mehrere SO-Fragen aufzuteilen, da das Thema recht weit gefasst ist.Wenn Sie lernen, wie man dies mit einfachem ADO.NET macht, werden Sie ein viel besseres Verständnis für die zugrunde liegenden Techniken erlangen, sodass Sie morgen, wenn Sie sich für die Verwendung eines solchen ORM entscheiden, bereits wissen, worauf Sie für effiziente Abfragen achten müssen.

Wie wäre es mit der Anzeige dieser Art von Daten, die die Razor -Syntax für eine Ansicht verwenden?

Sobald Sie Ihr hierarchisches Modell erstellt haben, ist es äußerst einfach.Sie müssen lediglich eine benutzerdefinierte Anzeigevorlage für definieren Post Geben Sie ein, in welchem ​​Sie die Anzeigevorlage für alle untergeordneten Beiträge aufrufen würden.

Gehen wir also von folgendem Modell aus:

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

und den folgenden Controller (in dem ich die Werte natürlich fest codiert habe, aber nachdem Sie das Tutorial gelesen haben, auf das ich am Anfang meines Beitrags verlinkt habe, können Sie dieses Modell mit einer einzigen SQL-Abfrage erstellen):

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

und dann hättest du ein ~/Views/Home/Index.cshtml Sicht:

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

und natürlich eine entsprechende Anzeigevorlage (~/Views/Home/DisplayTemplates/Post.cshtml), die in unserem Fall rekursiv ist, um den vollständigen Baum darzustellen:

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

und natürlich ist das Endergebnis das, was man erwarten könnte:

enter image description here


AKTUALISIEREN:

Wie im Kommentarbereich gewünscht, ist hier ein Beispiel dafür, wie man das Post-Modell füllen könnte.Nehmen wir an, Sie haben die befolgt verschachteltes Mengenmodell So entwerfen Sie Ihre Datenbanktabelle:

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

und dass Sie es mit den Beiträgen gefüllt haben:

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

Jetzt könntest du sie holen.

Aber wie immer vorher eigentlich tun etwas, das Sie beschreiben, was Sie tun möchten.Das ist:Sie definieren einen Vertrag:

public interface IPostsRepository
{
    Post GetPost();
}

Jetzt kommen Sie zum tun.In diesem Fall verwenden wir einfaches ADO.NET, um die Datenbank abzufragen und das Post-Objekt zu erstellen.Wir werden einen iterativen Algorithmus mit einem Stapel verwenden, um den Baum zu erstellen, Sie können aber auch einen rekursiven Algorithmus verwenden:

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

Da wir nun dieses Repository haben, können wir die Teile zusammenfügen:

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

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

und der letzte Teil besteht darin, Ihr bevorzugtes Dependency-Injection-Framework zu konfigurieren, um die gewünschte Implementierung des Repositorys zu injizieren, und da wir bisher nur eine haben, wäre das der Fall PostsRepositoryAdoNet.Und wenn Sie sich morgen dazu entschließen, auf ein ORM umzusteigen, müssen Sie nur noch das entsprechende Repository schreiben, das das implementiert IPostsRepository Schnittstelle.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top