خانه زبان های برنامه نویسی HierarchyId در Ef Core 8؛ کامل ترین آموزش برای مدیریت دیتاهای ساختار یافته زبان های برنامه نویسی EF Core نوشته شده توسط: تیم فنی نیک آموز تاریخ انتشار: ۰۱ آبان ۱۴۰۳ آخرین بروزرسانی: 01 آبان 1403 زمان مطالعه: 15 دقیقه ۰ (۰) HierarchyId در Ef Core 8 از امکانات جذاب برای ذخیرهسازی و مدیریت دیتاهای سازمان یافته است که شرکت سازنده آن را در نسخه هشتم Ef Core عرضه کرده است. در این مقاله هدف ما پرداختن به این ویژگی و آشنایی با نحوه نصب و استفاده از آن است. Ef Core 8 با عملکرد بینظیر و مثالزدنی خود توسعهدهندگان و مدیران پایگاه داده را شگفتزده کرد و امروزه برای توسعه برنامههای مدرن میتوان از آن استفاده کرد. از جمله امکانات جذاب Ef Core سادهسازی روند مدیریت دیتاها و ذخیرهسازی آنها است که در این مطلب با یکی از بهترین قابلیتهای آن آشنا خواهیم شد. کمی بیشتر در مورد HierarchyId در Ef Core 8 HierarchyId یک data type محسوب میشود که برای ذخیره دادههای درختی در دیتابیس استفاده میشود. در اینجا دادههای درختی اشاره به ساختاری دارد که طی آن هر مدل با مدل قبل از خود مانند ساختار یک درخت ارتباط دارد و هر یک از آنها میتوانند یک Parent یا تعدادی Child داشته باشند. بهترین مثالی که میتوان برای درک این ساختار بیان کرد، ساختار پوشهبندی داخل سیستم عامل است. یک پوشه اصلی وجود دارد که داخل آن یک یا چند پوشه دیگر در اختیار دارید. داخل هر یک از آنها مجدد میتواند تعدادی پوشه قرار داشته باشد و در نهایت هر یک از آنها ممکن است با یک یا چند فایل در ارتباط باشند. قابلیت HierarchyId در Ef Core 8، برای ما پلی بین تایپهای سیشارپ و low-level type های Sql ایجاد خواهد کرد که در اینجا ما سراغ SqlHierarchyId خواهیم رفت. در ابتدا باید ببینیم این تایپ در Sql برای چه به وجود آمده و چه کاربردی دارد. در مورد تایپ SqlHierarchyId بیشتر بدانید! برای درک بهتر HierarchyId در Ef Core 8 مطمئنا باید در مورد تایپ SqlHierarchyId اطلاعاتی را به دست آوریم. به صورت کلی، SqlHierarchyId یک تایپ دادهای در SQL است که برای نمایش موقعیت یک node در یک ساختار درختی استفاده میشود. این ساختار درختی میتواند هر دادهای شبیه به درخت مانند همان فایل یا پوشه بندیهای یک سیستم عامل که در بالا به آن اشاره کردیم باشد. هر node در این ساختار دارای یک شناسه است که به آن SqlHierarchyId گفته میشود. این شناسه از دو بخش تشکیل شده است: عمق: عمق یک node نشاندهنده فاصله آن از ریشه درخت است. عرض: عرض یک node نشان دهنده موقعیت آن در سطح خود است. با استفاده از این دو بخش، میتوان موقعیت هر node در ساختار درختی را به طور دقیق مشخص کرد. برای مثال، به ساختار پوشهبندی برگردیم. فرض میکنیم جزو گروه برنامه نویسهای ویندوز هستیم و لازم است تا ساختار پوشهبندی را پیادهسازی کنیم. ابزارهای در دسترس برای این کار دیتابیس SQL و Dot Net است. برای این که بتوانیم سادهتر و سریعتر این تسک را پیادهسازی کنیم، بیایید یک نمونه ساده را در نظر بگیریم که شامل چند پوشه است. یک پوشه اصلی داریم که root محسوب میشود. داخل این پوشه سه پوشه دیگر وجود دارند به نامهای Images و Videos و Music. داخل پوشه Images هیچ پوشهای وجود ندارد. داخل پوشه Videos دو پوشه به نامهای Movies و Series قرار دارند. داخل پوشه Music یک پوشه به اسم Yanni ایجاد شده است. یک جدول برای نمایش این ساختار درختی به صورت زیر تعریف میکنیم: HierarchyId Name Id / root ۱ /۱/ Images ۲ /۲/ Videos ۳ /۳/ Music ۴ /۲/۱/ Movies ۵ /۲/۲/ Series ۶ /۳/۱/ Yanni ۷ در جدول بالا root عمق صفر و عرض صفر دارد. یعنی ریشهای ترین قسمت این ساختار درختی است. پوشه Images در عمق یک و عرض یک قرار داد. پوشه بعدی یعنی Videos در عمق یک و در عرض دو هست. مثلا پوشه Yanni در عمق دو و در عرض یک قرار دارد. خوب تا اینجا متوجه شدیم که ساختار SqlHierarchyId به چه صورت است و چطور کار میکند. معنی عمق و عرض را هم متوجه شدیم. قبل از این که برگردیم به سیشارپ و این تسک را پیادهسازی کنیم، بیایید دیتابیس و جدولهای مورد نیازمان را فراهم کنیم. ایجاد دیتابیس و جداول برای کار با HierarchyId در Ef Core 8 برای دیتابیس، لازم است تا داکر را روی سیستم محلی نصب کنید. پس از این کار، در ترمینال دستور زیر را وارد کنید: docker pull mcr.microsoft.com/mssql/server:2022-latest این دستور باعث می شود که image مربوط به دیتابیس SQL 2022 دانلود شود. با استفاده از دستور زیر، امکان اجرای دیتابیس و تعریف پسورد و نام کاربری برای آن فراهم خواهد بود: docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Choose@Strong#Password" -p 1433:1433 --name sql1 --hostname sql1 -d mcr.microsoft.com/mssql/server:2022-latest با ساخت دیتابیس امکان مدیریت روی آن وجود دارد. در این مرحله SSMS را اجرا کرده و در دیتابیس مربوطه در سیستم محلی لاگین کنید. بعد از لاگین یک دیتابیس با نام دلخواه ایجاد میکنیم. برای این کار روی دیتابیس کلیک راست کرده و از منوی ظاهر شده روی New Database کلیک میکنیم. در پنجره باز شده نام ApplicationDb را تایپ کرده و روی Ok کلیک میکنیم. بعد از ایجاد موفقیتآمیز دیتابیس، کوئری زیر را برای ایجاد جدول Folders اجرا میکنیم: CREATE TABLE Folders ( ID INT PRIMARY KEY, Name VARCHAR(50), HierarchyID HierarchyId ) به این ترتیب یک جدول به اسم Folders ایجاد کردیم که شامل سه ستون است. ستون نخست برای ذخیره شناسه هر پوشه است. ستون بعدی نام پوشه و ستون انتهایی، شناسه پوشه در ساختار درختی پوشه هاست. ادامه کار با دات نت؛ قدرت HierarchyId در Ef Core 8 برگردیم به دات نت. یک کلاس به اسم Folders به همراه property هایی که نشان دهنده جدول ایجاد شده در دیتابیس هست، ایجاد میکنیم. public class Folders { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] public int Id { get; set; } public string Name { get; set; } public HierarchyId HierarchyId { get; set; } } این نقطه دقیقا جایی است که در ابتدا در موردش صحبت کردیم. دیتابیس Sql از تایپ HierarchyId پشتیبانی میکند و معادل همین تایپ در سیشارپ هم قابل استفاده است. اینجا دقیقا جایی است که پلی بین سیشارپ و دیتابیس ایجاد شده است. در اینجا ساختار پروژه را خیلی ساده و بدون رعایت اصول Clean Code یا Clean Architecture ایجاد میکنیم برای این که بتوانیم تنها روی بحث مورد نظر مقاله متمرکز باشیم. پس یک پروژه ساده API ایجاد کرده و پکیجهای زیر را روی آن نصب کنید: dotnet add package Microsoft.EntityFrameworkCore --version 8.0.0 dotnet add package Microsoft.EntityFrameworkCore.SqlServer.Abstractions --version 8.0.0 dotnet add package Microsoft.EntityFrameworkCore.SqlServer.HierarchyId --version 8.0.0 برای مدتهای طولانی قابلیت HierarchyId در پکیج EntityFrameworkCore.SqlServer.HierarchyId وجود داشته و به صورت غیررسمی مورد استفاده قرار گرفته است. ولی با عرضه داتنت هشت این قابلیت همزمان با ef core 8، به صورت رسمی معرفی شده است. پروژه Web Api شامل تعدادی کلاس و پوشه است. یکی از کلاسها Program.cs است و کدهایی که در Program.cs نوشته میشود معمولا برای رجیستر کردن بعضی از کلاسها و یا کانفیگ کردن بعضی از موارد برای اجرای پروژه است. این کلاس را در زیر مشاهده میکنید: var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.AddDatabase(); services.AddControllers(); services.AddSwaggerGen(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.Run(); برای تست پروژه Web Api، قابلیت Swagger را به پروژه اضافه کردیم. کلاس Program نکته قابل بیان دیگری ندارد. تنها نکتهای که در این کلاس وجود دارد، متد AddDatabase است. در اصل این متد یک تابع Extension برای اینترفیس IServiceCollection است. متد به صورت زیر است: public static class DependencyInjection { public static void AddDatabase(this IServiceCollection services) { var connectionString = "Server=localhost;Database=ApplicationDb;User Id=sa;Password=Choose@Strong#Password;TrustServerCertificate=True"; services.AddDbContext<ApplicationDbContext>(options => { options.UseSqlServer( connectionString, x => x.UseHierarchyId()); }); } } در این متد کانفیگهای مربوط به دیتابیس قرار میگیرند. این نوع کدنویسی باعث میشود تا تمامی Dependency ها و کانفیگهای مربوط به یک ماژول، در کلاسهای مشخصی دستهبندی شوند. این متد یک بخش بسیار مهم دارد. آن هم دقیقا جایی است که نوشته شده UseHierarchyId. این متد به UseSqlServer اعلام میکند کلاس HierarchyId برای تعریف شناسههای درختی استفاده شد و SQL باید شرایط آن را در نظر داشته باشد. اگر این کانفیگ به این شکل فراخوانی نشود، موقع تبدیل کدهای سیشارپ به Expression Tree و سپس به کوئریهای قابل اجرا روی دیتابیس، با خطای زیر مواجه خواهید شد: System.InvalidOperationException: The entity type 'HierarchyId' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943. پس اگر با این Exception مواجه شدید، یکی از راه حلهای آن را میدانید. کلاس ApplicationDbContext در این مثال خیلی ساده و بدون پیچیدگی در نظر گرفته شده است: public class ApplicationDbContext : DbContext { public DbSet<Folders> Folders { get; set; } public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } } کلاس ApplicationDbContext فقط از DbContext ارث بری میکند و به غیر از DbSet متد یا پارامتر دیگری ندارد. با استفاده از DbSet میگوییم که یک Entity به اسم Folders داریم که معادل یک جدول در دیتابیس است. پیاده سازی ساختار پوشه ها با HierarchyId در Ef Core 8 حالا آماده هستیم تا تسک ساختاربندی پوشههای ویندوز را پیادهسازی کنیم. برای این که به درستی به خاطر بیاوریم که قرار بود چه تسکی را پیادهسازی کنیم، به تصویر زیر نگاه کنید: قرار بود یک نمونه پوشهبندی متناسب با ساختار نمایش داده شده در تصویر بالا را پیادهسازی کنیم. برای آن که بتوانیم یک Web Api پیادهسازی کنیم، یک Controller به اسم FolderController ایجاد میکنیم: [ApiController] [Route("[Controller]")] public class FolderController(ApplicationDbContext dbContext) : ControllerBase { [HttpPost("Create")] public async Task<IActionResult> Create() { await dbContext.Database.EnsureCreatedAsync(); dbContext.Folders.AddRange( new Folders() { Id = 1, Name = "root", HierarchyId = HierarchyId.Parse("/") }, new Folders() { Id = 2, Name = "Images", HierarchyId = HierarchyId.Parse("/1/") }, new Folders() { Id = 3, Name = "Videos", HierarchyId = HierarchyId.Parse("/2/") }, new Folders() { Id = 4, Name = "Music", HierarchyId = HierarchyId.Parse("/3/") }, new Folders() { Id = 5, Name = "Movies", HierarchyId = HierarchyId.Parse("/2/1/") }, new Folders() { Id = 6, Name = "Series", HierarchyId = HierarchyId.Parse("/2/2/") }, new Folders() { Id = 7, Name = "Yanni", HierarchyId = HierarchyId.Parse("/3/1/") } ); await dbContext.SaveChangesAsync(); return Ok(); } } با استفاده از Primitive Constructor کلاس ApplicationDbContext را در کنترلر ایجاد شده Inject میکنیم. یک متد به اسم Create تعریف کردیم که از طریق HttpPost فراخوانی میشود. داخل متد، ابتدا با استفاده از تابع EnsureCreateAsync بررسی میکنیم که آیا دیتابیس ایجاد شده است یا خیر. اگر ایجاد نشده باشد، ef core دیتابیس را به همراه جدول Folders ایجاد میکند. در غیر این صورت به اجرای خط بعدی میرود. این موضوع را در نظر داشته باشید که در پروژههای بزرگ به دو دلیل نباید از متد EnsureCreateAsync استفاده کرد. دلیل نخست این است که ممکن است ایجاد یا تغییر دیتابیس از طریق لایه کنترلر یا application باعث ایجاد پیامدهای غیرقابل پیشبینی و مشکلات کنترلی شود. نکته دوم این است که اجرا این متد، زمان زیادی میبرد که برای اپلیکیشنهایی که در آن Lattency برنامه حائز اهمیت هستند، کارایی ندارد. به صورت کلی، فراخوانی لایه دیتابیس یا دامین در کنترلر، با توجه به قواعد و قوانین کدنویسی تمیز، صحیح نیست. ولی در اینجا برای ساده نگهداشتن بحث، از این قانون پیروی نکردیم و دیتابیس را در لایه کنترلر فراخوانی کردیم. داخل کنترلر و داخل اکشن Create، با استفاده از نمونهسازی از کلاس Folders، دقیقا همان ساختاری که در شکل پوشهبندی مشاهده کردیم را پیادهسازی کردیم. در آخر، تغییرات با استفاده از متد SaveChangesAsync در دیتابیس Commit شد. به عنوان مثال، گفتیم که یک پوشه با شناسه ۱ ایجاد کن که نام آن root باشد و در مسیر / قرار بگیرد. در اصل / مشخص میکند که پوشه تعریف شده، شاخه اصلی یا ریشه این ساختار درختی است. متد Hierarchy.Pars مقادیر وارد شده را به کدهای عددی که مشخص کننده جایگاه node در یک ساختار درختی است، تبدیل میکند و یک شناسه منحصر به فرد به آن اختصاص میدهد. یک بار کد را اجرا کنیم و سپس به نتیجه آن در دیتابیس نگاهی بیندازیم. برای این پروژه از Swagger استفاده شده است که بعد از اجرای موفقیتآمیز پروژه از طریق مسیر زیر قابل دسترس خواهد بود: http://localhost:5000/swagger/index.html روی Create کلیک کنید و یک درخواست به اپلیکیشن ارسال کنید: حالا زمان آن رسیده است تا ببینیم فیچر جدید ef core در عمل چه تغییری در دیتابیس و جدول Folders ایجاد میکند: همانطور که مشاهده میکنید، مسیرهایی که برای پوشهها ایجاد کردیم به صورت کدهای Hex در ستون HierarchyId ذخیره شدهاند. این کدهای HierarchyId در Ef Core 8 قابل تشخیص و تعریف هستند. بیایید در ادامه یک اکشن دیگر در کنترلر FolderController ایجاد کنیم تا بتوانیم لیست پوشههایی که داخل پوشه مثلا Videos هست را مشاهده کنیم: [HttpGet("Get")] public async Task<IActionResult> Get() { var videosHierarchyId = HierarchyId.Parse("/2/"); var videosFolders = dbContext.Folders.Where(x => x.HierarchyId.GetAncestor(videosHierarchyId.GetLevel()) == videosHierarchyId); return Ok(videosFolders); } نخستین کاری که باید انجام دهیم این است که مشخص کنیم مقدار HierarchyId برای پوشه Videos چقدر است. چون مقدار را نمیدانیم، از مسیر /۲/ و متد Parse استفاده میکنیم. سپس با استفاده از متد GetAncestor میتوان والد هر node را به دست آورد. اگر والد هر node برابر بود با HierarchyId پوشه Videos، پس node مربوطه میتواند زیر مجموعه پوشه Videos باشد. نتیجه را در شکل زیر مشاهده میکنید: ایجاد یک قابلیت بیشتر با HierarchyId در Ef Core 8 خوب تا اینجا تسک مورد نظر را پیادهسازی کردیم. پوشهها تماما ایجاد شدند و با اجرای کوئریهای مربوطه شرایط آن را مشاهده کردیم. قابلیتهای بیشتری را هم میتوان با HierarchyId در Ef Core 8 ایجاد کرد. حالا هدف ما اضافه کردن یک قابلیت جدید به این ساختار است. به این صورت که مثلا یک کاربر بتواند پوشه Yanni را از داخل پوشه Music به پوشه Images منتقل یا Cut کند. چنین تسکی را به چه نحوی میتوان پیادهسازی کرد؟ برای پیادهسازی سادهتر این تسک، فرض کنیم که شناسههای هر پوشه را در دیتابیس میدانیم. به عنوان مثال شناسه پوشه Music برابر با ۴ و برای Images برابر با ۲ است. پس در قدم اول، پوشههای والد را از دیتابیس میخوانیم: [HttpPost("Cut")] public async Task<IActionResult> Cut() { var oldFolder = await dbContext.Folders.FindAsync(4); // Music var newFolder = await dbContext.Folders.FindAsync(2); // Images var foldersList = await dbContext.Folders .Where(e => e.HierarchyId != oldFolder.HierarchyId && e.HierarchyId.IsDescendantOf(oldFolder.HierarchyId)) .ToListAsync(); foreach (var folder in foldersList) { folder.HierarchyId = folder.HierarchyId.GetReparentedValue(oldFolder.HierarchyId, newFolder.HierarchyId); } await dbContext.SaveChangesAsync(); return Ok(); } در قدم بعدی لیست پوشههایی که شناسه آنها برابر با شناسه پوشه قدیمی نیستند و فرزند پوشه قدیمی محسوب میشوند را فراخوانی میکنیم. متد IsDescendantOf زمانی مقدار true را بر میگرداند که شناسه داده شده، فرزند HierarchyId مورد نظر باشد. خوب حالا که لیست پوشهها را داریم، با استفاده از GetReparentedValue شناسههای پوشه قدیمی و جدید را با هم جابهجا میکنیم و در نهایت تغییرات را ذخیره میکنیم. اگر مجدد لیست پوشهها را از دیتابیس فراخوانی کنیم، میتوانیم تغییر رخ داده را مشاهده کنیم: [ { "id": 1, "name": "root", "hierarchyId": "/" }, { "id": 2, "name": "Images", "hierarchyId": "/1/" }, { "id": 3, "name": "Videos", "hierarchyId": "/2/" }, { "id": 4, "name": "Music", "hierarchyId": "/3/" }, { "id": 5, "name": "Movies", "hierarchyId": "/2/1/" }, { "id": 6, "name": "Series", "hierarchyId": "/2/2/" }, { "id": 7, "name": "Yanni", "hierarchyId": "/1/1/" } ] به همین سادگی با استفاده از قابلیت HierarchyId در Ef Core 8 توانستیم چنین تسکی را هم پیادهسازی کنیم. تسکی که در ابتدای امر به نظر میرسید پیادهسازی آن پیچیده و زمانبر باشد. مزایا و معایب HierarchyId در Ef Core 8 در پایان نگاهی به مزایا و معایب این فیچر بیاندازیم: مزایای استفاده از HierarchyId در Ef Core 8 فیچر HierarchyId در Ef Core 8 یک روش بهینه برای ذخیره دادههای سلسله مراتبی در SQL Server ارائه میدهد. Ef core ساختار سلسله مراتبی را در یک شناسه واحد رمزگذاری میکند که امکان ذخیره و بازیابی کارآمد دادهها را فراهم خواهد کرد. این فیچر، دارای توابع و عملگرهای داخلی برای انجام پرس و جوهای سلسله مراتبی پیچیده است که شامل شناسایی والد، فرزندان و حرکت در ساختار درختی آن هاست. معایب استفاده از HierarchyId در Ef Core 8 نوع داده HierarchyId نوع داده اختصاصی است که برای SQL Server معرفی شده است. به این معنی که با سیستمهای پایگاه داده دیگر سازگار نیست. این محدودیت در کاربرد، باعث میشود که برنامههای پیاده سازی شده در سایر پلتفرمهای پایگاه داده قابل استفاده نباشند. اگر چه HierarchyId توابع داخلی برای کار با ساختارهای درختی را ارائه میدهد، ولی بعضی از عملیات سلسله مراتبی پیچیده ممکن است نیاز به منطق کد پیچیدهتری برای مدیریت موثر داشته باشند. این میتواند پیچیدگی توسعه را افزایش دهد و در این شرایط به توسعهدهندگان با تجربهتری نیاز داشته باشد. در انتها لازم است اشاره کنیم با توجه به ماهیت خاص SQL Server، الزامات تنظیم اضافی و احتمال پیدایش کدهای پیچیده، ممکن است در بعضی از مواقع نتوان از این قابلیت استفاده کرد. توسعهدهندگان باید با توجه به الزامات خاص پروژه و چشمانداز پایگاه داده خود، معایب را با دقت ارزیابی کرده و در نهایت اقدام به استفاده از این فیچر در برنامههای خود کنند. جمع بندی استفاده از HierarchyId در Ef Core 8 یکی از ویژگیهای مهم و ضروری است که در اختیار توسعهدهندگان قرار گرفته است. با استفاده از این قابلیت میتوان بهراحتی ساختارهای دادهای مانند نمودارهای سازمانی، دستهبندی محصولات یا سیستمهای فایل را مدیریت کرد. این ویژگی به شما اجازه میدهد عملیات مختلفی مثل پیدا کردن فرزندان یا جابجایی گرهها را انجام دهید. برای استفاده از این قابلیت، ابتدا باید بسته NuGet مربوط به HierarchyId را نصب کرده و سپس با استفاده از متد UseHierarchyId() آن را در تنظیمات EF Core فعال کنید. چه رتبه ای میدهید؟ میانگین ۰ / ۵. از مجموع ۰ اولین نفر باش معرفی نویسنده مقالات 401 مقاله توسط این نویسنده محصولات 0 دوره توسط این نویسنده تیم فنی نیک آموز معرفی محصول علیرضا ارومند دوره آموزشی gRPC در ASP.NET Core 700.000 تومان 420.000 تومان مقالات مرتبط ۰۶ آذر زبان های برنامه نویسی مقایسه بهترین زبانهای برنامهنویسی ۲۰۲۵ ۰۵ آذر زبان های برنامه نویسی زبان گو (GO) و بررسی مزایا و کاربرد این زبان برنامه نویسی ۱۰ آبان زبان های برنامه نویسی عملکرد کتابخانه Turtle در پایتون و کاربرد های آن ۰۸ آبان زبان های برنامه نویسی Migration در لاراول چیست و چه کاربردهایی دارد؟ تیم فنی نیک آموز دیدگاه کاربران لغو پاسخ دیدگاه نام و نام خانوادگی ایمیل ذخیره نام، ایمیل و وبسایت من در مرورگر برای زمانی که دوباره دیدگاهی مینویسم. موبایل برای اطلاع از پاسخ لطفاً مرا با خبر کن ثبت دیدگاه Δ