اشتراکها
نظرات مطالب
Soft Delete در Entity Framework 6
اگر بخواهیم این کوئری رو غیر فعال کنیم به چه روشی هست؟
اینکه به مدیر سیستم رکوردهای حذف شده رو نمایش بدیم که بتونه حذف کنه یا از طریق ایجاد زمانبندی برای حذف اطلاعات
LINQ یا همان Language-Integrated Query، یک زبان سادهی کوئری نوشتن یکپارچهی با دات نت است. به کمک آن میتوان اعمال پیچیدهای را بر روی اشیاء، به زبانی ساده بیان کرد و امروزه تقریبا توسط تمام توسعه دهندگان دات نت مورد استفاده قرار میگیرد. اما ... این سادگی، بهایی را نیز به همراه دارد: کمتر بودن سرعت اجرا و همچنین افزایش مصرف حافظه. با توجه به گستردگی استفادهی از LINQ، اگر بهبودی در این زمینه حاصل شود، بر روی کارآیی تمام برنامههای دات نتی تاثیر خواهد گذاشت و این امر در دات نت 7 محقق شدهاست. کارآیی متدهای LINQ to Objects در دات نت 7 (مانند متدهای Enumerable.Max, Enumerable.Min, Enumerable.Average, Enumerable.Sum) به شدت افزایش یافته و این افزایش گاهی حتی بیشتر از 10 برابر نسبت به نگارشهای قبلی دات نت است؛ اما چگونه به چنین کارآیی رسیدهاند؟
تدارک یک آزمایش برای بررسی میزان افزایش کارآیی متدهای LINQ در دات نت 7
در ادامه یک آزمایش سادهی بررسی کارآیی متدهای Enumerable.Max, Enumerable.Min, Enumerable.Average, Enumerable.Sum را با استفاده از کتابخانهی معروف BenchmarkDotNet مشاهده میکنید:
برای آزمایش آن، یکبار target framework پروژه را بر روی net6.0 و بار دیگر بر روی net7.0 قرار داده و برنامه را اجرا میکنیم. خلاصهی مفهومی نتایج حاصل به صورت زیر است که ... شگفتانگیز هستند!
در مورد کار با آرایهها:
- زمان اجرای یافتن Min در آرایههای کوچک، در دات نت 7، نسبت به دات نت 6، حدودا 10 برابر کاهش یافته و اگر این آرایه بزرگتر شود و برای مثال حاوی 10 هزار المان باشد، این زمان 20 برابر کاهش یافتهاست.
- این کاهش زمانها برای سایر متدهای LINQ نیز تقریبا به همین صورت است؛ منها متد Sum که اندازهی آرایه، تاثیری را بر روی نتیجهی نهایی ندارد.
- همچنین در دات نت 7، با فراخوانی متدهای LINQ، افزایش حافظهای مشاهده نمیشود.
در مورد کار با لیستها:
- در دات نت 6، اعمال صورت گرفتهی توسط LINQ بر روی آرایهها، نسبت به لیستها، همواره سریعتر است.
- در دات نت 7 هم در مورد مجموعههای کوچک، وضعیت همانند دات نت 6 است. اما اگر مجموعهها بزرگتر شوند، تفاوتی بین مجموعهها و آرایهها وجود ندارد و حتی وضعیت مجموعهها بهتر است: کارآیی کار با لیستها 32 برابر بیشتر شدهاست!
اما چگونه در دات نت 7، چنین بهبود کارآیی خیرهکنندهای در متدهای LINQ حاصل شدهاست؟
برای بررسی چگونگی بهبود کارآیی متدهای LINQ در دات نت 7 باید به نحوهی پیاده سازی آنها در نگارشهای مختلف دات نت مراجعه کرد. برای مثال پیاده سازی متد الحاقی Min تا دات نت 6 به صورت زیر است:
این متد نسبتا سادهاست. یک IEnumerable را دریافت کرده و سپس با استفاده از متد MoveNext، مقدار فعلی را با مقدار بعدی مقایسه میکند. در این مقایسه، کوچکترین مقدار ذخیره میشود تا در نهایت به انتهای مجموعه برسیم.
اما ... پیاده سازی این متد در دات نت 7 متفاوت است:
در اینجا در ابتدا سعی میشود تا یک ReadOnlySpan از مجموعهی ارائه شده، تهیه شود. اگر این کار میسر نشد، کدهای همان روش قبلی دات نت 6 که توضیح داده شد، اجرا میشود. البته در آزمایشی که ما تدارک دیدیم، چون از لیستها و آرایهها استفاده شده بود، همواره امکان تهیهی یک ReadOnlySpan از آنها میسر است. بنابراین به قسمت اجرایی همانند دات نت 6 نمیرسیم.
اما ... ReadOnlySpan چیست؟ نوعهای Span و ReadOnlySpan، یک ناحیهی پیوستهی مدیریت شده و مدیریت نشدهی حافظه را بیان میکنند. یک Span از نوع ref struct است؛ یعنی تنها میتواند بر روی stack قرار گیرد که مزیت آن، عدم نیاز به تخصیص حافظهی اضافی و بهبود کارآیی است. همچنین ساختار داخلی Span در سی شارپ 11 اندکی تغییر کردهاست که در آن از ref fields جهت دسترسی امن به این ناحیهی از حافظه استفاده میشود. پیشتر از نوع داخلی ByReference برای اشاره به ابتدای این ناحیهی از حافظه استفاده میشد که به همراه بررسی امنیتی در این باره نبود.
پس از دریافت ReadOnlySpan، به سطر زیر میرسیم:
که بررسی میکند آیا سخت افرار فعلی از قابلیتهای SIMD برخوردار است یا خیر؟ اگر بله، اینبار با استفاده از ریاضیات برداری شتاب یافتهی توسط سخت افزار، محاسبات را انجام میدهد:
بنابراین به صورت خلاصه در دات نت 7 با استفاده از بکارگیری نوعهای ویژهی Span و نوعهای برداری شتابیافتهی توسط اکثر سخت افزارهای امروزی، سبب بهبود قابل ملاحظهی کارآیی متدهای LINQ شدهاند.
تدارک یک آزمایش برای بررسی میزان افزایش کارآیی متدهای LINQ در دات نت 7
در ادامه یک آزمایش سادهی بررسی کارآیی متدهای Enumerable.Max, Enumerable.Min, Enumerable.Average, Enumerable.Sum را با استفاده از کتابخانهی معروف BenchmarkDotNet مشاهده میکنید:
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System.Collections.Generic; using System.Linq; [MemoryDiagnoser(displayGenColumns: false)] public partial class Program { static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); [Params (10, 10000)] public int Size { get; set; } private IEnumerable<int> items; [GlobalSetup] public void Setup() { items = Enumerable.Range(1, Size).ToArray(); } [Benchmark] public int Min() => items.Min(); [Benchmark] public int Max() => items.Max(); [Benchmark] public double Average() => items.Average(); [Benchmark] public int Sum() => items.Sum(); }
در مورد کار با آرایهها:
- زمان اجرای یافتن Min در آرایههای کوچک، در دات نت 7، نسبت به دات نت 6، حدودا 10 برابر کاهش یافته و اگر این آرایه بزرگتر شود و برای مثال حاوی 10 هزار المان باشد، این زمان 20 برابر کاهش یافتهاست.
- این کاهش زمانها برای سایر متدهای LINQ نیز تقریبا به همین صورت است؛ منها متد Sum که اندازهی آرایه، تاثیری را بر روی نتیجهی نهایی ندارد.
- همچنین در دات نت 7، با فراخوانی متدهای LINQ، افزایش حافظهای مشاهده نمیشود.
در مورد کار با لیستها:
- در دات نت 6، اعمال صورت گرفتهی توسط LINQ بر روی آرایهها، نسبت به لیستها، همواره سریعتر است.
- در دات نت 7 هم در مورد مجموعههای کوچک، وضعیت همانند دات نت 6 است. اما اگر مجموعهها بزرگتر شوند، تفاوتی بین مجموعهها و آرایهها وجود ندارد و حتی وضعیت مجموعهها بهتر است: کارآیی کار با لیستها 32 برابر بیشتر شدهاست!
اما چگونه در دات نت 7، چنین بهبود کارآیی خیرهکنندهای در متدهای LINQ حاصل شدهاست؟
برای بررسی چگونگی بهبود کارآیی متدهای LINQ در دات نت 7 باید به نحوهی پیاده سازی آنها در نگارشهای مختلف دات نت مراجعه کرد. برای مثال پیاده سازی متد الحاقی Min تا دات نت 6 به صورت زیر است:
public static int Min(this IEnumerable<int> source) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } int value; using (IEnumerator<int> e = source.GetEnumerator()) { if (!e.MoveNext()) { ThrowHelper.ThrowNoElementsException(); } value = e.Current; while (e.MoveNext()) { int x = e.Current; if (x < value) { value = x; } } } return value; }
اما ... پیاده سازی این متد در دات نت 7 متفاوت است:
public static int Min(this IEnumerable<int> source) => MinInteger(source); private static T MinInteger<T>(this IEnumerable<T> source) where T : struct, IBinaryInteger<T> { T value; if (source.TryGetSpan(out ReadOnlySpan<T> span)) { if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2) { .... // Optimized implementation return ....; } } .... //Implementation as in .NET 6 }
اما ... ReadOnlySpan چیست؟ نوعهای Span و ReadOnlySpan، یک ناحیهی پیوستهی مدیریت شده و مدیریت نشدهی حافظه را بیان میکنند. یک Span از نوع ref struct است؛ یعنی تنها میتواند بر روی stack قرار گیرد که مزیت آن، عدم نیاز به تخصیص حافظهی اضافی و بهبود کارآیی است. همچنین ساختار داخلی Span در سی شارپ 11 اندکی تغییر کردهاست که در آن از ref fields جهت دسترسی امن به این ناحیهی از حافظه استفاده میشود. پیشتر از نوع داخلی ByReference برای اشاره به ابتدای این ناحیهی از حافظه استفاده میشد که به همراه بررسی امنیتی در این باره نبود.
پس از دریافت ReadOnlySpan، به سطر زیر میرسیم:
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2)
private static T MinInteger<T>(this IEnumerable<T> source) where T : struct, IBinaryInteger<T> { .... if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count * 2) { var mins = new Vector<T>(span); index = Vector<T>.Count; do { mins = Vector.Min(mins, new Vector<T>(span.Slice(index))); index += Vector<T>.Count; } while (index + Vector<T>.Count <= span.Length); value = mins[0]; for (int i = 1; i < Vector<T>.Count; i++) { if (mins[i] < value) { value = mins[i]; } } .... }
با توجه به ماهیت چندسکویی NET 5.، در اکثر سیستمهای ویندوزی، سرویس بومی سازی، بر اساس استاندارد NLS کار میکند، اما در سیستمهای لینوکسی و مبتنی بر یونیکس، این استاندارد از نوع ICU است (و وجود و تنظیم آنها خارج از NET. و توسط سیستم عامل مدیریت میشود). جهت یکدست سازی این دو نوع سیستم بومی سازی در دات نت، از نگارش 5 آن به بعد، استاندارد ICU که به صورت گستردهتری مورد پذیرش قرار گرفتهاست، استاندارد بومی سازی پیشفرض دات نت درنظر گرفته میشود؛ مگر اینکه سیستم عاملی آنرا پشتیبانی نکند.
کدام نگارش از ویندوز، از ICU پشتیبانی میکند؟
تمام ویندوزهای پس از Windows 10 May 2019 Update، به همراه icu.dll، به عنوان جزء استاندارد سیستم عامل هستند. بنابراین دات نت 5 و نگارشهای پس از آن، در این سیستم عاملها، از سرویس بومی سازی ICU استفاده خواهند کرد؛ اما اگر از نگارشهای پیشین ویندوز استفاده میکنید، به اجبار به سیستم NLS سوئیچ خواهد شد.
تاثیر ICU بر برنامههای دات نت 5 به بعد
قطعه کد زیر را درنظر بگیرید:
در نگارشهای پیش از 5 دات نت، خروجی کدهای فوق، عدد 6 است؛ اما ... اما ... (!) از زمان دات نت 5 به بعد، خروجی آن «منهای یک» است! البته به شرطی که آخرین به روز رسانی ویندوز 10 را نصب کرده باشید؛ یعنی حداقل Windows 10 May 2019 Update را داشته باشید.
حالت «پیشفرض» جستجو و مقایسهی رشتهها در دات نت 5 به بعد، یک مقایسهی مبتنی بر «دستورات زبانی» بر اساس فرهنگ تنظیم شدهی در Thread جاری برنامهاست (یا همان System.Threading.Thread.CurrentThread.CurrentCulture).
چرا متدهای کار بر روی رشتهها در دات نت 5 به بعد، نسبت به نگارشهای قبلی متفاوت عمل میکنند؟
زمانیکه متدی مانند IndexOf فراخوانی میشود، هدف عمدهی برنامهنویسها، یک جستجوی Ordinal است (یعنی مقایسهی کاراکتر به کاراکتر؛ بدون درنظر گرفتن نکات زبانی و بومی)؛ اما فراموش میکنند که این متدها دارای پارامتر دومی هم هستند که از نوع StringComparison است و سالها است که توصیه میشود این پارامتر را هم به صورت صریحی مقدار دهی کنید تا هدف خود را از نوع جستجو دقیقا مشخص نمائید. از زمان دات نت 5 به بعد، اگر این پارامتر را مشخص نکنید، جستجوی صورت گرفته یک رفتار culture-specific را خواهد داشت و نه Ordinal. از این لحاظ مقایسهی رشتهها توسط استانداردهای ICU و NLS، بر اساس پیاده سازیهای مختلف زبانشناسی، خروجیهای یکسانی را ارائه نمیدهند و به همین جهت است که اینبار خروجی منهای یک را دریافت میکنیم.
یک نکته: خروجی قطعه کد فوق در سیستمهای لینوکسی که از .NET Core 2x - 3x. هم استفاده میکنند، دقیقا منهای یک است؛ چون پیشفرض بومی سازی آنها نیز ICU است.
چگونه میتوان به همان حالت پیشین مقایسهی رشتهها در NET. بازگشت؟
مایکروسافت بستهی نیوگت Microsoft.CodeAnalysis.FxCopAnalyzers را جهت گوشزد کردن نکتهی ذکر صریح StringComparison، به روز رسانی کردهاست. بنابراین بهتر است تا آنرا به پروژهی خود اضافه کنید. در این حالت اخطارهای مناسبی را جهت یافتن قسمتهای مشکلدار برنامهی خود دریافت میکنید. برای مثال برای اینکه در قطعه کد فوق به همان پاسخ متداول 6 برسیم، تنها کافیاست پارامتر دوم StringComparison را ذکر کنیم:
و یا حتی میتوانید فایل csproj پروژهی خود را ویرایش کرده و یک سطر زیر را به آن اضافه کنید:
در این حالت کل برنامهی شما بدون هیچ تغییری مانند قبل کار کرده و از سیستم NLS استفاده میشود.
کدام متدهای کار با رشتهها در دات نت 5، تحت تاثیر این تغییرات قرار گرفتهاند؟
اگر از متدهای زیر در برنامههای خود استفاده میکنید، نکتهی ذکر پارامتر StringComparison.Ordinal را فراموش نکنید:
سؤال: اگر متدی پارامتر دوم StringComparison را نداشت چطور؟
اگر به ماخذ «Behavior changes when comparing strings on .NET 5» مراجعه کنید، در انتهای آن جدولی را ارائه داده که دو سطر اول آن، به صورت زیر است:
در این جدول، هر متدی که رفتار پیشفرض آن از نوع CurrentCulture است، تحت تاثیر قرار گرفتهاست و متدی مانند string.Contains که رفتار پیشفرض آن Ordinal است، از این تغییرات مصون است و نیازی به تغییری ندارد.
برای مطالعهی بیشتر:
Behavior changes when comparing strings on .NET 5+
.NET globalization and ICU.
Globalization breaking changes
بحث و گفتگویی در این مورد
کدام نگارش از ویندوز، از ICU پشتیبانی میکند؟
تمام ویندوزهای پس از Windows 10 May 2019 Update، به همراه icu.dll، به عنوان جزء استاندارد سیستم عامل هستند. بنابراین دات نت 5 و نگارشهای پس از آن، در این سیستم عاملها، از سرویس بومی سازی ICU استفاده خواهند کرد؛ اما اگر از نگارشهای پیشین ویندوز استفاده میکنید، به اجبار به سیستم NLS سوئیچ خواهد شد.
تاثیر ICU بر برنامههای دات نت 5 به بعد
قطعه کد زیر را درنظر بگیرید:
string s = "Hello\r\nworld!"; int idx = s.IndexOf("\n"); Console.WriteLine(idx);
حالت «پیشفرض» جستجو و مقایسهی رشتهها در دات نت 5 به بعد، یک مقایسهی مبتنی بر «دستورات زبانی» بر اساس فرهنگ تنظیم شدهی در Thread جاری برنامهاست (یا همان System.Threading.Thread.CurrentThread.CurrentCulture).
چرا متدهای کار بر روی رشتهها در دات نت 5 به بعد، نسبت به نگارشهای قبلی متفاوت عمل میکنند؟
زمانیکه متدی مانند IndexOf فراخوانی میشود، هدف عمدهی برنامهنویسها، یک جستجوی Ordinal است (یعنی مقایسهی کاراکتر به کاراکتر؛ بدون درنظر گرفتن نکات زبانی و بومی)؛ اما فراموش میکنند که این متدها دارای پارامتر دومی هم هستند که از نوع StringComparison است و سالها است که توصیه میشود این پارامتر را هم به صورت صریحی مقدار دهی کنید تا هدف خود را از نوع جستجو دقیقا مشخص نمائید. از زمان دات نت 5 به بعد، اگر این پارامتر را مشخص نکنید، جستجوی صورت گرفته یک رفتار culture-specific را خواهد داشت و نه Ordinal. از این لحاظ مقایسهی رشتهها توسط استانداردهای ICU و NLS، بر اساس پیاده سازیهای مختلف زبانشناسی، خروجیهای یکسانی را ارائه نمیدهند و به همین جهت است که اینبار خروجی منهای یک را دریافت میکنیم.
یک نکته: خروجی قطعه کد فوق در سیستمهای لینوکسی که از .NET Core 2x - 3x. هم استفاده میکنند، دقیقا منهای یک است؛ چون پیشفرض بومی سازی آنها نیز ICU است.
چگونه میتوان به همان حالت پیشین مقایسهی رشتهها در NET. بازگشت؟
مایکروسافت بستهی نیوگت Microsoft.CodeAnalysis.FxCopAnalyzers را جهت گوشزد کردن نکتهی ذکر صریح StringComparison، به روز رسانی کردهاست. بنابراین بهتر است تا آنرا به پروژهی خود اضافه کنید. در این حالت اخطارهای مناسبی را جهت یافتن قسمتهای مشکلدار برنامهی خود دریافت میکنید. برای مثال برای اینکه در قطعه کد فوق به همان پاسخ متداول 6 برسیم، تنها کافیاست پارامتر دوم StringComparison را ذکر کنیم:
int idx = s.IndexOf("\n", StringComparison.Ordinal);
و یا حتی میتوانید فایل csproj پروژهی خود را ویرایش کرده و یک سطر زیر را به آن اضافه کنید:
<ItemGroup> <RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" /> </ItemGroup>
کدام متدهای کار با رشتهها در دات نت 5، تحت تاثیر این تغییرات قرار گرفتهاند؟
اگر از متدهای زیر در برنامههای خود استفاده میکنید، نکتهی ذکر پارامتر StringComparison.Ordinal را فراموش نکنید:
System.String.Compare System.String.EndsWith System.String.IndexOf System.String.StartsWith System.String.ToLower System.String.ToLowerInvariant System.String.ToUpper System.String.ToUpperInvariant System.Globalization.TextInfo (most members) System.Globalization.CompareInfo (most members) System.Array.Sort (when sorting arrays of strings) System.Collections.Generic.List<T>.Sort() (when the list elements are strings) System.Collections.Generic.SortedDictionary<TKey,TValue> (when the keys are strings) System.Collections.Generic.SortedList<TKey,TValue> (when the keys are strings) System.Collections.Generic.SortedSet<T> (when the set contains strings)
سؤال: اگر متدی پارامتر دوم StringComparison را نداشت چطور؟
اگر به ماخذ «Behavior changes when comparing strings on .NET 5» مراجعه کنید، در انتهای آن جدولی را ارائه داده که دو سطر اول آن، به صورت زیر است:
API Default behavior Remarks string.Compare CurrentCulture
برای مطالعهی بیشتر:
Behavior changes when comparing strings on .NET 5+
.NET globalization and ICU.
Globalization breaking changes
بحث و گفتگویی در این مورد
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- محاسبه کاربران آنلاین در ASP.NET
- نحوه جابجایی tempdb در SQL Server 2005 و ضرورت آن
- Firefox فارسی در دست تهیه
- تست نرمافزار در Agile Software Development
- محافظت از فرمهای اینترنتی با ریکپچا
- تبادل NTFS و رجیستری
- غیر فعال کردن اشتراک درایوهای مخفی در ویندوز
- تحت نظر قرار دادن پوشههای سیستمی در سی شارپ
- نحوه استفاده از jQuery در ASP.Net
- پلاگین کیبورد مجازی فارسی برای jQuery
- بکارگیری ویژگیهای CSS3 در طراحی وب
- ۹ قابلیت پنهان گوگل که باید بدانید
- فیدبرنر و گوگل
- پشتیبانی چیست و چگونه باید انجام شود
- دانشآموزان ایرانی در IT از معلمان خود باسوادترند
- امنیت وبلاگ وردپرس خود را بالا ببرید
- سرعت تبدیل اعداد انگلیسی به فارسی
- آیا پنگوئن میتواند پنجره را بشکند؟ و نصیحت لینوکسی
- دل پر خون کاربران از عملکرد وزارت ICT در حوزه اینترنت
- انتخاب افقی قسمتی از کد!
Visual Studio
ASP. Net
- از سیر تا پیاز نحوهی کارکرد سشن در ASP.Net
- آشنایی با برنامه Fiddler
- jQuery, JSON, and ASP.NET
- ASP کلاسیک مرده است!
- دستکاری ظاهر شیرپوینت با استفاده از jQuery
- بررسی سایت whitehouse.gov
طراحی و توسعه وب
- ایجاد گوشههای گرد
- استفاده از فایرفاکس برای غلط یابی یک صفحه وب
- کتابخانه DateJs (و اگر وقت کردید سری به این پست بزنید)
- 45 تکنیک JQuery
- نحوه نمایش لینکهای خود را بهبود بخشید
- Formy CSS Form Framework
- Top list of ajax upload scripts
اسکیوال سرور
- به روز رسانی سوم اس کیوال سرور 2008 ، لیست موارد فیکس شده، دریافت
- طراحی جدید سایت اس کیوال سرور 2008
- آشنایی با لاگ فایلها در SQL Server
- تولید اعداد منحصربفرد در اس کیوال سرور
- از sp_detach_db استفاده نکنید!
سی شارپ
عمومی دات نت
- UTF8Encoding.Default != Encoding.UTF8
- مقایسه Nhibernate و Linq
- مقایسه Entity Framework و LINQ to SQL
- بررسی اندازه یک رشته در دات نت
- مونو و Android
- Google Maps for Windows Forms
- برگههای تقلب دات نت
- دایرکتوری کتاب خانههای ثبت وقایع در دات نت
ویندوز
مسایل اجتماعی و انسانی برنامه نویسی
- اثر رکود اقتصادی بر بازار کار برنامه نویسی
- دلایل استعفای تعدادی از کارمندان گوگل
- تهیهی برنامههایی با مقیاس گسترده در چند روز بجای چند هفته
- از دست همکاران خود خسته شدهاید؟!
- هنر مصاحبه کردن
متفرقه
وبلاگها و سایتهای ایرانی
Visual Studio
امنیت
- رمزنگاری کوانتمی و شبکهای رمزنگاری شده با این روش
ASP. Net
طراحی وب
اسکیوال سرور
به روز رسانیها
ابزارها
- نگارش جدید برنامه RSS Bandit . (برنامهای سورس باز نوشته شده با سی شارپ)
- Visual Round Trip Analyzer (استفاده از NetMon API)
سیشارپ
- Interactive GUI Shell از توسعه دهندگان تیم Mono
عمومی دات نت
- آشنایی با کلاس CommaDelimitedStringCollection در دات نت فریم ورک
- مونو و دات نت گزارشی از PDC2008
CPP
دلفی
- CompilerPlugin برای دلفی 2009. (توسط آن میتوان پروژههای دلفی 2007 را با دلفی 2009 نیز کامپایل کرد)
- نسخه جدید CnPack منتشر شد. (با پشتیبانی دلفی 5 تا 2009)
ویندوز
- آنالیز crash dumps ویندوز . (آیا میدانید صفحات آبی ویندوز را چگونه باید تفسیر کرد؟)
Office
- آشنایی با یک سری از اصطلاحات outlook 2007 برای برنامه نویسها. (اگر قصد داشته باشید یک add-in را برای outlook 2007 با استفاده از امکانات VSTO توسعه دهید، آشنایی با این اصطلاحات بسیار ضروری خواهد بود)
متفرقه
استفاده از DbProviderFactory امکان اتصال به دیتابیسهای مختلف با یک کد واحد را برای شما فراهم میسازد،بطوریکه اگر بخواهید برنامه ای بنویسید که قابلیت اتصال به Oracle و SqlServer و دیگر دیتابیسها را داشته باشد، استفاده از DbProviderFactory ، کار شما را تسهیل مینماید.
DbProviderFactory در Net Framework 2.0. ارائه شده است.برای درک و چگونگی استفاده از DBProviderFactory مثالی را بررسی مینماییم.
ابتدا کد زیر را درون یک فرم کپی نمایید:
برای استفاد از DBProviderFactory میبایست از فضای نامی System.Data.Common استفاده نمایید. بعد از اعلان کلاس فرم تعدادی آبجکت تعریف شده است، که سه آبجکت ابتدایی آن، بیانگر Provider دیتابیسهای MySQL،SQLSERVER و Oracle میباشد:
Providerهای بیان شده، جهت استفاده DBFactory برای تشخیص نوع Database میباشد، تا بتواند آبجکتهای مربوط به دیتابیس را ایجاد و در اختیار برنامه نویس قرار دهد. در این مثال ارتباط با دیتابیس SQLSERVER را امتحان میکنیم. بنابراین خواهیم داشت:
در کد بالا، Provider، دیتابیس SQLSERVER به DbProviderFactory به عنوان ورودی داده شده است، بنابراین آبجکتهای مربوط به دیتابیس SQL Server ایجاد و در اختیار شما قرار میگیرد.
اگر به نام فضای نامی System.Data.Common توجه نمایید،از کلمه Common استفاده شده است و منظور این است که تمامی کلاسهایی را که این فضای نامی ارائه میدهد، در هر دیتابیسی قابل استفاده میباشد. برای تشخیص، کلاسهای مربوط به این فضای نامی نیز در ابتدای نام آنها از دو حرف DB استفاده شده است. تمامی کلاسهای زیر در فضای نامی System.Data.Common قابل ارائه و استفاده میباشد:
جهت اطلاع: ممکن است سئوالی در ذهن شما ایجاد شود که دات نت چگونه براساس نام Provider نوع دیتابیس را تشخیص میدهد؟
جواب: زمانی که دیتابیسهای مختلف روی سیستم شما نصب میشود، Providerهای مربوط به هر دیتابیس درون فایل Machine.config که مربوط به دات نت میباشد، درج میشود. و دات نت براساس اطلاعات مربوط به همین فایل آبجکتهای دیتابیس را ایجاد مینماید.
امیدوارم مطلب فوق مفید واقع شود.
DbProviderFactory در Net Framework 2.0. ارائه شده است.برای درک و چگونگی استفاده از DBProviderFactory مثالی را بررسی مینماییم.
ابتدا کد زیر را درون یک فرم کپی نمایید:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Data.Common; namespace DBFactory { public partial class Form1 : Form { private string _MySQLProvider = "MySql.Data.MySqlClient"; private string _SQLProvider="System.Data.SqlClient"; private string _OracleProvider ="System.Data.OracleClient"; private DbProviderFactory _DbProviderFactory; private DbConnection _DbConnection = null; private DbCommand _DbCommand = null; private DbDataAdapter _DbDataAdapter = null; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { try { string _SQLconnectionstring = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Test;Data Source=FARHAD-PC"; string _Oracleconnectionstring = "Data Source=ServiceName;User Id=Username;Password=Password"; _DbProviderFactory = DbProviderFactories.GetFactory(_SQLProvider); _DbConnection = _DbProviderFactory.CreateConnection(); _DbConnection.ConnectionString = _SQLconnectionstring; _DbConnection.Open(); if (_DbConnection.State == ConnectionState.Closed) { MessageBox.Show("اتصال با دیتابیس برقرار نشده است"); } else { MessageBox.Show("اتصال با دیتابیس با موفقیت بر قرار شده است"); } } catch (System.Exception excep) { MessageBox.Show(excep.Message.ToString()); } } } }
برای استفاد از DBProviderFactory میبایست از فضای نامی System.Data.Common استفاده نمایید. بعد از اعلان کلاس فرم تعدادی آبجکت تعریف شده است، که سه آبجکت ابتدایی آن، بیانگر Provider دیتابیسهای MySQL،SQLSERVER و Oracle میباشد:
private string _MySQLProvider = "MySql.Data.MySqlClient"; private string _SQLProvider="System.Data.SqlClient"; private string _OracleProvider ="System.Data.OracleClient";
_DbProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
در کد بالا، Provider، دیتابیس SQLSERVER به DbProviderFactory به عنوان ورودی داده شده است، بنابراین آبجکتهای مربوط به دیتابیس SQL Server ایجاد و در اختیار شما قرار میگیرد.
اگر به نام فضای نامی System.Data.Common توجه نمایید،از کلمه Common استفاده شده است و منظور این است که تمامی کلاسهایی را که این فضای نامی ارائه میدهد، در هر دیتابیسی قابل استفاده میباشد. برای تشخیص، کلاسهای مربوط به این فضای نامی نیز در ابتدای نام آنها از دو حرف DB استفاده شده است. تمامی کلاسهای زیر در فضای نامی System.Data.Common قابل ارائه و استفاده میباشد:
DbCommand DbCommandBuilder DbConnection DbDataAdapter DbDataReader DbException DbParameter DbTransaction
جهت اطلاع: ممکن است سئوالی در ذهن شما ایجاد شود که دات نت چگونه براساس نام Provider نوع دیتابیس را تشخیص میدهد؟
جواب: زمانی که دیتابیسهای مختلف روی سیستم شما نصب میشود، Providerهای مربوط به هر دیتابیس درون فایل Machine.config که مربوط به دات نت میباشد، درج میشود. و دات نت براساس اطلاعات مربوط به همین فایل آبجکتهای دیتابیس را ایجاد مینماید.
امیدوارم مطلب فوق مفید واقع شود.
پس از آشنایی با دو مدل هاستینگ برنامههای Blazor در قسمت قبل، اکنون میخواهیم ساختار ابتدایی قالبهای این دو پروژه را بررسی کنیم.
ایجاد پروژههای خالی Blazor
در انتهای قسمت قبل، با روش ایجاد پروژههای خالی Blazor به کمک NET SDK 5x. آشنا شدیم. به همین جهت دو پوشهی جدید BlazorWasmSample و BlazorServerSample را ایجاد کرده و از طریق خط فرمان و با کمک NET CLI.، در پوشهی اولی دستور dotnet new blazorwasm و در پوشهی دومی دستور dotnet new blazorserver را اجرا میکنیم.
البته اجرای این دو دستور، نیاز به اتصال به اینترنت را هم برای بار اول دارند؛ تا فایلهای مورد نیاز و بستههای مرتبط را دریافت و restore کنند. بسته به سرعت اینترنت، حداقل یک ربعی را باید صبر کنید تا دریافت ابتدایی بستههای مرتبط به پایان برسد. برای دفعات بعدی، از کش محلی NuGet، برای restore بستههای blazor استفاده میشود که بسیار سریع است.
بررسی ساختار پروژهی Blazor Server و اجرای آن
پس از اجرای دستور dotnet new blazorserver در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همانطور که مشاهده میکنید، ساختار این پروژه، بسیار شبیه به یک پروژهی استاندارد ASP.NET Core از نوع Razor pages است.
- در پوشهی properties آن، فایل launchSettings.json قرار دارد که برای نمونه، شماره پورت اجرایی برنامه را در حالت اجرای توسط دستور dotnet run و یا توسط IIS Express مشخص میکند.
- پوشهی wwwroot آن، مخصوص ارائهی فایلهای ایستا مانند wwwroot\css\bootstrap است. در ابتدای کار، این پوشه به همراه فایلهای CSS بوت استرپ است. در ادامه اگر نیاز باشد، فایلهای جاوا اسکریپتی را نیز میتوان به این قسمت اضافه کرد.
- در پوشهی Data آن، سرویس تامین اطلاعاتی اتفاقی قرار دارد؛ به نام WeatherForecastService که هدف آن، تامین اطلاعات یک جدول نمایشی است که در ادامه در آدرس https://localhost:5001/fetchdata قابل مشاهده است.
- در پوشهی Pages، تمام کامپوننتهای Razor برنامه قرار میگیرند. یکی از مهمترین صفحات آن، فایل Pages\_Host.cshtml است. کار این صفحهی ریشه، افزودن تمام فایلهای CSS و JS، به برنامهاست. بنابراین در آینده نیز از همین صفحه برای افزودن فایلهای CSS و JS استفاده خواهیم کرد. اگر به قسمت body این صفحه دقت کنیم، تگ جدید کامپوننت قابل مشاهدهاست:
کار آن، رندر کامپوننت App.razor واقع در ریشهی پروژهاست.
همچنین در همینجا، تگهای دیگری نیز قابل مشاهده هستند:
همانطور که در قسمت قبل نیز عنوان شد، اتصال برنامهی در حال اجرای در مرورگر blazor server با backend، از طریق یک اتصال دائم SignalR صورت میگیرد. اگر در این بین خطای اتصالی رخ دهد، div با id مساوی blazor-error-ui فوق، یکی از پیامهایی را که مشاهده میکنید، بسته به اینکه برنامه در حالت توسعه در حال اجرا است و یا در حالت ارائهی نهایی، نمایش میدهد. افزودن مدخل blazor.server.js نیز به همین منظور است. کار آن مدیریت اتصال دائم SignalR، به صورت خودکار است و از این لحاظ نیازی به کدنویسی خاصی از سمت ما ندارد. این اتصال، کار به روز رسانی UI و هدایت رخدادها را به سمت سرور، انجام میدهد.
- در پوشهی Shared، یکسری فایلهای اشتراکی قرار دارند که قرار است در کامپوننتهای واقع در پوشهی Pages مورد استفاه قرار گیرند. برای نمونه فایل Shared\MainLayout.razor، شبیه به master page برنامههای web forms است که قالب کلی سایت را مشخص میکند. داخل آن Body@ را مشاهده میکنید که به معنای نمایش صفحات دیگر، دقیقا در همین محل است. همچنین در این پوشه فایل Shared\NavMenu.razor نیز قرار دارد که ارجاعی به آن در MainLayout.razor ذکر شده و کار آن نمایش منوی آبیرنگ سمت چپ صفحهاست.
- در پوشهی ریشهی برنامه، فایل Imports.razor_ قابل مشاهدهاست. مزیت تعریف usingها در اینجا این است که از تکرار آنها در کامپوننتهای razor ای که در ادامه تهیه خواهیم کرد، جلوگیری میکند. هر using تعریف شدهی در اینجا، در تمام کامپوننتها، قابل دسترسی است؛ به آن global imports هم گفته میشود.
- در پوشهی ریشهی برنامه، فایل App.razor نیز قابل مشاهدهاست. کار آن تعریف قالب پیشفرض برنامهاست که برای مثال به Shared\MainLayout.razor اشاره میکند. همچنین کامپوننت مسیریابی نیز در اینجا ذکر شدهاست:
اگر شخصی مسیر از پیش تعریف شدهای را وارد کند، به قسمت Found وارد میشود و در غیر اینصورت به قسمت NotFound. در قسمت Found است که از قالب MainLayout برای رندر یک کامپوننت توسط کامپوننت RouteView، استفاده خواهد شد و در قسمت NotFound، فقط پیام «Sorry, there's nothing at this address» به کاربر نمایش داده میشود. قالبهای هر دو نیز قابل تغییر و سفارشی سازی هستند.
- فایل appsettings.json نیز همانند برنامههای استاندارد ASP.NET Core در اینجا مشاهده میشود.
- فایل Program.cs آن که نقطهی آغازین برنامهاست و کار فراخوانی Startup.cs را انجام میدهد، دقیقا با یک فایل Program.cs برنامهی استاندارد ASP.NET Core یکی است.
- در فایل Startup.cs آن، همانند قبل دو متد تنظیم سرویسها و تنظیم میانافزارها قابل مشاهدهاست.
در ConfigureServices آن، سرویسهای صفحات razor و ServerSideBlazor اضافه شدهاند. همچنین سرویس نمونهی WeatherForecastService نیز در اینجا ثبت شدهاست.
قسمتهای جدید متد Configure آن، ثبت مسیریابی توکار BlazorHub است که مرتبط است با اتصال دائم SignalR برنامه و اگر مسیری پیدا نشد، به Host_ هدایت میشود:
در ادامه در همان پوشه، دستور dotnet run را اجرا میکنیم تا پروژه، کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.
که به همراه 13 درخواست و نزدیک به 600 KB دریافت اطلاعات از سمت سرور است.
این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. اگر صفحهی شمارشگر آنرا باز کنیم، با کلیک بر روی دکمهی آن، هرچند مقدار current count افزایش مییابد، اما شاهد post-back متداولی به سمت سرور نیستیم و این صفحه بسیار شبیه به صفحات برنامههای SPA (تک صفحهای وب) به نظر میرسد:
همانطور که عنوان شد، مدخل blazor.server.js فایل Pages\_Host.cshtml، کار به روز رسانی UI و هدایت رخدادها را به سمت سرور به صورت خودکار انجام میدهد. به همین جهت است که post-back ای را مشاهده نمیکنیم و برنامه، شبیه به یک برنامهی SPA به نظر میرسد؛ هر چند تمام رندرهای آن سمت سرور انجام میشوند و توسط SignalR به سمت کلاینت بازگشت داده خواهند شد.
برای نمونه اگر بر روی دکمهی شمارشگر کلیک کنیم، در برگهی network مرورگر، هیچ اثری از آن مشاهده نمیشود (هیچ رفت و برگشتی را مشاهده نمیکنیم). علت اینجا است که اطلاعات متناظر با این کلیک، از طریق web socket باز شدهی توسط SignalR، به سمت سرور ارسال شده و نتیجهی واکنش به این کلیکها و رندر HTML نهایی سمت سرور آن، از همین طریق به سمت کلاینت بازگشت داده میشود.
بررسی ساختار پروژهی Blazor WASM و اجرای آن
پس از اجرای دستور dotnet new blazorwasm در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همان صفحات پروژهی خالی Blazor Server در اینجا نیز قابل مشاهده هستند. این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. صفحات و کامپوننتهای پوشههای Pages و Shared نیز دقیقا همانند پروژهی Blazor Server قابل مشاهده هستند. مفاهیمی مانند فایلهای Imports.razor_ و App.razor نیز مانند قبل هستند.
البته در اینجا فایل Startup ای مشاهده نمیشود و تمام تنظیمات آغازین برنامه، داخل فایل Program.cs انجام خواهند شد:
در اینجا ابتدا کامپوننت App.razor را به برنامه معرفی میکند که ساختار آن با نمونهی مشابه Blazor Server دقیقا یکی است. سپس سرویسی را برای دسترسی به HttpClient، به سیستم تزریق وابستگیهای برنامه معرفی میکند. هدف از آن، دسترسی سادهتر به endpointهای یک ASP.NET Core Web API است. از این جهت که در یک برنامهی سمت کلاینت، دیگر دسترسی مستقیمی به سرویسهای سمت سرور را نداریم و برای کار با آنها همانند سایر برنامههای SPA که از Ajax استفاده میکنند، در اینجا از HttpClient برای کار با Web APIهای مختلف استفاده میشود.
تفاوت ساختاری دیگر این پروژهی WASM، با نمونهی Blazor Server، ساختار پوشهی wwwroot آن است:
که به همراه فایل جدید نمونهی wwwroot\sample-data\weather.json است؛ بجای سرویس متناظر سمت سرور آن در برنامهی blazor server. همچنین فایل جدید wwwroot\index.html نیز قابل مشاهدهاست و محتوای تگ body آن به صورت زیر است:
- چون این برنامه، یک برنامهی سمت کلاینت است، اینبار بجای فایل Host_ سمت سرور، فایل index.html سمت کلاینت را برای ارائهی آغازین برنامه داریم که وابستگی به دات نت ندارد و توسط مرورگر قابل درک است.
- در ابتدای بارگذاری برنامه، یک loading نمایش داده میشود که در اینجا نحوهی تعریف آن مشخص است. همچنین اگر خطایی رخ دهد نیز توسط div ای با id مساوی blazor-error-ui اطلاع رسانی میشود.
- مدخل blazor.webassembly.js در اینجا، کار دریافت وب اسمبلی و فایلهای NET runtime. را انجام میدهد؛ برخلاف برنامههای Blazor Server که توسط فایل blazor.server.js، یک ارتباط دائم SignalR را با سرور برقرار میکردند تا کدهای رندر شدهی سمت سرور را دریافت و نمایش دهند و یا اطلاعاتی را به سمت سرور ارسال کنند: برای مثال بر روی دکمهای کلیک شدهاست، اطلاعات مربوطه را در سمت سرور پردازش کن و نتیجهی نهایی رندر شده را بازگشت بده. اما در اینجا همه چیز داخل مرورگر اجرا میشود و برای این نوع اعمال، رفت و برگشتی به سمت سرور صورت نمیگیرد. به همین جهت تمام کدهای #C ما به سمت کلاینت ارسال شده و داخل مرورگر به کمک فناوری وب اسمبلی، اجرا میشوند. در اینجا از لحاظ ارسال تمام کدهای مرتبط با UI برنامهی سمت کلاینت به مرورگر کاربر، تفاوتی با فریمورکهایی مانند Angular و یا React نیست و آنها هم تمام کدهای UI برنامه را کامپایل کرده و یکجا ارسال میکنند.
در ادامه در همان پوشه، دستور dotnet run را اجرا میکنیم تا پروژه کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.
که به همراه 205 درخواست و نزدیک به 9.6 MB دریافت اطلاعات از سمت سرور است. البته اگر همین صفحه را refresh کنیم، دیگر شاهد دریافت مجدد فایلهای DLL مربوط به NET Runtime. نخواهیم بود و اینبار از کش مرورگر خوانده میشوند:
در این برنامهی سمت کلاینت، ابتدا تمام فایلهای NET Runtime. و وب اسمبلی دریافت شده و سپس اجرای تغییرات UI، در همین سمت و بدون نیاز به اتصال دائم SignalR ای به سمت سرور، پردازش و نمایش داده میشوند. به همین جهت زمانیکه بر روی دکمهی شمارشگر آن کلیک میکنیم، اتفاقی در برگهی network مرورگر ثبت نمیشود و رفت و برگشتی به سمت سرور صورت نمیگیرد.
عدم وجود اتصال SignalR، مزیت امکان اجرای آفلاین برنامهی WASM را نیز میسر میکند. برای مثال یکبار دیگر همان برنامهی Blazor Server را به کمک دستور dotnet run اجرا کنید. سپس آنرا در مرورگر در آدرس https://localhost:5001 باز کنید. اکنون پنجرهی کنسولی که dotnet run را اجرا کرده، خاتمه دهید (قسمت اجرای سمت سرور آنرا ببندید).
بلافاصله تصویر «سعی در اتصال مجدد» فوق را مشاهده خواهیم کرد که به دلیل قطع اتصال SignalR رخ دادهاست. یعنی یک برنامهی Blazor Server، بدون این اتصال دائم، قادر به ادامهی فعالیت نیست. اما چنین محدودیتی با برنامههای Blazor WASM وجود ندارد.
البته بدیهی است اگر یک Web API سمت سرور برای ارائهی اطلاعاتی به برنامهی WASM نیاز باشد، API سمت سرور برنامه نیز باید جهت کار و نمایش اطلاعات در سمت کلاینت در دسترس باشد؛ وگرنه قسمتهای متناظر با آن، قادر به نمایش و یا ثبت اطلاعات نخواهند بود.
ایجاد پروژههای خالی Blazor
در انتهای قسمت قبل، با روش ایجاد پروژههای خالی Blazor به کمک NET SDK 5x. آشنا شدیم. به همین جهت دو پوشهی جدید BlazorWasmSample و BlazorServerSample را ایجاد کرده و از طریق خط فرمان و با کمک NET CLI.، در پوشهی اولی دستور dotnet new blazorwasm و در پوشهی دومی دستور dotnet new blazorserver را اجرا میکنیم.
البته اجرای این دو دستور، نیاز به اتصال به اینترنت را هم برای بار اول دارند؛ تا فایلهای مورد نیاز و بستههای مرتبط را دریافت و restore کنند. بسته به سرعت اینترنت، حداقل یک ربعی را باید صبر کنید تا دریافت ابتدایی بستههای مرتبط به پایان برسد. برای دفعات بعدی، از کش محلی NuGet، برای restore بستههای blazor استفاده میشود که بسیار سریع است.
بررسی ساختار پروژهی Blazor Server و اجرای آن
پس از اجرای دستور dotnet new blazorserver در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همانطور که مشاهده میکنید، ساختار این پروژه، بسیار شبیه به یک پروژهی استاندارد ASP.NET Core از نوع Razor pages است.
- در پوشهی properties آن، فایل launchSettings.json قرار دارد که برای نمونه، شماره پورت اجرایی برنامه را در حالت اجرای توسط دستور dotnet run و یا توسط IIS Express مشخص میکند.
- پوشهی wwwroot آن، مخصوص ارائهی فایلهای ایستا مانند wwwroot\css\bootstrap است. در ابتدای کار، این پوشه به همراه فایلهای CSS بوت استرپ است. در ادامه اگر نیاز باشد، فایلهای جاوا اسکریپتی را نیز میتوان به این قسمت اضافه کرد.
- در پوشهی Data آن، سرویس تامین اطلاعاتی اتفاقی قرار دارد؛ به نام WeatherForecastService که هدف آن، تامین اطلاعات یک جدول نمایشی است که در ادامه در آدرس https://localhost:5001/fetchdata قابل مشاهده است.
- در پوشهی Pages، تمام کامپوننتهای Razor برنامه قرار میگیرند. یکی از مهمترین صفحات آن، فایل Pages\_Host.cshtml است. کار این صفحهی ریشه، افزودن تمام فایلهای CSS و JS، به برنامهاست. بنابراین در آینده نیز از همین صفحه برای افزودن فایلهای CSS و JS استفاده خواهیم کرد. اگر به قسمت body این صفحه دقت کنیم، تگ جدید کامپوننت قابل مشاهدهاست:
<body> <component type="typeof(App)" render-mode="ServerPrerendered" />
همچنین در همینجا، تگهای دیگری نیز قابل مشاهده هستند:
<body> <component type="typeof(App)" render-mode="ServerPrerendered" /> <div id="blazor-error-ui"> <environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment> <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.server.js"></script> </body>
- در پوشهی Shared، یکسری فایلهای اشتراکی قرار دارند که قرار است در کامپوننتهای واقع در پوشهی Pages مورد استفاه قرار گیرند. برای نمونه فایل Shared\MainLayout.razor، شبیه به master page برنامههای web forms است که قالب کلی سایت را مشخص میکند. داخل آن Body@ را مشاهده میکنید که به معنای نمایش صفحات دیگر، دقیقا در همین محل است. همچنین در این پوشه فایل Shared\NavMenu.razor نیز قرار دارد که ارجاعی به آن در MainLayout.razor ذکر شده و کار آن نمایش منوی آبیرنگ سمت چپ صفحهاست.
- در پوشهی ریشهی برنامه، فایل Imports.razor_ قابل مشاهدهاست. مزیت تعریف usingها در اینجا این است که از تکرار آنها در کامپوننتهای razor ای که در ادامه تهیه خواهیم کرد، جلوگیری میکند. هر using تعریف شدهی در اینجا، در تمام کامپوننتها، قابل دسترسی است؛ به آن global imports هم گفته میشود.
- در پوشهی ریشهی برنامه، فایل App.razor نیز قابل مشاهدهاست. کار آن تعریف قالب پیشفرض برنامهاست که برای مثال به Shared\MainLayout.razor اشاره میکند. همچنین کامپوننت مسیریابی نیز در اینجا ذکر شدهاست:
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
- فایل appsettings.json نیز همانند برنامههای استاندارد ASP.NET Core در اینجا مشاهده میشود.
- فایل Program.cs آن که نقطهی آغازین برنامهاست و کار فراخوانی Startup.cs را انجام میدهد، دقیقا با یک فایل Program.cs برنامهی استاندارد ASP.NET Core یکی است.
- در فایل Startup.cs آن، همانند قبل دو متد تنظیم سرویسها و تنظیم میانافزارها قابل مشاهدهاست.
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); }
قسمتهای جدید متد Configure آن، ثبت مسیریابی توکار BlazorHub است که مرتبط است با اتصال دائم SignalR برنامه و اگر مسیری پیدا نشد، به Host_ هدایت میشود:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); }
که به همراه 13 درخواست و نزدیک به 600 KB دریافت اطلاعات از سمت سرور است.
این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. اگر صفحهی شمارشگر آنرا باز کنیم، با کلیک بر روی دکمهی آن، هرچند مقدار current count افزایش مییابد، اما شاهد post-back متداولی به سمت سرور نیستیم و این صفحه بسیار شبیه به صفحات برنامههای SPA (تک صفحهای وب) به نظر میرسد:
همانطور که عنوان شد، مدخل blazor.server.js فایل Pages\_Host.cshtml، کار به روز رسانی UI و هدایت رخدادها را به سمت سرور به صورت خودکار انجام میدهد. به همین جهت است که post-back ای را مشاهده نمیکنیم و برنامه، شبیه به یک برنامهی SPA به نظر میرسد؛ هر چند تمام رندرهای آن سمت سرور انجام میشوند و توسط SignalR به سمت کلاینت بازگشت داده خواهند شد.
برای نمونه اگر بر روی دکمهی شمارشگر کلیک کنیم، در برگهی network مرورگر، هیچ اثری از آن مشاهده نمیشود (هیچ رفت و برگشتی را مشاهده نمیکنیم). علت اینجا است که اطلاعات متناظر با این کلیک، از طریق web socket باز شدهی توسط SignalR، به سمت سرور ارسال شده و نتیجهی واکنش به این کلیکها و رندر HTML نهایی سمت سرور آن، از همین طریق به سمت کلاینت بازگشت داده میشود.
بررسی ساختار پروژهی Blazor WASM و اجرای آن
پس از اجرای دستور dotnet new blazorwasm در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همان صفحات پروژهی خالی Blazor Server در اینجا نیز قابل مشاهده هستند. این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. صفحات و کامپوننتهای پوشههای Pages و Shared نیز دقیقا همانند پروژهی Blazor Server قابل مشاهده هستند. مفاهیمی مانند فایلهای Imports.razor_ و App.razor نیز مانند قبل هستند.
البته در اینجا فایل Startup ای مشاهده نمیشود و تمام تنظیمات آغازین برنامه، داخل فایل Program.cs انجام خواهند شد:
namespace BlazorWasmSample { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); } } }
تفاوت ساختاری دیگر این پروژهی WASM، با نمونهی Blazor Server، ساختار پوشهی wwwroot آن است:
که به همراه فایل جدید نمونهی wwwroot\sample-data\weather.json است؛ بجای سرویس متناظر سمت سرور آن در برنامهی blazor server. همچنین فایل جدید wwwroot\index.html نیز قابل مشاهدهاست و محتوای تگ body آن به صورت زیر است:
<body> <div id="app">Loading...</div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.webassembly.js"></script> </body>
- در ابتدای بارگذاری برنامه، یک loading نمایش داده میشود که در اینجا نحوهی تعریف آن مشخص است. همچنین اگر خطایی رخ دهد نیز توسط div ای با id مساوی blazor-error-ui اطلاع رسانی میشود.
- مدخل blazor.webassembly.js در اینجا، کار دریافت وب اسمبلی و فایلهای NET runtime. را انجام میدهد؛ برخلاف برنامههای Blazor Server که توسط فایل blazor.server.js، یک ارتباط دائم SignalR را با سرور برقرار میکردند تا کدهای رندر شدهی سمت سرور را دریافت و نمایش دهند و یا اطلاعاتی را به سمت سرور ارسال کنند: برای مثال بر روی دکمهای کلیک شدهاست، اطلاعات مربوطه را در سمت سرور پردازش کن و نتیجهی نهایی رندر شده را بازگشت بده. اما در اینجا همه چیز داخل مرورگر اجرا میشود و برای این نوع اعمال، رفت و برگشتی به سمت سرور صورت نمیگیرد. به همین جهت تمام کدهای #C ما به سمت کلاینت ارسال شده و داخل مرورگر به کمک فناوری وب اسمبلی، اجرا میشوند. در اینجا از لحاظ ارسال تمام کدهای مرتبط با UI برنامهی سمت کلاینت به مرورگر کاربر، تفاوتی با فریمورکهایی مانند Angular و یا React نیست و آنها هم تمام کدهای UI برنامه را کامپایل کرده و یکجا ارسال میکنند.
در ادامه در همان پوشه، دستور dotnet run را اجرا میکنیم تا پروژه کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.
که به همراه 205 درخواست و نزدیک به 9.6 MB دریافت اطلاعات از سمت سرور است. البته اگر همین صفحه را refresh کنیم، دیگر شاهد دریافت مجدد فایلهای DLL مربوط به NET Runtime. نخواهیم بود و اینبار از کش مرورگر خوانده میشوند:
در این برنامهی سمت کلاینت، ابتدا تمام فایلهای NET Runtime. و وب اسمبلی دریافت شده و سپس اجرای تغییرات UI، در همین سمت و بدون نیاز به اتصال دائم SignalR ای به سمت سرور، پردازش و نمایش داده میشوند. به همین جهت زمانیکه بر روی دکمهی شمارشگر آن کلیک میکنیم، اتفاقی در برگهی network مرورگر ثبت نمیشود و رفت و برگشتی به سمت سرور صورت نمیگیرد.
عدم وجود اتصال SignalR، مزیت امکان اجرای آفلاین برنامهی WASM را نیز میسر میکند. برای مثال یکبار دیگر همان برنامهی Blazor Server را به کمک دستور dotnet run اجرا کنید. سپس آنرا در مرورگر در آدرس https://localhost:5001 باز کنید. اکنون پنجرهی کنسولی که dotnet run را اجرا کرده، خاتمه دهید (قسمت اجرای سمت سرور آنرا ببندید).
بلافاصله تصویر «سعی در اتصال مجدد» فوق را مشاهده خواهیم کرد که به دلیل قطع اتصال SignalR رخ دادهاست. یعنی یک برنامهی Blazor Server، بدون این اتصال دائم، قادر به ادامهی فعالیت نیست. اما چنین محدودیتی با برنامههای Blazor WASM وجود ندارد.
البته بدیهی است اگر یک Web API سمت سرور برای ارائهی اطلاعاتی به برنامهی WASM نیاز باشد، API سمت سرور برنامه نیز باید جهت کار و نمایش اطلاعات در سمت کلاینت در دسترس باشد؛ وگرنه قسمتهای متناظر با آن، قادر به نمایش و یا ثبت اطلاعات نخواهند بود.
پاسخ: بله! فقط async در نگارشهای قبل از دات نت 4.5 وجود ندارند و متدهای الحاقی از دات نت 3 به بعد اضافه شدند و نیاز به افزودن ارجاعی به System.Runtime.CompilerServices.ExtensionAttribute دارند (برای دات نت 2 البته).
بنابراین اگر از VS 2015 استفاده میکنید برای مثال به سادگی میتوانید از قابلیتهای C# 6 در برنامههای دات نت 4 استفاده کنید. برای نمونه یک چنین کدی در VS 2015 با دات نت 4 هم قابل کامپایل است و بدون مشکل کار میکند:
بنابراین اگر از VS 2015 استفاده میکنید برای مثال به سادگی میتوانید از قابلیتهای C# 6 در برنامههای دات نت 4 استفاده کنید. برای نمونه یک چنین کدی در VS 2015 با دات نت 4 هم قابل کامپایل است و بدون مشکل کار میکند:
using static System.Console; namespace VS2015_Net4_Tests { class Program { static void Main(string[] args) { var test = "Test"; WriteLine($"{test}"); } } }
نظرات مطالب
Entity Framework و آینده
EF 5 به بعد بر مبنای دات نت 4 و نیم است.
ویندوز 8 دات نت 4 و نیم سر خود است.
از دیدگاه تیم BCL، دات نت 4 و نیم یک به روز رسانی درجای دات نت 4 است و صد در صد با آن سازگاری دارد.
دات نت 4 و نیم فقط بر روی ویندوزهای ویستا سرویس پک 2 به بعد قابل نصب است (روی XP یا ویندوز سرور 2003 نصب نمیشود).
ویندوز 8 دات نت 4 و نیم سر خود است.
از دیدگاه تیم BCL، دات نت 4 و نیم یک به روز رسانی درجای دات نت 4 است و صد در صد با آن سازگاری دارد.
دات نت 4 و نیم فقط بر روی ویندوزهای ویستا سرویس پک 2 به بعد قابل نصب است (روی XP یا ویندوز سرور 2003 نصب نمیشود).