Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' <URL> <URL>". Either the 'unsafe-inline' keyword, a hash ('sha256-e89EFOm4894OkHmgoH52lEUIFeaK8fITnql0='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.
معرفی C# Source Generators
مخلص کلام اینکه : اگه با Fody یا PostSharp و همچنین Code Analyzerها آشنایی دارین. این قابلیت یه چیزی تو مایههای ترکیب ایناس و بهتون اجازه میده موقع Compile شدن کد پروژه رو Analyze کنین و یه کد جدیدی بهش اضافه کنین. (مثلا پیاده سازی اینترفیس INotifyPropertyChanged به صورت خودکار به هنگام کامپایل)
We’re pleased to introduce the first preview of Source Generators, a new C# compiler feature that lets C# developers inspect user code and generate new C# source files that can be added to a compilation. This is done via a new kind of component that we’re calling a Source Generator.
To get started with Source Generators, you’ll need to install the latest .NET 5 preview and the latest Visual Studio preview.
نمونه ای از پیاده سازی INotifyPropertyChanged with C# 9.0 Source Generators
سورس نگارش کامل دات نت
The referencesource repository contains sources from Microsoft .NET Reference Source that represent a subset of the .NET Framework. This subset contains similar functionality to the class libraries that are being developed in .NET Core. We intend to consult the referencesource repository as we develop .NET Core. It is also for the community to leverage to enable more scenarios for .NET developers.
چگونه کوئری های SQL بهتری بنویسیم؟
SQL is far from dead: it’s one of the most in-demand skills that you find in job descriptions from the data science industry, whether you’re applying for a data analyst, a data engineer, a data scientist or any other roles. This is confirmed by 70% of the respondents of the 2016 O’Reilly Data Science Salary Survey, who indicate that they use SQL in their professional context.
Class Coupling and Cohesion
تعدادی از قواعد شهودی هم، با Coupling و Cohesion به ترتیب مابین و درون کلاسها، سروکار دارند. تلاش ما در راستای افزایش Cohesion درون کلاسها و سست کردن و کاهش Coupling مابین کلاسها میباشد. این قواعد شهودی همین اهداف را در پارادایم action-oriented، در ارتباط با توابع دارند. هدف از Tight Cohesion (انسجام و چسبندگی قوی) در توابع، انسجام بالا و ارتباط نزدیک مابین کدهای موجود در تابع، میباشد. هدفی که Loose Coupling (اتصال سست و ضعیف، وابستگی ضعیف) در بین توابع دنبال میکند، اشاره دارد به اینکه اگر تابعی قصد استفاده از تابع دیگری را داشته باشد، باید وارد شدن و خروج از آن، از یک نقطه صورت گیرد. این مباحث منجربه مطرح شدن قواعد شهودی از جمله: «یک تابع باید طوری سازماندهی شود که تنها یک دستور return داشته باشد»، در پارادایم action-oriented میشود.
ما در پارادایم شیء گرا، اهداف خود از Loose Coupling و Tight Cohesion را در سطح کلاس مطرح میکنیم. 5 شکل اصلی Coupling مابین کلاسها به شرح زیر میباشد:
- Ni Coupling
- Export Coupling
- Overt Coupling
- Covert Coupling
- Surreptitious Coupling
بهترین حالتی که دو کلاس به طور مطلق هیچ وابستگی به یکدیگر ندارند. در این صورت میتوان یکی کلاسها را حذف کرد، بدون اینکه تأثیری بر روی سایر آنها داشته باشد. البته وجود برنامهای کاربردی با این نوع اتصال ممکن نخواهد بود. بهترین چیزی که میشود با این نوع اتصال ایجاد کرد، Class Libraryای میباشد که شامل مجموعه ای از کلاسهای مستقل بوده، به طوری که هیچ تأثیری بر روی یکدیگر ندارند.
در این شکل از اتصال، یک کلاس به واسط عمومی کلاس دیگر وابسته میباشد؛ به این معنی که از عملیاتی که کلاس مورد نظر در واسط عمومی خود قرار داده است، استفاده میکند.
این نوع اتصال زمانی رخ میدهد که یک کلاس از جزئیات پیاده سازی کلاس دیگر با داشتن اجازه دسترسی از جانب آن، استفاده کند. به عنوان مثال، مکانیزم کلاسهای friend در زبان سی پلاس پلاس، که امکان این را میدهد کلاس X اجازه دوستی به کلاس Y را اعطا کند و در این صورت کلاس Y میتواند به جزئیات پیاده سازی خصوصی کلاس X دسترسی داشته باشد.
این نوع اتصال هم به مانند Overt میباشد؛ با این تفاوت که هیچ اجازه دسترسی به کلاس Y داده نشده است. اگر زبانی داشته باشیم که به کلاس Y اجازه دهد خود را به عنوان دوست کلاس X معرفی کند، در این صورت نوع اتصال بین دو کلاس X و Y از نوع Covert میباشد. به عنوان مثال واقعی، میتوان به استفاده از Reflection در دات نت اشاره کرد.
آخرین نوع اتصال که بدترین حالت هم محسوب میشود، مربوط است به زمانیکه کلاس X به هر طریقی که شده از جزئیات داخلی کلاس Y آگاه باشد و از اعضای عمومی دادهای (public data member) آن کلاس استفاده کند. منظور این است که با تغییر این دادههای کلاس متوجه میشود که بر روی عملیات b از کلاس چه تأثیری میگذارد و با اتکاء به این دستاورد، جزئیات داخلی خود را پیاده سازی میکند و یک اتصال پنهان را با کلاس Y ایجاد کرده است. در این حالت یک وابستگی قوی به صورت پنهان مابین رفتاری از کلاس Y و پیاده سازی کلاس X ایجاد شده است.
اتصال و پیوستگی مابین کلاسها باید از نوع Nil یا Export باشد؛ به این معنی که یک کلاس فقط از واسط عمومی کلاس دیگر استفاده کند یا کاری با آن نداشته باشد. (Classes should only exhibit nil or export coupling with other classes, that is, a class should only use operations in the public interface of another class or have nothing to do with that class.)بجز این دو نوع اتصال، بقیه شکلهای اتصال به طریقی اجازه دسترسی به جزئیات پیاده سازی کلاسها را اعطا میکنند. در نتیجه باعث ایجاد وابستگی مابین پیاده سازی دو کلاس میشوند. این وابستگی ما بین پیاده سازیها به محض نیاز به تغییر پیاده سازی یکی از کلاسها ، باعث به وجود آمدن مشکلات نگهداری خواهند شد.
Cohesion درون کلاسها سعی بر این دارد که مطمئن شود تمام اجزای یک کلاس به شدت باهم مرتبط هستند. تعدادی از قواعد شهودی نیز در ادامه بر این خصوصیت دلالت دارند.
قاعده شهودی 2.8
یک کلاس باید یک و تنها یک Key Abstraction را تسخیر نماید. (A class should capture one and only one key abstraction)یک Key Abstraction به عنوان یک Entity در Domain Model تعریف میشود و اغلب در غالب اسم در اسناد و مشخصات نیازمندیها ظاهر میشوند. هر کدام از آنها باید فقط به یک کلاس نگاشت پیدا کنند. اگر این نگاشت به بیش از یک کلاس انجام گیرد، در نتیجه احتمالا طراح هر تابع را به عنوان یک کلاس تسخیر کرده است. اگر بیش از یک Key Abstraction به یک کلاس نگاشت پیدا کرده باشد، پس احتمالا طراح یک سیستم متمرکز را طراحی کرده است. این کلاسها Vague Classes نامیده میشوند و باید آنها در دو کلاس یا بیشتر، تسخیر شوند.
قاعده شهودی 2.9
داده و رفتار مرتبط را در یک جا (کلاس) نگه دارید. (Keep related data and behavior in one place)در واقع هدفی که این قاعده به دنبال آن میباشد این است که هر دو جزء تشکیل دهنده یک Key Abstraction ، یعنی همان داده و رفتار، باید توسط فقط یک کلاس تسخیر شوند. با نقض این قاعده، توسعه دهنده باید با قرار داد (Convention) خاصی برنامه نویسی کند.
طراح باید کلاسهایی را که مرتبا دادههای مورد نیاز خود را با متدهای get از سایر کلاسها دریافت میکنند، شناسایی کند. زیرا این نوع کلاسها این قاعده شهودی را نقض کردهاند.
مثال واقعی
یا برعکس آن ضد الگوی Anemic Domain Model که ناقض این قاعده میباشد.
در قسمت اول اشاره کردیم این قواعد را به راحتی میتوان در صورت نیاز نقض کرد. بعضی از مواقع نیاز به طراحی فیزیکی است که باعث تغییر در طراحی منطقی شده و چه بسا میتواند باعث نقض هر کدام از این قواعد شهودی نیز شود. اگر به بخش پروژههای سایت رجوع کنید این نقض کاملا مشهود (DomainClasses و ServiceLayer موجود در طراحی فیزیکی آنها) میباشد (بیشتر از Anemic Domain Model استفاده شده است)؛ ولی نمیتوان گفت که این کار اشتباه است.
قاعده شهودی 2.10
اطلاعات نامرتبط به هم را در کلاسهای جدا از هم قرار دهید. ((Spin off nonrelated information into another class (i.e., noncommunicating behavior)
هدف از این قاعده این است که اگر کلاسی داریم که یکسری از متدهایش با بخشی از داده و یکسری دیگر با بخش دیگر دادهها کار میکنند، در واقع شما دو Key Abstraction را به یک کلاس نگاشت کرده اید (Vague Class) و باید آنها را به کلاسهای جدا نگاشت کنید.
به کلاس Dictionary در تصویر زیر توجه کنید.
برای تعداد کمی داده، بهترین پیاده سازی با استفاده از List و در مقابل برای تعداد داده زیاد بهترین پیاده سازی، استفاده از HashTable میباشد. هر یک از این پیاده سازیها، به متدهایی برای add و find کلمات نیاز دارند. طراحی سمت چپ تصویر نشان از نقض این قاعده شهودی دارد.
در طرح سمت چپ، استفاده کننده باید بداند که چقدر داده وارد کند. از طرفی نمایش جزئیات پیاده سازی در نام کلاس هم ایده خوبی نیست (طرح سمت چپ). بهترین راه حل که در مقالات آینده به آنها خواهیم رسید، بحث استفاده از ارث بری میباشد. به این ترتیب، با استفاده از یک کلاس Dictionary که نمایش جزئیات داخلی خود را مخفی کرده و در شرایط لازم نمایش جزئیات داخلی خود را تغییر دهد. منظور این است که استفاده کننده درگیر جزئیات داخلی آن نشود و این جزئیات که کدام نوع PDict یا HDict استفاده خواهد شد، از دید او مخفی باشد.
Microsoft's .NET Fundamentals Team a few weeks ago announced a new version of .NET Framework 4.6.1. It includes a number of streamline improvements to Windows Presentation Foundation and SQL Connectivity, to name a few. And just recently, the team also re-emphasized end of support for versions of .NET Framework versions older than 4.5.1.
RML Utilities X64
RML Utilities X86
که نهایتا در این مسیر C:\Program Files\Microsoft Corporation\RMLUtils نصب خواهد شد.
سپس در خط فرمان این سه دستور را امتحان کنید:
-- Insert 10000 records using 20 threads, Repeat Execution 3 times -- disk-based "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblNormal values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output -- memory-optimized, tblMemoryOptimized_Schema_And_Data "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblMemoryOptimized_Schema_And_Data values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output -- memory-optimized, tblMemoryOptimized_Schema_Only "C:\Program Files\Microsoft Corporation\RMLUtils\ostress.exe" –n20 –r3 -S. -E -dTestdb2 -q -Q"set statistics time off; SET STATISTICS IO Off; set nocount on; DECLARE @start datetime = getdate(); declare @insertCount int = 10000; declare @startId int = 1; while @startId < @insertCount begin insert into tblMemoryOptimized_Schema_Only values ('Test', '2013-01-01T00:00:00') set @startId +=1 end; Print DATEDIFF(ms,@start,getdate());" –oc:\temp\output
البته برای اجرای این دستورات نیاز است فیلد CustomerID را identity تعریف کنید (در هر سه جدول مطلب جاری).
-- It is not Memory Optimized CREATE TABLE tblNormal ( [CustomerID] int identity NOT NULL PRIMARY KEY NONCLUSTERED, [Name] nvarchar(250) NOT NULL, CustomerSince DATETIME not NULL INDEX [ICustomerSince] NONCLUSTERED ) -- DURABILITY = SCHEMA_AND_DATA CREATE TABLE tblMemoryOptimized_Schema_And_Data ( [CustomerID] INT identity NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 131072), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA) -- DURABILITY = SCHEMA_ONLY CREATE TABLE tblMemoryOptimized_Schema_Only ( [CustomerID] INT identity NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 131072), [Name] NVARCHAR(250) NOT NULL, [CustomerSince] DATETIME NOT NULL INDEX [ICustomerSince] NONCLUSTERED ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
public class HandleConcurrencyExceptionAttribute : FilterAttribute, IExceptionFilter { private PropertyMatchingMode _propertyMatchingMode; /// <summary> /// This defines when the concurrencyexception happens, /// </summary> public enum PropertyMatchingMode { /// <summary> /// Uses only the field names in the model to check against the entity. This option is best when you are using /// View Models with limited fields as opposed to an entity that has many fields. The ViewModel (or model) field names will /// be used to check current posted values vs. db values on the entity itself. /// </summary> UseViewModelNamesToCheckEntity = 0, /// <summary> /// Use any non-matching value fields on the entity (except timestamp fields) to add errors to the ModelState. /// </summary> UseEntityFieldsOnly = 1, /// <summary> /// Tells the filter to not attempt to add field differences to the model state. /// This means the end user will not see the specifics of which fields caused issues /// </summary> DontDisplayFieldClashes = 2 } public HandleConcurrencyExceptionAttribute() { _propertyMatchingMode = PropertyMatchingMode.UseViewModelNamesToCheckEntity; } public HandleConcurrencyExceptionAttribute(PropertyMatchingMode propertyMatchingMode) { _propertyMatchingMode = propertyMatchingMode; } /// <summary> /// The main method, called by the mvc runtime when an exception has occured. /// This must be added as a global filter, or as an attribute on a class or action method. /// </summary> /// <param name="filterContext"></param> public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled && filterContext.Exception is DbUpdateConcurrencyException) { //Get original and current entity values DbUpdateConcurrencyException ex = (DbUpdateConcurrencyException)filterContext.Exception; var entry = ex.Entries.Single(); //problems with ef4.1/4.2 here because of context/model in different projects. //var databaseValues = entry.CurrentValues.Clone().ToObject(); //var clientValues = entry.Entity; //So - if using EF 4.1/4.2 you may use this workaround var clientValues = entry.CurrentValues.Clone().ToObject(); entry.Reload(); var databaseValues = entry.CurrentValues.ToObject(); List<string> propertyNames; filterContext.Controller.ViewData.ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The " + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again to cause your changes to be the current saved values."); PropertyInfo[] entityFromDbProperties = databaseValues.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); if (_propertyMatchingMode == PropertyMatchingMode.UseViewModelNamesToCheckEntity) { //We dont have access to the model here on an exception. Get the field names from modelstate: propertyNames = filterContext.Controller.ViewData.ModelState.Keys.ToList(); } else if (_propertyMatchingMode == PropertyMatchingMode.UseEntityFieldsOnly) { propertyNames = databaseValues.GetType().GetProperties(BindingFlags.Public).Select(o => o.Name).ToList(); } else { filterContext.ExceptionHandled = true; UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; return; } UpdateTimestampField(filterContext, entityFromDbProperties, databaseValues); //Get all public properties of the entity that have names matching those in our modelstate. foreach (var propertyInfo in entityFromDbProperties) { //If this value is not in the ModelState values, don't compare it as we don't want //to attempt to emit model errors for fields that don't exist. //Compare db value to the current value from the entity we posted. if (propertyNames.Contains(propertyInfo.Name)) { if (propertyInfo.GetValue(databaseValues, null) != propertyInfo.GetValue(clientValues, null)) { var currentValue = propertyInfo.GetValue(databaseValues, null); if (currentValue == null || string.IsNullOrEmpty(currentValue.ToString())) { currentValue = "Empty"; } filterContext.Controller.ViewData.ModelState.AddModelError(propertyInfo.Name, "Current value: " + currentValue); } } //TODO: hmm.... how can we only check values applicable to the model/modelstate rather than the entity we saved? //The problem here is we may only have a few fields used in the viewmodel, but many in the entity //so we could have a problem here with that. //object o = propertyInfo.GetValue(myObject, null); } filterContext.ExceptionHandled = true; filterContext.Result = new ViewResult() { ViewData = filterContext.Controller.ViewData }; } }