ساختار نظرات یک پست را در نظر بگیرید. هر شخصی میتواند برای هر پستی نظر بگذارد و هرشخص میتواند برای نظر دیگری یک نظر جدید وارد کند. نظرات به صورت پدر و فرزندی میباشد یعنی هر نظر میتواند چندین فرزند داشته باشد ولی هر نظر فقط یک پدر دارد و در نهایت میخواهید کل نظرات ثبت شده برای یک نظر خاص را دریافت کنید. برای این کار میتوانید از WITH Hierachy استفاده کنید. برای پیاده سازی این سناریو ابتدا سه کلاس با مشخصات زیر ایجاد کرده ایم :

public class Users
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public string FullName { get; set; }
    public ICollection<Comments> ChildComments { get; set; }
    public ICollection<Posts> Posts { get; set; }
}
public class Posts
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public string Title { get; set; }
    public string Descriptions { get; set; }
    public int UserId { get; set; }
    public Users User { get; set; }
    public ICollection<Comments> ChildComments { get; set; }
}
public class Comments
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public int PostId { get; set; }
    public int UserId { get; set; }
    public string Text { get; set; }
    public int? ParentId { get; set; }
    public Users User { get; set; }
    public Posts Post { get; set; }
    public Comments Parent { get; set; }
    public ICollection<Comments> ChildComments { get; set; }
}

هر پست دارای یک کاربر است که پست را نوشته است و هر پست شامل چندین نظر است.

هر نظر دارای یک آیدی پست است که برای آن پست نظر وارد شده است و هر نظر میتواند یک ParentId داشته باشد. فیلد ParentId برای پاسخ به نظرات دیگران در نظر گرفته شده است یعنی هر کاربر بتواند برای نظرات دیگران نظر وارد کند در این صورت هرکس بخواهد برای نظر دیگری یک نظر وارد کند آیدی نظر مورد نظر در فیلد ParentId قرار میگیرد.

کلاس دیتاکانتکس : 

public class HierarchicalDataDbContext : DbContext
{
    public HierarchicalDataDbContext()
    {
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseSqlServer("Data Source=.; Initial Catalog=HierarchicalData; Integrated Security=True;");
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Users>()
            .HasData(new Models.Users
            {
                Id = 1,
                FullName = "Farhad Zamani"
            },
            new Users
            {
                Id = 2,
                FullName = "Himan falahi"
            });
        modelBuilder.Entity<Posts>()
            .HasData(new Models.Posts
            {
                Id = 1,
                UserId = 1,
                Title = "Test HierarchicalData",
                Descriptions = "HierarchicalData"
            });
        modelBuilder.Entity<Comments>()
            .HasOne(a => a.User)
            .WithMany(a => a.ChildComments)
            .HasForeignKey(a => a.UserId)
            .IsRequired()
            .OnDelete(DeleteBehavior.Restrict);
        modelBuilder.Entity<Comments>()
            .HasOne(a => a.Parent)
            .WithMany(a => a.ChildComments)
            .HasForeignKey(a => a.ParentId)
            .IsRequired(false)
            .OnDelete(DeleteBehavior.Restrict);
        modelBuilder.Entity<Comments>()
            .HasData(new Models.Comments
            {
                Id = 1,
                ParentId = null,
                PostId = 1,
                Text = "First comments",
                UserId = 1
            }, new Models.Comments
            {
                Id = 2,
                ParentId = 1,
                PostId = 1,
                Text = "Reply to first comments",
                UserId = 2
            }, new Models.Comments
            {
                Id = 3,
                ParentId = null,
                PostId = 1,
                Text = "Second comment",
                UserId = 1
            }, new Models.Comments
            {
                Id = 4,
                ParentId = 2,
                PostId = 1,
                Text = "Reply to previous comment",
                UserId = 2
            }, new Models.Comments
            {
                Id = 5,
                ParentId = 3,
                PostId = 1,
                Text = "Reply to second comment",
                UserId = 2
            });
    }
    public DbSet<Users> Users { get; set; }
    public DbSet<Posts> Posts { get; set; }
    public DbSet<Comments> Comments { get; set; }
}

در متد OnModelCreating مقداردهی اولیه برای کلاس های نوشته شده بالا را انجام داده ایم. دو کاربر ایجاد کرده ایم همراه با یک پست که برای آن پست تعدادی نظر وارد کرده اند و در متد OnConfiguring رشته اتصال به دیتابیس را مشخص کرده ایم. بعد از وارد کردن Add-Migrations و Update-Database بانک اطلاعاتی مقداردهی اولیه میشود. در ادامه باید کوئری مربوط به دریافت کل نظرات ثبت شده برای یک نظر خاص را بنویسیم. برای نوشتن کوئری  از Dapper استفاده کرده ایم.

static async Task Main(string[] args)
{
    using (SqlConnection connection = new SqlConnection("Data Source=.; Initial Catalog=HierarchicalData; Integrated Security=True;"))
    {
        DynamicParameters parameters = new DynamicParameters();
        parameters.Add("CommentId", 1, DbType.Int32);
        string query = @"
                    WITH Hierachy(Id, ParentId,UserId,Text) AS (
                                      SELECT Id, ParentId,UserId,Text
                                          FROM Comments
                                      UNION ALL
                                      SELECT h.Id, comment.ParentId,h.UserId,h.Text
                                      FROM Comments comment
                                      INNER JOIN Hierachy h ON comment.Id = h.ParentId
                              )
	                 Select * from Comments where Id in 
	                 ( SELECT Id
	                 	 FROM Hierachy h
	                 	 WHERE h.ParentId = @CommentId)";
        var affectedRows = (await connection.QueryAsync<Comments>(query, parameters)).AsEnumerable();
        foreach (var comment in affectedRows)
        {
            Console.WriteLine($"{comment.Id}\t{comment.Text}");
        }
        Console.ReadLine();
    }
}

در نهایت اگر پروژه را اجرا کنید رکوردهای زیر را مشاهده خواهید کرد : 

2       Reply to first comments
4       Reply to previous comment

نظر با آیدی 2 برای نظر آیدی 1 ثبت شده است و نظر با آیدی 4 برای نظر آیدی 2 ثبت شده است و در این کوئری تمامی زیرمجموعه های نظر با آیدی 1 برگردانده شده است.

پکیج های استفاده شده : 

<PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.7">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

کدهای این مقاله را میتوانید از گیت هاب دانلود کنید ;)

Powered by Froala Editor