در حال حاضر reference types در زبان #C نال پذیر هستند. جهت بالا بردن میزان امنیت زبان، پیشنهاد شدهاست که حالت پیش فرض reference types به غیرنال پذیر تغییر یابد و اگر علاقمند بودید که نال پذیر شوند، همانند nullable value types فعلی مانند int? x، نوع ?T را تعریف کنید. البته این مورد یک پیشنهاد از طرف اعضای تیم سیشارپ است و عدهای با آن موافق هستند (جهت بالا بردن ضریب امنیت و کاهش null reference exceptions) و عدهای خیر (گیج کنندهاست و کدهای فعلی را با مشکل مواجه میکند؛ یا خطاهای زیادی را توسط کامپایلر گزارش خواهد کرد).
بیشتر برای مرور راحتتر کدهاست. همه اعضای تیم مانیتورهای بزرگ ندارن و گاها پیش میاد که روی سیستم یه نفر دیگه داریم کدها رو مرور میکنیم که یه مانیتور داره (مانیتور خود لپ تاپ مثلا) و عملا باید هی صفحه رو اسکرول کنه تا متوجه بشیم چی نوشته (چون مانیتور نفر قبلی بزرگ بوده). و یا موقع مرج کردن دو تا برنچ خیلی این مورد میتونه کمک کنه.
قابلیت word wrap که روی همه سیستم هاهم فعال باشه، اگه روی گیتلب کدهارو ریویو کنیم بازم باید اسکرول کنیم چون از طریق مرورگر داریم کدها رو مرور میکنم نه در Visual Studio.
در کل من خودم از این افزونه برای این مورد استفاده میکنم که اگه کدهام قراره تو هرجایی مرور بشه کمترین اسکرول رو داشته باشم.
قابلیت word wrap که روی همه سیستم هاهم فعال باشه، اگه روی گیتلب کدهارو ریویو کنیم بازم باید اسکرول کنیم چون از طریق مرورگر داریم کدها رو مرور میکنم نه در Visual Studio.
در کل من خودم از این افزونه برای این مورد استفاده میکنم که اگه کدهام قراره تو هرجایی مرور بشه کمترین اسکرول رو داشته باشم.
نظرات مطالب
Microbenchmark
مطلب جالبی هست از یکی از اعضای تیم کامپایلر سی شارپ :(^)
بحث محاسبه کارآیی در دات نت شامل زمان صرف شده برای JIT اولیه کدها هم هست. به همین جهت اجرای اولیه اندکی بیشتر زمان میبره. همچنین GC هم در اینجا در ترد دیگری به موازات کار شما مشغول به کار است و اگر در یک اجرا زمان خوبی بدست آوردید به این معنا نیست که الزاما در اجرای بعدی هم همان زمان را بدست میآورید چون GC موکول شده به بعد. ضمن اینکه این نوع محاسبات چون به صورت ایزوله انجام میشود عموما بیانگر شرایط دنیای واقعی که پارامترهای زیادی در آنها دخیل هستند، نیست.
و ... اینکه برای خیلی از برنامه نویسها این نوع مقایسهها بیشتر جذاب هستند:
Head-to-head benchmark: C++ vs .NET
بحث محاسبه کارآیی در دات نت شامل زمان صرف شده برای JIT اولیه کدها هم هست. به همین جهت اجرای اولیه اندکی بیشتر زمان میبره. همچنین GC هم در اینجا در ترد دیگری به موازات کار شما مشغول به کار است و اگر در یک اجرا زمان خوبی بدست آوردید به این معنا نیست که الزاما در اجرای بعدی هم همان زمان را بدست میآورید چون GC موکول شده به بعد. ضمن اینکه این نوع محاسبات چون به صورت ایزوله انجام میشود عموما بیانگر شرایط دنیای واقعی که پارامترهای زیادی در آنها دخیل هستند، نیست.
و ... اینکه برای خیلی از برنامه نویسها این نوع مقایسهها بیشتر جذاب هستند:
Head-to-head benchmark: C++ vs .NET
نظرات مطالب
ASP.NET MVC #6
- به قسمت کار با Ajax در ASP.NET MVC که برسیم، کتابخانه برگزیده، jQuery است. بنابراین لازم است از همین الان اطلاعاتی را در این مورد داشته باشید (^).
- برای طراحی CSS بین برنامه نویسهای دات نت، فریم ورکی به نام LESS خیلی محبوبیت دارد (^).
- کتابخانههای جاوا اسکریپتی سمت کلاینت معنا پیدا میکنند. بنابراین آنچنان وارد بحث ASP.NET که سمت سرور است نمیشوند مگر اینکه کمک حالی در این رابطه باشند مانند jQuery. یا البته جاوا اسکریپت سمت سرور هم به نام نودجیاس وجود دارد که بحث دیگری است. در کل حین کار با جاوا اسکریپت دست بازتر است چون داخل مرورگر کاربر اجرا میشود که stateful است (مثل برنامههای سیلورلایت). به همین جهت کتابخانه MVVM هم برای جاوا اسکریپت وجود دارد (^). جالب اینجا است که این کتابخانه MVVM توسط یکی از اعضای تیم ASP.NET MVC طراحی شده.
- برای طراحی CSS بین برنامه نویسهای دات نت، فریم ورکی به نام LESS خیلی محبوبیت دارد (^).
- کتابخانههای جاوا اسکریپتی سمت کلاینت معنا پیدا میکنند. بنابراین آنچنان وارد بحث ASP.NET که سمت سرور است نمیشوند مگر اینکه کمک حالی در این رابطه باشند مانند jQuery. یا البته جاوا اسکریپت سمت سرور هم به نام نودجیاس وجود دارد که بحث دیگری است. در کل حین کار با جاوا اسکریپت دست بازتر است چون داخل مرورگر کاربر اجرا میشود که stateful است (مثل برنامههای سیلورلایت). به همین جهت کتابخانه MVVM هم برای جاوا اسکریپت وجود دارد (^). جالب اینجا است که این کتابخانه MVVM توسط یکی از اعضای تیم ASP.NET MVC طراحی شده.
علتش رو اینجا توضیح دادم: (^).
ASP.NET Webforms از نظر مایکروسافت در رده Done قرار دارد. فقط این اواخر کمی «ماله کشی و صافکاری» روی آن انجام شده و خواهد شد.
ضمنا الگوی MVVM به درد ASP.NET نمیخوره. نیاز به سیستمی State full داره که سیستمهای وب در این رده قرار نمیگیرند. ASP.NET اساسا Stateless است. به همین جهت در پروژههای وب تمایل به MVC بیشتر است تا هر الگوی دیگری.
همچنین یکی از اعضای تیم ASP.NET MVC ، اخیرا فریم ورک MVVM JavaScript ایی را به نام knockoutjs ارائه داده ((^)). علت ارائه برای جاوا اسکریپت هم دقیقا به State full آن بر میگردد، زمانیکه داخل مرورگر کاربر اجرا میشود. مانند Silverlight که آن هم State full است.
ASP.NET Webforms از نظر مایکروسافت در رده Done قرار دارد. فقط این اواخر کمی «ماله کشی و صافکاری» روی آن انجام شده و خواهد شد.
ضمنا الگوی MVVM به درد ASP.NET نمیخوره. نیاز به سیستمی State full داره که سیستمهای وب در این رده قرار نمیگیرند. ASP.NET اساسا Stateless است. به همین جهت در پروژههای وب تمایل به MVC بیشتر است تا هر الگوی دیگری.
همچنین یکی از اعضای تیم ASP.NET MVC ، اخیرا فریم ورک MVVM JavaScript ایی را به نام knockoutjs ارائه داده ((^)). علت ارائه برای جاوا اسکریپت هم دقیقا به State full آن بر میگردد، زمانیکه داخل مرورگر کاربر اجرا میشود. مانند Silverlight که آن هم State full است.
نظرات مطالب
یکپارچه کردن ELMAH با WCF RIA Services
الف - این اسم هر چیزی میتونه باشه که در قسمت path های ذکر شده فوق قابل تغییر است (بنابراین از اسمهای پیش فرض استفاده نکنید).
ب - بهترین مرجع برای ELMAH ، فایل web.config پوشه samples آن است. در این مورد تنظیمات لازم وجود دارد (در مورد بستن دسترسی ریموت یا استفاده از authorization).
خلاصه این دو مورد رو یکی از اعضای اصلی تیم ASP.NET در عمل نشان داده:
http://haacked.com/archive/2007/07/24/securely-implement-elmah-for-plug-and-play-error-logging.aspx
+
http://code.google.com/p/elmah/wiki/SecuringErrorLogPages
ب - بهترین مرجع برای ELMAH ، فایل web.config پوشه samples آن است. در این مورد تنظیمات لازم وجود دارد (در مورد بستن دسترسی ریموت یا استفاده از authorization).
خلاصه این دو مورد رو یکی از اعضای اصلی تیم ASP.NET در عمل نشان داده:
http://haacked.com/archive/2007/07/24/securely-implement-elmah-for-plug-and-play-error-logging.aspx
+
http://code.google.com/p/elmah/wiki/SecuringErrorLogPages
اشتراکها
معرفی سایت
سایت فوق،یک سایت آموزشی میباشد، و در مورد موضوعاتی همچون JAVA،HTML،CSS،CSharp،VB.Net،WPF،Oracle ،PHP ،SQL Serverو بسیاری از زبانهای دیگر، دارای مطالب آموزشی است.
نکته جالب توجه سایت فوق در این است که، مطالبش بصورت خوبی دسته بندی شده است، و مثالهای آن برای یادگیری مفاهیم مختصر و مفید است.
چنانچه برای مشاهده سایت با مشکل مواجه شدید،می بایست از فیلتر شکن استفاده نمایید.
نظرات مطالب
پیاده سازی SoftDelete در EF Core
نکتهای در مورد Owned Entities از EF-Core 3.1 به بعد
از EF-Core 3.1 به بعد، زمان حذف اطلاعات وابستهی به یک رکورد تغییر کردهاست. برای مثال اگر رکوردی به همراه یک owned entity باشد و سعی کنیم آنرا حذف کنیم و این حذف هم از نوع soft delete باشد، کوئری حاصل به همراه نال کردن مقادیر این owned entity هم خواهد بود (حتی اگر این اطلاعات نالپذیر هم نباشند). برای بازگشت به سیستم قبلی و حذف اطلاعات وابسته پس از حذف رکورد اصلی و نه قبل از آن، باید به صورت زیر عمل کرد:
// To fix https://github.com/dotnet/efcore/issues/19786 context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges; context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
«بهبود عملکرد» با «بهبود کارآیی» یکی نیست. پیاده سازی سیستم change tracking در حالت کلی، بدون پیاده سازی مباحث AOP غیرممکن است. بهتر است دورهی مرتبطی را در سایت در این مورد مرور کنید تا کلیات بحث تشکیل Proxyها بهتر مشخص شوند (و ... تشکیل پروکسی با روشهای مختلفی و با الگوریتمهای متفاوتی ممکن است و مهم نیست که dynamic proxy چندسکویی باشد یا خیر؛ این مورد نام یک الگوی طراحی شیء گرا است و نه یک کتابخانهی خاص). هدف من از عنوان این مسایل، اشاره به کلیات زیرساخت پیاده سازی این مباحث هست.
برای نمونه زمانیکه مقدار خاصیت شیء واکشی شدهای از Context را تغییر میدهید و سپس SaveChanges را فراخوانی میکنید، در این بین یک پروکسی وجود دارد (یک لایهی نامرئی و حائل بین شیء اصلی و تغییراتی که قرار است به آن اعمال شوند) که به تغییرات گوش فرا میدهد و در نهایت صرفا یک کوئری به روز رسانی آن فیلد خاص را تولید میکند و نه تمام فیلدهای دیگر را. این نوع مفاهیم کلی در اینجا مدنظر هستند. یک نمونه پیاده سازی کلی این مفهوم را در اینجا میتوانید مشاهده کنید.
همچنین EF Core 2.1 به همراه بستهی Microsoft.EntityFrameworkCore.Proxies است که پیاده سازی Lazy loading را میسر کردهاست و از Castel.Core هم استفاده میکند (یا همان Castle DynamicProxy که در دوره «Aspect oriented programming» مورد بررسی قرار گرفتهاست).
برای نمونه زمانیکه مقدار خاصیت شیء واکشی شدهای از Context را تغییر میدهید و سپس SaveChanges را فراخوانی میکنید، در این بین یک پروکسی وجود دارد (یک لایهی نامرئی و حائل بین شیء اصلی و تغییراتی که قرار است به آن اعمال شوند) که به تغییرات گوش فرا میدهد و در نهایت صرفا یک کوئری به روز رسانی آن فیلد خاص را تولید میکند و نه تمام فیلدهای دیگر را. این نوع مفاهیم کلی در اینجا مدنظر هستند. یک نمونه پیاده سازی کلی این مفهوم را در اینجا میتوانید مشاهده کنید.
همچنین EF Core 2.1 به همراه بستهی Microsoft.EntityFrameworkCore.Proxies است که پیاده سازی Lazy loading را میسر کردهاست و از Castel.Core هم استفاده میکند (یا همان Castle DynamicProxy که در دوره «Aspect oriented programming» مورد بررسی قرار گرفتهاست).
نوع دادهی 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