اشتراکها
اشتراکها
دوره 7 ساعته Docker
اشتراکها
بررسی تازههای C# 9, 10 و 11
از آنجا که برای کار با جاوا اسکریپت نیاز به درک کاملی دربارهی مفهوم حوزه کارکرد متغیرها (Scope) میباشد و نحوه فراخوانی توابع نیز نقش اساسی در این مورد بازی میکند، در این قسمت با این موارد آشنا خواهیم شد:
جاوا اسکریپت از مفهومی به نام functional scope برای تعیین حوزه متغیرها استفاده میکند و به این معنی است که با تعریف توابع، حوزه عملکرد متغیر مشخص میشود. در واقع هر متغیری که در یک تابع تعریف میشود در کلیه قسمتهای آن تابع، از قبیل If statement – for loops و حتی nested function نیز در دسترس میباشد.
اجازه دهید با مثالی این موضوع را بررسی نماییم.
function testScope() { var myTest = true; if (true) { var myTest = "I am changed!" } alert(myTest); } testScope(); // will alert "I am changed!"
function testScope() { var myTest = true; if (true) { var myTest = "I am changed!" } alert(myTest); } testScope(); // will alert "I am changed!" alert(myTest); // will throw a reference error, because it doesn't exist outside of the function
1 – متغیر myTest را در بیرون بلاک testScope() تعریف کنیم
2 – هنگام تعریف متغیر myTest، کلمه کلیدی var را حذف کنیم که این موضوع باعث میشود این متغیر در کل window قابل دسترس باشد و یا به عبارتی متغیر global میشود.
قبل از پرداختن به ادامه بحث خواندن مقاله مربوط به Closure در جاوااسکریپت توصیه میگردد .
در پایان بحث Scopeها با یک مثال نسبتا جامع اکثر این حالات به همراه خروجی را نشان میدهیم :
<script type="text/javascript"> // a globally-scoped variable var a = 1; // global scope function one() { alert(a); } // local scope function two(a) { alert(a); } // local scope again function three() { var a = 3; alert(a); } // Intermediate: no such thing as block scope in javascript function four() { if (true) { var a = 4; } alert(a); // alerts '4', not the global value of '1' } // Intermediate: object properties function Five() { this.a = 5; } // Advanced: closure var six = function () { var foo = 6; return function () { // javascript "closure" means I have access to foo in here, // because it is defined in the function in which I was defined. alert(foo); } }() // Advanced: prototype-based scope resolution function Seven() { this.a = 7; } // [object].prototype.property loses to [object].property in the lookup chain Seven.prototype.a = -1; // won't get reached, because 'a' is set in the constructor above. Seven.prototype.b = 8; // Will get reached, even though 'b' is NOT set in the constructor. // These will print 1-8 one(); two(2); three(); four(); alert(new Five().a); six(); alert(new Seven().a); alert(new Seven().b); </Script>
Function Invocation Patterns In JavaScript :
از آنجا که توابع در جاوااسکریپت به منظور 1 – ساخت اشیاء و 2 – حوزه دسترسی متغیرها(Scope) نقش اساسی ایفا میکنند بهتر است کمی درباره استفاده و نحوه فراخوانی آنها (Function Invocation Patterns) در جاوااسکریپت بحث نماییم.
در جاوااسکریپت 4 مدل فراخوانی تابع داریم که به نامهای زیر مطرح هستند:
1. Method Invocation
2. Function Invocation
3. Constructor Invocation
4. Apply And Call Invocation
در فراخوانی توابع به هر یک از روشهای بالا باید به این نکته توجه داشت که حوزه دسترسی متغیرها در جاوااسکریپت ابتدا و انتهای توابع هستند و اگر به عنوان مثال از توابع تو در تو استفاده کردیم ،حوزه شی this برای توابع داخلی تغییر خواهد کرد .این موضوع را در طی مثالهایی نشان خواهیم داد.
Method Invocation :
وقتی یک تابع قسمتی از یک شی باشد به آن متد میگوییم به عنوان مثال :
var obj = { value: 0, increment: function() { this.value+=1; } }; obj.increment(); //Method invocation
Function Invocation:
در اینحالت که از () برای فراخوانی تابع استفاده میگردد ،This به شی سراسری (global object ) اشاره میکند؛ منظور اینکه this به اجزای تابعی که فراخوانی آن انجام شده اشاره نمیکند. اجازه دهید با مثالی این موضوع را روشن کنیم
<script type="text/javascript"> var value = 500; //Global variable var obj = { value: 0, increment: function() { this.value++; var innerFunction = function() { alert(this.value); } innerFunction(); //Function invocation pattern } } obj.increment(); //Method invocation pattern <script type="text/javascript"> Result : 500
برای حل این گونه مشکلات ساختار کد نویسی ما بایستی به شکل زیر باشد :
<script type="text/javascript"> var value = 500; //Global variable var obj = { value: 0, increment: function() { var that = this; that.value++; var innerFunction = function() { alert(that.value); } innerFunction(); //Function invocation pattern } } obj.increment(); <script type="text/javascript"> Result : 1
Constructor Invocation :
در این روش برای فراخوانی تابع از کلمه new استفاده میکنیم. در این حالت یک شیء مجزا ایجاد شده و به متغیر دلخواه ما اختصاص پیدا میکند. به عنوان مثال داریم :
var Dog = function(name) { //this == brand new object ({}); this.name = name; this.age = (Math.random() * 5) + 1; }; var myDog = new Dog('Spike'); //myDog.name == 'Spike' //myDog.age == 2 var yourDog = new Dog('Spot'); //yourDog.name == 'Spot' //yourDog.age == 4
اگر از new استفاده نشود متغیر myDog ،undefined میشود.
یک مثال دیگر :
var createCallBack = function(init) { //First function return new function() { //Second function by Constructor Invocation var that = this; this.message = init; return function() { //Third function alert(that.message); } } } window.addEventListener('load', createCallBack("First Message")); window.addEventListener('load', createCallBack("Second Message"));
Apply And Call Invocation:
تمامی توابع جاوااسکریپت دارای دو متد توکار apply() و call() هستند که توسط این متدها میتوان این توابع را با context دلخواه فراخوانی کرد.
نحوه فراخوانی به شکل مقابل است :
myFunction.apply(thisContext, arrArgs); myFunction.call(thisContext, arg1, arg2, arg3, ..., argN);
مثال :
var contextObject = { testContext: 10 } var otherContextObject = { testContext: "Hello World!" } var testContext = 15; // Global variable function testFunction() { alert(this.testContext); } testFunction(); // This will alert 15 testFunction.call(contextObject); // Will alert 10 testFunction.apply(otherContextObject); // Will alert "Hello World”
یک مثال کاملتر :
var o = { i : 0, F : function() { var a = function() { this.i = 42; }; a(); document.write(this.i); } }; o.F(); Result :0
برای حل این مشکل 2 راه وجود دارد
راه اول :
var p = { i : 0, F : function() { var a = function() { this.i = 42; }; a.apply(this); document.write(this.i); } }; p.F(); Result :42
یا اینکه همانند مثالهای قبلی :
var q = { i: 0, F: function F() { var that = this; var a = function () { that.i = 42; } a(); document.write(this.i); } } q.F();
منابع :
Javascript programmer,s refrence
زیرنویسهای فارسی قسمت پنجم «Building Windows 8 Metro Apps in C# and XAML» را از اینجا و یا اینجا میتونید دریافت کنید.
لیست سرفصلهای قسمت پنجم به شرح زیر است:
Application Model 00:59:50
Metro and WinRT introduce some significant changes to the world in which applications execute.
This module describes the implications for developers.
Introduction
Application Lifecycle
Demo: Application Lifecycle
Managing State
Demo: Saving State
Splash Screens
Launching Applications
Application Manifest
Packaging
Summary
این قسمت به جزئیات نحوه اجرای برنامههای مترو میپردازد. اگر با IIS کار کرده باشید، سیکل اجرایی برنامههای مترو ویندوز 8، همانند سیکل اجرایی برنامههای ASP.NET شده است! ویندوز مختار است برنامه شما را پس از مدتی بیکاری (البته این مدت در اینجا فقط 5 ثانیه است!)، معلق کرده یا حتی خاتمه دهد و تمام اینها هم از دید کاربر نهایی مخفی است. مانند زمانیکه یک برنامه ASP.NET پس از مدتی بیکاری، توسط IIS خاتمه مییابد (از حافظه خارج میشود) و پس از مدتی با رسیدن یک درخواست جدید، یک پروسه جدید برای اجرای آن ایجاد شده و مجددا سایت شروع به کار خواهد کرد؛ اینجا هم در دنیای مترو تقریبا به همین نحو با یک برنامه رفتار میشود.
یک نکته جالب دیگر هم در برنامههای مترو وجود دارد: ترد اصلی برنامه از ترد رابط کاربری جدا شده است. برای مثال سازنده کلاس App برنامه در یک ترد و رابط کاربری برنامه در ترد مجزای دیگری اجرا میشوند.
به علاوه روشهای متفاوتی هم برای اجرای برنامههای مترو درنظر گرفته شده. دیگر فقط حالت کلیک بر روی یک برنامه سبب اجرای آن نمیشود. میتوان بر اساس اتصال یک سخت افزار خاص به سیستم یا حتی یک جستجو هم سبب اجرای برنامهای شد. برای مثال میتوانید برنامه خود را طوری طراحی کنید که نتیجهی جستجویی را در سیستم نمایش دهد.
سیستم بسته بندی برنامههای مترو نیز بسیار شبیه به فایلهای XAP برنامههای سیلورلایت است که همه چیز داخل یک فایل قرار داده میشود؛ از فایلهای تنظیمات برنامه تا فایلهای کامپایل شده و منابع مورد نیاز. البته در اینجا نامش به appx تغییر یافته است به علاوه یک فایل cer که حاوی مجوز دیجیتال اجباری توزیع برنامههای مترو در فروشگاه ویندوز است.
Dependency management is a core feature of NuGet. Managing dependencies for a single project can be easy. Managing dependencies for multi-project solutions can prove to be difficult as they start to scale in size and complexity. In situations where you manage common dependencies for many different projects, you can leverage NuGet’s central package management features to do all of this from the ease of a single location.
مدتی است که حالت READ_COMMITTED_SNAPSHOT بسیار مورد توجه واقع شده:
- در سایت Stack overflow از آن استفاده میشود (^).
- در SQL Server Azure حالت پیش فرض ایجاد دیتابیسها و تراکنشهای جدید است (^).
- در Entity framework 6 حالت پیش فرض تراکنشهای ایجاد شده، قرار گرفته است (^ ).
و ... در Oracle، تنها حالت مدیریت مسایل همزمانی است! (البته به نام MVCC، اما با همین عملکرد)
اما READ_COMMITTED_SNAPSHOT در SQL Server چیست و کاربرد آن کجا است؟
اگر استفاده گسترده و سنگینی از SQL Server داشته باشید، حتما به پیغامهای خطای deadlock آن برخوردهاید:
روش پیش فرض مدیریت مسایل همزمانی در SQL Server، حالت READ COMMITTED است. به این معنا که اگر در طی یک تراکنش مشغول به تغییر اطلاعاتی باشیم، سایر کاربران از خواندن نتیجه آن (اصطلاحا به آن Dirty read گفته میشود) منع خواهند شد؛ تا زمانیکه این تراکنش با موفقیت به پایان برسد. هرچند در این حالت سایر تراکنشها امکان ویرایش یا حذف اطلاعات را خواهند داشت. به علاوه اگر در طی این تراکنش، اطلاعاتی خوانده شوند، سایر تراکنشها تا پایان تراکنش جاری، قادر به تغییر این اطلاعات خوانده شده نخواهند بود (منشاء بروز خطاهای deadlock یاد شده در سیستمهای پرترافیک).
در SQL Server 2005 برای بهبود مقیاس پذیری SQL Server و کاهش خطاهای deadlock، مکانیزم READ_COMMITTED_SNAPSHOT معرفی گشت.
به صورت خلاصه زمانیکه که تراکنش مورد نظر تحت حالت READ COMMITTED SNAPSHOT انجام میشود، optimistic reads and pessimistic writes خواهیم داشت (خواندنهای خوشبینانه و نوشتنهای بدبینانه). در این حالت تضمین میشود که خواندن اطلاعات داخل یک تراکنش، شامل اطلاعات تغییر یافته توسط سایر تراکنشهای همزمان نخواهد بود. همچنین زمانیکه در این بین، اطلاعاتی خوانده میشود، بر روی این اطلاعات برخلاف حالت READ COMMITTED قفل قرار داده نمیشود. بنابراین تراکنشهایی که درحال خواندن اطلاعات هستند، تراکنشهای همزمانی را که در حال نوشتن اطلاعات میباشند، قفل نخواهد کرد و برعکس.
نحوه فعال سازی READ_COMMITTED_SNAPSHOT
فعال سازی READ_COMMITTED_SNAPSHOT باید ابتدا در سطح یک بانک اطلاعاتی SQL Server انجام شود:
کاری که در اینجا انجام خواهد شد، ایجاد یک snapshot یا یک کپی فقط خواندنی، از بانک اطلاعاتی کاری شما میباشد. بنابراین در این حالت، زمانیکه یک عبارت Select را فراخوانی میکنید، این خواندن، از بانک اطلاعاتی فقط خواندنی تشکیل شده، صورت خواهد گرفت. اما تغییرات بر روی دیتابیس اصلی کاری درج شده و سپس این snapshot به روز میشود.
حالت READ_COMMITTED_SNAPSHOT خصوصا برای برنامههای وبی که تعداد بالایی Read در مقابل تعداد کمی Write دارند، به شدت بر روی کارآیی و بالا رفتن سرعت و مقیاس پذیری آنها تاثیر خواهد داشت؛ به همراه حداقل تعداد deadlockهای حاصل شده.
در Entity framework وضعیت به چه صورتی است؟
EF از حالت پیش فرض مدیریت مسایل همزمانی در SQL Server یا همان حالت READ COMMITTED در زمان فراخوانی متد SaveChanges استفاده میکند.
در EF 6 این حالت پیش فرض به READ_COMMITTED_SNAPSHOT تغییر کرده است. البته همانطور که عنوان شد، پیشتر باید بانک اطلاعاتی را نیز جهت پذیرش این نوع تراکنشها آماده ساخت.
اگر از نگارشهای پایینتر از EF 6 استفاده میکنید، برای استفاده از حالت READ_COMMITTED_SNAPSHOT باید صراحتا IsolationLevel را مشخص ساخت:
- در سایت Stack overflow از آن استفاده میشود (^).
- در SQL Server Azure حالت پیش فرض ایجاد دیتابیسها و تراکنشهای جدید است (^).
- در Entity framework 6 حالت پیش فرض تراکنشهای ایجاد شده، قرار گرفته است (^ ).
و ... در Oracle، تنها حالت مدیریت مسایل همزمانی است! (البته به نام MVCC، اما با همین عملکرد)
اما READ_COMMITTED_SNAPSHOT در SQL Server چیست و کاربرد آن کجا است؟
اگر استفاده گسترده و سنگینی از SQL Server داشته باشید، حتما به پیغامهای خطای deadlock آن برخوردهاید:
Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
در SQL Server 2005 برای بهبود مقیاس پذیری SQL Server و کاهش خطاهای deadlock، مکانیزم READ_COMMITTED_SNAPSHOT معرفی گشت.
به صورت خلاصه زمانیکه که تراکنش مورد نظر تحت حالت READ COMMITTED SNAPSHOT انجام میشود، optimistic reads and pessimistic writes خواهیم داشت (خواندنهای خوشبینانه و نوشتنهای بدبینانه). در این حالت تضمین میشود که خواندن اطلاعات داخل یک تراکنش، شامل اطلاعات تغییر یافته توسط سایر تراکنشهای همزمان نخواهد بود. همچنین زمانیکه در این بین، اطلاعاتی خوانده میشود، بر روی این اطلاعات برخلاف حالت READ COMMITTED قفل قرار داده نمیشود. بنابراین تراکنشهایی که درحال خواندن اطلاعات هستند، تراکنشهای همزمانی را که در حال نوشتن اطلاعات میباشند، قفل نخواهد کرد و برعکس.
نحوه فعال سازی READ_COMMITTED_SNAPSHOT
فعال سازی READ_COMMITTED_SNAPSHOT باید ابتدا در سطح یک بانک اطلاعاتی SQL Server انجام شود:
ALTER DATABASE testDatabase SET ALLOW_SNAPSHOT_ISOLATION ON; ALTER DATABASE testDatabase SET READ_COMMITTED_SNAPSHOT ON;
حالت READ_COMMITTED_SNAPSHOT خصوصا برای برنامههای وبی که تعداد بالایی Read در مقابل تعداد کمی Write دارند، به شدت بر روی کارآیی و بالا رفتن سرعت و مقیاس پذیری آنها تاثیر خواهد داشت؛ به همراه حداقل تعداد deadlockهای حاصل شده.
در Entity framework وضعیت به چه صورتی است؟
EF از حالت پیش فرض مدیریت مسایل همزمانی در SQL Server یا همان حالت READ COMMITTED در زمان فراخوانی متد SaveChanges استفاده میکند.
در EF 6 این حالت پیش فرض به READ_COMMITTED_SNAPSHOT تغییر کرده است. البته همانطور که عنوان شد، پیشتر باید بانک اطلاعاتی را نیز جهت پذیرش این نوع تراکنشها آماده ساخت.
اگر از نگارشهای پایینتر از EF 6 استفاده میکنید، برای استفاده از حالت READ_COMMITTED_SNAPSHOT باید صراحتا IsolationLevel را مشخص ساخت:
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) { // update some tables using entity framework context.SaveChanges(); transactionScope.Complete(); }
نوع دادهی HierarchyID به همراه SQL Server 2008 برای کار با دادههایی با ساختار درختی ارائه شد. در حال حاضر هیچکدام از ORMهای موجود، پشتیبانی رسمی را از این نوع داده به عمل نمیآورند؛ اما با توجه به سورس باز بودن Entity framework، یک Fork مستقل از آن تهیه شدهاست و این نوع دادهی جدید به همراه متدهای مرتبط با آن، به این Fork اضافه شدهاند.
- اصل Fork در اینجا
- تاریخچهی این Fork غیر رسمی در اینجا
- بستهی نیوگت آن در اینجا
چون تیم EF در نگارش فعلی این کتابخانه حاضر به افزودن این نوع جدید نشدهاست، بنابراین بجای بستهی اصلی Entity framework نیاز است بستهی EntityFrameworkWithHierarchyId را نصب کنید.
یک تذکر مهم:
چون امضای دیجیتال این بسته، با امضای دیجیتال بستهی اصلی EF یکی نیست، اگر پروژهی شما صرفا از EF استفاده میکند، مشکلی نخواهید داشت. اما اگر برای مثال از ASP.NET Identity کامپایل شدهی برای کار با EF اصلی استفاده کنید، پیام یافت نشدن DLL مرتبط را دریافت خواهید کرد.
تعریفی مدلی با خاصیتی از نوع جدید HierarchyId
در اینجا مدلی را ملاحظه میکنید که از نوع دادهی جدید HierarchyId استفاده میکند. همانطور که عنوان شد این نوع در بستهی EntityFrameworkWithHierarchyId موجود است.
تعریف Context و مقدار دهی اولیهی آن
در این حالت Context برنامه به همراه تنظیمات اولیهی Migrations آن یک چنین شکلی را پیدا خواهد کرد:
در اینجا نحوهی تعریف رکوردهای جدید مبتنی بر HierarchyId را مشاهده میکنید که توسط آنها تعدادی کارمند، در یک سازمان فرضی ثبت شدهاند.
همچنین چند فیلد محاسباتی نیز بر اساس امکانات توکار SQL Server اضافه شدهاند. متدهایی مانند ToString، GetLevel، GetAncestor و امثال آن جزئی از پیاده سازی توکار SQL Server هستند. همچنین این متدها توسط کتابخانهی EntityFrameworkWithHierarchyId نیز ارائه شدهاند.
کوئری نویسی
مرتب سازی رکوردها بر اساس HierarchyId آنها
با این خروجی
یافتن یک HierarchyId خاص و سپس یافتن کلیهی فرزندان آن در یک سطح پایینتر
این کوئری را به این شکل نیز میتوان عنوان کرد: یافتن یک HierarchyId و سپس یافتن کلیه نودهایی که والدشان (GetAncestor) این HierarchyId است. عدد یک در اینجا مشخص کنندهی Level یا سطح است.
با این خروجی:
کوئریهای فوق را میتوان بجای استفاده از متد GetAncestor، با استفاده از متد IsDescendantOf به شکل زیر نیز نوشت:
با این خروجی SQL (یک کوئری بجای دو کوئری):
جابجا کردن نودها توسط متد GetReparentedValue
در کوئری ذیل، تمامی فرزندان ریشهی /1/ یافت شده و سپس والد آنها به صورت پویا تغییر داده میشود:
با این خروجی
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
HierarcyIdTests.zip
- اصل Fork در اینجا
- تاریخچهی این Fork غیر رسمی در اینجا
- بستهی نیوگت آن در اینجا
چون تیم EF در نگارش فعلی این کتابخانه حاضر به افزودن این نوع جدید نشدهاست، بنابراین بجای بستهی اصلی Entity framework نیاز است بستهی EntityFrameworkWithHierarchyId را نصب کنید.
PM> install-package EntityFrameworkWithHierarchyId
یک تذکر مهم:
چون امضای دیجیتال این بسته، با امضای دیجیتال بستهی اصلی EF یکی نیست، اگر پروژهی شما صرفا از EF استفاده میکند، مشکلی نخواهید داشت. اما اگر برای مثال از ASP.NET Identity کامپایل شدهی برای کار با EF اصلی استفاده کنید، پیام یافت نشدن DLL مرتبط را دریافت خواهید کرد.
تعریفی مدلی با خاصیتی از نوع جدید HierarchyId
public class Employee { public int Id { get; set; } [Required, MaxLength(100)] public string Name { get; set; } [Required] public HierarchyId Node { get; set; } // نوع داده جدید }
تعریف Context و مقدار دهی اولیهی آن
در این حالت Context برنامه به همراه تنظیمات اولیهی Migrations آن یک چنین شکلی را پیدا خواهد کرد:
public class MyContext : DbContext { public DbSet<Employee> Employees { get; set; } public MyContext() : base("Connection1") { this.Database.Log = log => Console.WriteLine(log); } } public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { if (context.Employees.Any()) return; context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] ADD NodePath as Node.ToString() persisted"); context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] ADD Level AS Node.GetLevel() persisted"); context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] ADD ManagerNode as Node.GetAncestor(1) persisted"); context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] ADD ManagerNodePath as Node.GetAncestor(1).ToString() persisted"); context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] ADD CONSTRAINT [UK_EmployeeNode] UNIQUE NONCLUSTERED (Node)"); context.Database.ExecuteSqlCommand( "ALTER TABLE [dbo].[Employees] WITH CHECK ADD CONSTRAINT [EmployeeManagerNodeNodeFK] " + "FOREIGN KEY([ManagerNode]) REFERENCES [dbo].[Employees] ([Node])"); context.Employees.Add(new Employee { Name = "Root", Node = new HierarchyId("/") }); context.Employees.Add(new Employee { Name = "Emp1", Node = new HierarchyId("/1/") }); context.Employees.Add(new Employee { Name = "Emp2", Node = new HierarchyId("/2/") }); context.Employees.Add(new Employee { Name = "Emp3", Node = new HierarchyId("/1/1/") }); context.Employees.Add(new Employee { Name = "Emp4", Node = new HierarchyId("/1/1/1/") }); context.Employees.Add(new Employee { Name = "Emp5", Node = new HierarchyId("/2/1/") }); context.Employees.Add(new Employee { Name = "Emp6", Node = new HierarchyId("/1/2/") }); base.Seed(context); } }
همچنین چند فیلد محاسباتی نیز بر اساس امکانات توکار SQL Server اضافه شدهاند. متدهایی مانند ToString، GetLevel، GetAncestor و امثال آن جزئی از پیاده سازی توکار SQL Server هستند. همچنین این متدها توسط کتابخانهی EntityFrameworkWithHierarchyId نیز ارائه شدهاند.
کوئری نویسی
مرتب سازی رکوردها بر اساس HierarchyId آنها
using (var context = new MyContext()) { Console.WriteLine("\ngetItems OrderByDescending(employee => employee.Node)"); var employees = context.Employees.OrderByDescending(employee => employee.Node).ToList(); foreach (var employee in employees) { Console.WriteLine("{0} {1}", employee.Id, employee.Node); } }
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Node] AS [Node] FROM [dbo].[Employees] AS [Extent1] ORDER BY [Extent1].[Node] DESC 6 /2/1/ 3 /2/ 7 /1/2/ 5 /1/1/1/ 4 /1/1/ 2 /1/ 1 /
یافتن یک HierarchyId خاص و سپس یافتن کلیهی فرزندان آن در یک سطح پایینتر
using (var context = new MyContext()) { Console.WriteLine("\nGetAncestor(1) of /1/"); var firstItem = context.Employees.Single(employee => employee.Node == new HierarchyId("/1/")); foreach (var item in context.Employees.Where(employee => firstItem.Node == employee.Node.GetAncestor(1))) { Console.WriteLine("{0} {1}", item.Id, item.Name); } }
با این خروجی:
SELECT TOP (2) [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Node] AS [Node] FROM [dbo].[Employees] AS [Extent1] WHERE cast('/1/' as hierarchyid) = [Extent1].[Node] SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Node] AS [Node] FROM [dbo].[Employees] AS [Extent1] WHERE (@p__linq__0 = ([Extent1].[Node].GetAncestor(1))) OR ((@p__linq__0 IS NULL) AND ([Extent1].[Node].GetAncestor(1) IS NULL)) -- p__linq__0: '/1/' (Type = Object) 4 Emp3 7 Emp6
کوئریهای فوق را میتوان بجای استفاده از متد GetAncestor، با استفاده از متد IsDescendantOf به شکل زیر نیز نوشت:
var list = context.Employees.Where( employee => employee.Node.IsDescendantOf(new HierarchyId("/1/")) && employee.Node.GetLevel() == 2).ToList();
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Node] AS [Node] FROM [dbo].[Employees] AS [Extent1] WHERE (([Extent1].[Node].IsDescendantOf(cast('/1/' as hierarchyid))) = 1) AND (2 = ([Extent1].[Node].GetLevel()))
جابجا کردن نودها توسط متد GetReparentedValue
در کوئری ذیل، تمامی فرزندان ریشهی /1/ یافت شده و سپس والد آنها به صورت پویا تغییر داده میشود:
var items = context.Employees.Where(employee => employee.Node.IsDescendantOf(new HierarchyId("/1/"))) .Select(employee => new { Id = employee.Id, OrigPath = employee.Node, ReparentedValue = employee.Node.GetReparentedValue(new HierarchyId("/1/"), HierarchyId.GetRoot()), Level = employee.Node.GetLevel() }).ToList(); foreach (var item in items) { Console.WriteLine("Id:{0}; OrigPath:{1}; ReparentedValue:{2}; Level:{3}", item.Id, item.OrigPath, item.ReparentedValue, item.Level); }
SELECT [Extent1].[Id] AS [Id], [Extent1].[Node] AS [Node], [Extent1].[Node].GetReparentedValue(cast('/1/' as hierarchyid), hierarchyid::GetRoot()) AS [C1], [Extent1].[Node].GetLevel() AS [C2] FROM [dbo].[Employees] AS [Extent1] WHERE ([Extent1].[Node].IsDescendantOf(cast('/1/' as hierarchyid))) = 1 Id:2; OrigPath:/1/; ReparentedValue:/; Level:1 Id:4; OrigPath:/1/1/; ReparentedValue:/1/; Level:2 Id:5; OrigPath:/1/1/1/; ReparentedValue:/1/1/; Level:3 Id:7; OrigPath:/1/2/; ReparentedValue:/2/; Level:2
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
HierarcyIdTests.zip