# Añadir una parte del sistema de comentarios 1 - Instalación de la base de datos

<!--category-- Entity Framework  -->
<datetime class="hidden">2024-08-26T11:53</datetime>

## El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Uno de los aspectos claves de un sitio de blog como este es un sistema de comentarios. Así queM SK1 Decidí construir uno. Esta es la primera parte de una serie de post sobre cómo construir un system de comentarios.

[TOC]

## Se está preparando la base de datos.

Como con el resto del sitio I' utilizo Postgres para los blogs; para empezar con I ' utilizco consultas recursivas para almacenar los comentariosM SK3 no me gusta esto intensamenteMSC4 pero nos lleva a salir de la puerta con un sistema de comentarios threaded MST5y yo noM ST6no tengo mucho tráficoM st7, así que itMst8 no es una gran cuestiónM St9 I\MST10\ se trasladará a un sistema más eficiente en el futuro\M st11\

### El Contexto del EF

En el sistema actual usamos un 'Comment Closure' para definir las relaciones entre los comentarios - de modo que podré apoyar los comentarios threadedM SK3 Nuestro diagrama de clase se ve asíMSC4

```mermaid
classDiagram
    class CommentEntity {
        int Id
        CommentStatus Status
        string Author
        string? HtmlContent
        string Content
        DateTime CreatedAt
        int PostId
        int? ParentCommentId
    }

    class BlogPostEntity {
        int Id
        string Title
        string Content
        DateTime CreatedAt
    }

    class CommentClosure {
        int AncestorId
        int DescendantId
        int Depth
    }

    CommentEntity "1" --> "1" BlogPostEntity : belongs to
    CommentEntity "1" --> "0..1" CommentEntity : parent
    CommentEntity "0..*" --> "0..*" CommentClosure : Ancestors
    CommentEntity "0..*" --> "0..*" CommentClosure : Descendants
    CommentClosure "1" --> "1" CommentEntity : Ancestor
    CommentClosure "1" --> "1" CommentEntity : Descendant

    class CommentStatus {
        <<enum>>
        Pending
        Approved
        Rejected
        Deleted
    }

```

Pueden ver aquí que cada uno de los `CommentEntity` El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `PostId` El Comité recomienda al Estado Parte que incluya en su próximo informe periódico datos desglosados por sexo, edad y origen étnico. `ParentCommentId` Para el comentario se adjunta 's a (si hay algúnM SK2 El `CommentClosure` se utiliza para almacenar las relaciones entre comentarios.

### El Servicio de Comentarios

El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `CommentService` es responsable de gestionar los comentarios. Tiene métodos para agregar, aprobarM SK2 rechazar , y borrar los comentarios

Nuestra interfaz para este servicio parece esta:

```csharp
public interface ICommentService
{
    Task<string> Add( int postId, int? parentCommentId, string author, string content);
    Task<List<CommentEntity>> GetForPost(int blogPostId, int page = 1, int pageSize = 10, int? maxDepth = null, CommentStatus? status = null);
    Task<List<CommentEntity>> GetDescendants(int commentId, int maxDepth = 0);

     Task<CommentEntity> Get(int commentId);
    Task<List<CommentEntity>> GetAncestors(int commentId);
    Task Delete(int commentId);
    Task Reject(int commentId);
    Task Approve(int commentId);
}
```

#### La metodología de Añadir

Nuestro método Add resume la estructura de este sistema de comentarios.

En primer lugar, inicia una nueva transacción; para las transacciones no familiares son un modo de asegurar que un grupo de operaciones se trate como una única unidad de trabajoM SK1 Si alguna de las operaciones fracasa, la transación puede ser reemplazada , y todos los cambios son anuladosMSC4 Esto es importante cuando hay múltiples operaciones que tienen que hacerse conjuntamenteMNK5 y desea asegurarse de que todas tengan éxito o fracasen conjuntamente

Luego analiza el texto de comentarios desde Markdown a HTML usando la biblioteca Markdig. Esta es una manera sencilla de permitir que los usuarios formaten sus comentarios utilizando Markdown.

Luego crea la entidad y la guarda en la base de datos.

Esto se hace en dos fases - primero el comentario es guardado, luego la fecha de cierre del comentario está guardadaM SK2 Esto se debe a que el comentario tiene que ser guardado para generar el IdMSC3 que se usa en la fecha .

Además, en el párrafo 2 de la parte dispositiva del proyecto de resolución se pide al Secretario General que le presente en su quincuagésimo séptimo período de sesiones un informe sobre los progresos realizados en la aplicación de las recomendaciones formuladas por la Comisión Consultiva en Asuntos Administrativos y de Presupuesto. `CommentClosures` que definen las relaciones entre los comentarios. Comenzamos con un autoM SK1la entrada de referencia de cierre, después, si hay un comentario parental, extraemos todos los antepasados del comentario parenta y los añademos al nuevo comentario.

Por último, nos comprometemos a realizar la transacción

```csharp
 public async Task<string> Add(int postId, int? parentCommentId, string author, string content)
  {
      await using var transaction = await context.Database.BeginTransactionAsync();
      try
      {
         var html = Markdig.Markdown.ToHtml(content);
          // Create the new comment
          var newComment = new CommentEntity()
          {
              HtmlContent = html,
              Content = content,
              CreatedAt = DateTime.UtcNow,
              PostId = postId,
              Author = author,
              Status = CommentStatus.Pending,
              ParentCommentId = parentCommentId
          };
            
          context.Comments.Add(newComment);
          await context.SaveChangesAsync();
          logger.LogInformation("Saved comment to DB");// Save to generate the new comment's Id

          // Insert into CommentClosure table
          var commentClosures = new List<CommentClosure>();

          // Self-referencing closure entry
          commentClosures.Add(new CommentClosure
          {
              AncestorId = newComment.Id,
              DescendantId = newComment.Id,
              Depth = 0
          });

          // If there is a parent comment, insert the ancestor relationships
          if (parentCommentId.HasValue)
          {
              // Fetch all ancestors of the parent comment
              var parentAncestors = await context.CommentClosures
                  .Where(cc => cc.DescendantId == parentCommentId.Value)
                  .ToListAsync();

              // Add ancestor relationships for the new comment
              foreach (var ancestor in parentAncestors)
              {
                  commentClosures.Add(new CommentClosure
                  {
                      AncestorId = ancestor.AncestorId,
                      DescendantId = newComment.Id,
                      Depth = ancestor.Depth + 1
                  });
              }

              // Add a direct parent-child relationship
              commentClosures.Add(new CommentClosure
              {
                  AncestorId = parentCommentId.Value,
                  DescendantId = newComment.Id,
                  Depth = 1
              });
          }

          context.CommentClosures.AddRange(commentClosures);
          await context.SaveChangesAsync();
          logger.LogInformation("Saved comment closure to DB");

          // Commit transaction
          await transaction.CommitAsync();
          return html;
      }
      catch (Exception e)
      {
          // Rollback transaction in case of failure
          await transaction.RollbackAsync();
          logger.LogError(e, "Failed to save comment to DB");
      }

      return string.Empty;
  }

```

#### El método GetForPost

No vamos a cubrir todo el asunto, pero `Add` El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer. `Get` son nuestras principales operaciones de CRUD para este servicio.

Como usted puede ver tenemos paging basado en el comentario de nivel superior. También tenemos un `maxDepth` parameter que nos permite limitar la profundidad del árbol de comentarios. Esto es útil si sólo queremos mostrar los comentarios de nivel superiorM SK1, o si queremos limitar la глубинa del árbol para mejorar el rendimiento .

Sin duda con este enfoque recursivo necesitamos aplicar el filtro de profundidad después de que hayamos ganado los comentarios', como podemos hacer en la consulta,. Esto es porque la profundidad de un comentario se determina por el número de antepasados que tiene , y nosotros podemosM SK5, sin dificultad, hacer una consulta en SQL

```csharp
  public async Task<List<CommentEntity>> GetForPost(int blogPostId, int page = 1, int pageSize = 10, int? maxDepth = null, CommentStatus? status = null)
  {
      // Step 1: Query the top-level comments for the specified blog post
      var query = context.Comments
          .Where(c => c.PostId == blogPostId)
          .OrderByDescending(c => c.CreatedAt)
          .Skip((page - 1) * pageSize)
          .Take(pageSize);

      // Step 2: Filter by status if provided
      if (status.HasValue)
      {
          query = query.Where(c => c.Status == status.Value);
      }

      var topLevelComments = await query
          .Include(c => c.ParentComment)
          .Include(d=>d.Descendants)
          .ToListAsync();

      // Step 4: Filter descendants based on the maxDepth
      foreach (var comment in topLevelComments)
      {
          if (maxDepth != null)
          {
              FilterDescendantsByDepth(comment, 0, maxDepth.Value);
          }
      }

      return topLevelComments;
  }

// Recursive helper method to limit the descendants based on the specified depth
  private void FilterDescendantsByDepth(CommentEntity comment, int currentDepth, int maxDepth)
  {
      if (currentDepth >= maxDepth)
      {
          // If the max depth is reached or there are no descendants, stop recursion
          comment.Descendants = new List<CommentClosure>();  // Clear further descendants beyond maxDepth
          return;
      }

      foreach (var closure in comment.Descendants.ToList())  // Iterate over a copy to prevent modification during iteration
      {
          FilterDescendantsByDepth(closure.Descendant, currentDepth + 1, maxDepth);
      }
  }

```

## El Comité recomienda al Estado Parte que adopte todas las medidas necesarias para garantizar la igualdad entre los géneros y el empoderamiento de la mujer.

Este es un sistema de comentarios con hilos simples que utiliza consultas recursivas para almacenar las relaciones entre los comentarios. Es ' no el sistema más eficiente, pero esM SK3 sencillo y funcionaMSC4 En el futuroMST5 IMSSK6 cubriré los aspectos de fondo de este sistemaMSP7 muchos HTMXMSV8 AlpineMSR9jsM SR10 y Tailwind CSSMSL11

Hasta entonces, no dude en dejar un comentario a continuación!