اشتراک‌ها
پروژه Visual Resume

Visual Resume in Visio format. Use it as a template.  

پروژه Visual Resume
نظرات مطالب
SQL Antipattern #1
سلام
منظورتان از گروه it موسسه هدایت فرهیختگان چه بود؟ ایشان کتاب را ترجمه کردند؟ چگونه می‌توان آن را تهیه کرد؟
با تشکر
اشتراک‌ها
مدیریت مباحث همزمانی مرتبط با یک Rich Domain Model با استفاده از EFCore و الگوی Aggregate

In summary, the most important issues here are:

  • The Aggregate’s main task is to protect invariants (business rules, the boundary of immediate consistency)
  • In a multi-threaded environment, when multiple threads are running simultaneously on the same Aggregate, a business rule may be broken
  • A way to solve concurrency conflicts is to use Pessimistic or Optimistic concurrency techniques
  • Pessimistic Concurrency involves the use of a database transaction and a locking mechanism. In this way, requests are processed one after the other, so basically concurrency is lost and it can lead to deadlocks.
  • Optimistic Concurrency technique is based on versioning database records and checking whether the previously loaded version has not been changed by another thread.
  • Entity Framework Core supports Optimistic Concurrency. Pessimistic Concurrency is not supported
  • The Aggregate must always be treated and versioned as a single unit
  • Domain events are an indicator, that state was changed so Aggregate version should be changed as well 
public class AggregateRootBase : Entity, IAggregateRoot
{
    private int _versionId;

    public void IncreaseVersion()
    {
        _versionId++;
    }
}
internal sealed class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        builder.Property("_versionId").HasColumnName("VersionId").IsConcurrencyToken();
 
        //...
    }
}
var order = await _ordersContext.Orders.FindAsync(orderId);
order.AddOrderLine(request.ProductCode); 
var domainEvents = DomainEventsHelper.GetAllDomainEvents(order);
if (domainEvents.Any())
{
    order.IncreaseVersion();
}
await _ordersContext.SaveChangesAsync();


مدیریت مباحث همزمانی مرتبط با یک Rich Domain Model با استفاده از EFCore و الگوی Aggregate
مطالب
نکات مربوط به گرد کردن اعداد در دات نت
متد System.Math.Round که برای گرد کردن اعداد اعشاری به کار می‌رود، دارای 8 نوعoverload می‌باشد، که عدم توجه به موارد مربوط به آن باعث بروز خطا در محاسبات خواهد شد. به طور مثال پیش بینی شما از گرد کردن عدد 3.45 عدد 3.5 است ولی گاهی 3.5 و گاهی 3.4 گرد خواهد شد.
پس بهتر است تا با نکات زیر به شکل دقیق آشنا باشید.
Round(Decimal)
Rounds a decimal value to the nearest integral value.
(ورودی: دسیمال) به نزدیکترین عدد کامل گرد میکند،  4.3 به 4 و 4.8 به 5 گرد می‌شود. ولی در صورتیکه فاصله تا عدد کامل قبل و بعد برابر باشد به نزدیکترین عدد زوج گرد میکند، 4.5 به 4 گرد می‌شود چرا که 5  عددی فرد است.
نکته 1: خروجی تابع از نوع دسیمال است نه عدد کامل.
نکته 2: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest یا banker's roundingنیز گفته می‌شود. نتیجه برای به حداقل رساندن خطا است. نتیجه این حالت از متد با نتیجه Round(Decimal, MidpointRounding.ToEven) برابر است.



Round(Double)
Rounds a double-precision floating-point value to the nearest integral value.
(ورودی: double) به نزدیکترین عدد کامل گرد میکند،  4.3 به 4 و 4.8 به 5 گرد می‌شود. ولی در صورتیکه فاصله تا عدد کامل قبل و بعد برابر باشد به نزدیکترین عدد زوج گرد میکند، 4.5 به 4 گرد می‌شود چرا که 5  عددی فرد است.
نکته 1: خروجی تابع از نوع double است نه عدد کامل.
نکته 2: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest یا banker's roundingنیز گفته می‌شود. نتیجه برای به حداقل رساندن خطا است. نتیجه این حالت از متد با نتیجه Round(Double, MidpointRounding.ToEven) برابر است.
نکته 3: گاهی اوقات به دلیل از دست دادن دقت، ناشی از استفاده از مقادیر دسیمال به جای ممیز شناور و یا انجام محاسبات ریاضی بر روی بخش ممیزی خواهد بود. مثلا: زمانی که 11.5 ماحصل جمع 1. و  11.4 باشد به جای 12 که عدد زوج است به 11 گرد می‌شود!



Round(Decimal, Int32)
Rounds a decimal value to a specified number of fractional digits.
عدد دسیمال ورودی خود را به صورتی گرد میکند که:
1: تعداد ارقام اعشاری بعد از ممیز به اندازه پارامتر دوم این نوع ورودی متد Round باشد (بین صفر تا 28).
2: استفاده از این متد همانند فراخوانی آن با ورودی‌های Round(Decimal, Int32, MidpointRounding.ToEven) است. یعنی اینکه اگر رقم آخر بعد از ممیز دقیقا وسط مقدار قبل و بعد باشد (3.75) در صورتی که رقم ماقبل آخر فرد باشد رو به بالا گرد خواهد شد (مثال: 3.75 به 3.8 گرد خواهد شد) و اگر رقم ما قبل آخر زوج باشد تغییر نخواهد کرد (مثال: 3.45 به 3.4 گرد خواهد شد)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest یا banker's roundingنیز گفته می‌شود.

Math.Round(3.44, 1); //Returns 3.4.
Math.Round(3.45, 1); //Returns 3.4.
Math.Round(3.46, 1); //Returns 3.5.
Math.Round(4.34, 1); // Returns 4.3
Math.Round(4.35, 1); // Returns 4.4
Math.Round(4.36, 1); // Returns 4.4


Round(Decimal, MidpointRounding)
Rounds a decimal value to the nearest integer. 
A parameter specifies how to round the value if it is midway between two other numbers.
عدد دسیمال ورودی خود را به نزدیک‌ترین عدد integer گرد میکند، پارامتر اول عدد گرد نشده و پارامتر دوم مشخص میکند که در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و .... باشد) چگونه این گرد کردن صورت گیرد.

حالت‌های MidpointRounding:
MidpointRounding.ToEven: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، و رقم ماقبل آخر اعشار فرد باشد، رو به بالا گرد خواهد شد و در صورتیکه رقم ماقبل آخر اعشار زوج باشد، بدون تغییر باقی خواهد ماند (3.75 به 3.8 و 3.65 به 3.6 گرد می‌شود.)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest  یا  banker's roundingنیز گفته می‌شود.
MidpointRounding.AwayFromZero: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، در این حالت همواره عمل گرد کردن رو به رقم بعدی خواهد بود. این رایج‌ترین حالت گرد کردن است که به symmetric arithmetic rounding شناخته می‌شود.



Round(Double, Int32)
Rounds a double-precision floating-point value to a specified number of fractional digits.
یک عدد اعشاری از نوع Double (با دقت مضاعف) که تعداد مشخصی رقم بعد از ممیز دارد (به طور مثال 10 رقم اعشار)، به تعداد رقم اعشاری که کاربر به عنوان پارامتر دوم ذکر میکند(بین صفر تا 15 رقم)، (مثلا  4 رقم)گرد میکند. اگر تعداد رقم اعشار بیش از 15 تعیین شود، عدد 15 جایگزین خواهد شد. استفاده از این متد همانند فراخوانی آن به صورت Round(Double, Int32, MidpointRounding.ToEven) می‌باشد. یعنی اینکه اگر رقم آخر بعد از ممیز دقیقا وسط مقدار قبل و بعد باشد (3.75) در صورتی که رقم ماقبل آخر فرد باشد رو به بالا گرد خواهد شد (مثال: 3.75 به 3.8 گرد خواهد شد) و اگر رقم ما قبل آخر زوج باشد تغییر نخواهد کرد (مثال: 3.45 به 3.4 گرد خواهد شد)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest یا banker's roundingنیز گفته می‌شود.



Round(Double, MidpointRounding)
Rounds a double-precision floating-point value to the nearest integer.
A parameter specifies how to round the value if it is midway between two other numbers.
(عدد اعشاری ورودی: Double) عدد با دقت مضاعف ورودی خود را به نزدیک‌ترین عدد integer گرد میکند، پارامتر اول عدد گرد نشده و پارامتر دوم مشخص میکند در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و .... باشد) چگونه این گرد کردن صورت گیرد.

حالت‌های MidpointRounding:
MidpointRounding.ToEven: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، و رقم ماقبل آخر اعشار فرد باشد، رو به بالا گرد خواهد شد و در صورتیکه رقم ماقبل آخر اعشار زوج باشد، بدون تغییر باقی خواهد ماند (3.75 به 3.8 و 3.65 به 3.6 گرد می‌شود.)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest  یا  banker's roundingنیز گفته می‌شود.
MidpointRounding.AwayFromZero: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، در این حالت همواره عمل گرد کردن رو به رقم بعدی خواهد بود. این رایج‌ترین حالت گرد کردن است که به symmetric arithmetic rounding شناخته می‌شود.



Round(Decimal, Int32, MidpointRounding)
Rounds a decimal value to a specified number of fractional digits. 
A parameter specifies how to round the value if it is midway between two other numbers.
عدد دسیمال ورودی خود را با تعداد اعشار اعلام شده و به صورتی گرد میکند که:
1: تعداد ارقام اعشاری بعد از ممیز به اندازه پارامتر دوم این نوع ورودی متد Round باشد (بین صفر تا 28).

حالت‌های MidpointRounding:
MidpointRounding.ToEven: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، و رقم ماقبل آخر اعشار فرد باشد، رو به بالا گرد خواهد شد و در صورتیکه رقم ماقبل آخر اعشار زوج باشد، بدون تغییر باقی خواهد ماند (3.75 به 3.8 و 3.65 به 3.6 گرد می‌شود.)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest  یا  banker's roundingنیز گفته می‌شود.
MidpointRounding.AwayFromZero: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، در این حالت همواره عمل گرد کردن به سمت رقم بعدی خواهد بود. این رایج‌ترین حالت گرد کردن است که به symmetric arithmetic rounding شناخته می‌شود. 
3.4 = Math.Round( 3.45, 1, MidpointRounding.ToEven)
3.5 = Math.Round( 3.45, 1, MidpointRounding.AwayFromZero)

-3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven)
-3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)


Round(Double, Int32, MidpointRounding)
Rounds a double-precision floating-point value to the specified number of fractional digits. 
A parameter specifies how to round the value if it is midway between two other numbers.
(عدد اعشاری ورودی: Double) عدد با دقت مضاعف ورودی خود را به نزدیک‌ترین عدد با تعداد رقم اعشار مشخص شده گرد میکند، پارامتر اول متد، عدد گرد نشده و پارامتر دوم تعداد رقم اعشار (بین صفر تا 15 رقم) تعیین شده گرد می‌کند. اگر تعداد رقم اعشار بیش از 15 تعیین شود، عدد 15 جایگزین خواهد شد. و پارامتر سوم مشخص میکند که در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و .... باشد) چگونه این گرد کردن صورت گیرد.

حالت‌های MidpointRounding:
MidpointRounding.ToEven: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، و رقم ماقبل آخر اعشار فرد باشد، رو به بالا گرد خواهد شد و در صورتیکه رقم ماقبل آخر اعشار زوج باشد، بدون تغییر باقی خواهد ماند (3.75 به 3.8 و 3.65 به 3.6 گرد می‌شود.)
نکته 1: این تابع بر طبق استاندارد IEEE Standard 754, section 4 پیاده سازی شده است که در اصطلاح rounding to nearest  یا  banker's roundingنیز گفته می‌شود.

MidpointRounding.AwayFromZero: در صورتیکه مقدار اعشاری عددی میانی (5و 50و 500و 5000و ....) باشد، در این حالت همواره عمل گرد کردن به سمت رقم بعدی خواهد بود. این رایج‌ترین حالت گرد کردن است که به symmetric arithmetic rounding شناخته می‌شود.

 // The example displays the following output:
// 2.125 --> 2.13
// 2.135 --> 2.13
// 2.145 --> 2.15
// 3.125 --> 3.13
// 3.135 --> 3.14
// 3.145 --> 3.15
This code example produces the following results:

 3.4 = Math.Round( 3.45, 1)
-3.4 = Math.Round(-3.45, 1)

 3.4 = Math.Round( 3.45, 1, MidpointRounding.ToEven)
 3.5 = Math.Round( 3.45, 1, MidpointRounding.AwayFromZero)

-3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven)
-3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)

مطالب دوره‌ها
استفاده از Async و Await در برنامه‌های دسکتاپ
امکان استفاده از قابلیت‌های غیرهمزمان دات نت 4.5 در برنامه‌های WPF نیز به روش‌های مختلفی میسر است که در ادامه دو روش مرسوم آن‌را بررسی خواهیم کرد.

تهیه مقدمات بحث

ابتدا یک برنامه‌ی WPF جدید را آغاز کنید. سپس کدهای MainWindow.xaml آن‌را به نحو ذیل تغییر دهید.
<Window x:Class="Async10.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <DockPanel Dock="Top">
            <Button Name="BtnGo" Content="Go" Click="BtnGo_OnClick" />
            <ProgressBar Name="ProgressBar" IsIndeterminate="True" Visibility="Collapsed"/>
        </DockPanel>
        <TextBox Name="Results"/>
    </DockPanel>
</Window>
قصد داریم اطلاعاتی را از وب دریافت و سپس در TextBox قرار گرفته در صفحه نمایش دهیم.
در این مثال از کلاس جدید HttpClient نیز استفاده خواهیم کرد. برای استفاده از آن نیاز است ارجاعی را به اسمبلی استاندارد System.Net.Http.dll نیز به پروژه اضافه کنید.


روش اول

در ادامه کدهای فایل MainWindow.xaml.cs را به نحو ذیل تغییر داده و سپس برنامه را اجرا کنید.
using System.Net.Http;
using System.Windows;

namespace Async10
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtnGo_OnClick(object sender, RoutedEventArgs e)
        {
            BtnGo.IsEnabled = false;
            ProgressBar.Visibility = Visibility.Visible;

            var url = "https://www.dntips.ir";
            var client = new HttpClient(); // make sure you have an assembly reference to System.Net.Http.dll
            client.DefaultRequestHeaders.UserAgent.ParseAdd("Test Async");
            var task = client.GetStringAsync(url);
            task.ContinueWith(t =>
            {
                Results.Text = t.Result;

                BtnGo.IsEnabled = true;
                ProgressBar.Visibility = Visibility.Collapsed;
            });
        }
    }
}
روال رخدادگردان BtnGo_OnClick به نحو مرسوم آن نوشته شده است. بنابراین جهت دریافت نتیجه‌ی متد GetStringAsync می‌توان از متد ContinueWith بر روی task دریافت اطلاعات از وب، استفاده کرد. همچنین در اینجا مستقیما اطلاعات و نتیجه‌ی دریافتی را به عناصر UI انتساب داده‌ایم.
اگر پروژه را اجرا کنید، برنامه با استثنای زیر متوقف می‌شود:
 The calling thread cannot access this object because a different thread owns it.
چون task آغاز شده در ترد دیگری نسبت به ترد UI اجرا می‌شود، مجوز تغییری را در کدهای UI ندارد. برای حل این مشکل می‌توان از دو روش ذیل استفاده کرد:

الف) با استفاده از SynchronizationContext.Current و متد Post آن
 var context = SynchronizationContext.Current;
task.ContinueWith(t => context.Post(state =>
{
   Results.Text = t.Result;

   BtnGo.IsEnabled = true;
   ProgressBar.Visibility = Visibility.Collapsed;
}, null));
با این روش در قسمت اول آشنا شدید. SynchronizationContext.Current در اینجا چون در ابتدای متد و خارج از ContinueWith دریافت اطلاعات، اجرا می‌شود، به ترد UI یا ترد اصلی برنامه اشاره می‌کند. سپس همانطور که ملاحظه می‌کنید، توسط متد Post آن می‌توان اطلاعات را در زمینه‌ی تردی که SynchronizationContext به آن اشاره می‌کند اجرا کرد.

ب) با استفاده از امکانات TaskScheduler
 var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(t =>
{
   Results.Text = t.Result;

   BtnGo.IsEnabled = true;
   ProgressBar.Visibility = Visibility.Collapsed;
}, taskScheduler);
وقتی یک task اجرا می‌شود، TPL یا task parallel library نیاز دارد بداند، این task بر روی چه تردی و چه زمانی قرار است اجرا شود. به صورت پیش فرض از thread pool استفاده می‌کند، اما الزامی به آن نیست. با استفاده از TaskScheduler می‌توان بر روی نحوه‌ی رفتار تردهای TPL تاثیر گذاشت و یا حتی آن‌ها را سفارشی سازی کرد. متد FromCurrentSynchronizationContext، یک TaskScheduler جدید را در اختیار ما قرار می‌دهد که کدهای آن بر اساس SynchronizationContext.Current کار می‌کند؛ در اینجا Context به UI اشاره می‌کند و در یک برنامه‌ی وب، به یک درخواست رسیده.
برای مثال اگر در برنامه‌های وب یک Task جدید را اجرا کنید شاید اینطور به نظر برسد که به HttpContext دسترسی ندارید. این نقیصه را می‌توان توسط کار با SynchronizationContext جاری برطرف کرد.
در مثال فوق، چون taskScheduler پیش از فراخوانی متد ContinueWith ایجاد شده‌است، به ترد UI اشاره می‌کند. در این حالت برای نمایش اطلاعات در همان ترد اصلی برنامه کافی است این taskScheduler را به عنوان پارامتر متد ContinueWith معرفی کنیم.


روش دوم

در دات نت 4.5 می‌توان روال رخدادگردان تعریف شده را به صورت async نیز معرفی کرد (یعنی مجاز هستیم امضای متد پیش فرض تولید شده را تغییر دهیم):
 private async void BtnGo_OnClick(object sender, RoutedEventArgs e)
سپس استفاده از await در کدهای برنامه میسر خواهد شد:
        private async void BtnGo_OnClick(object sender, RoutedEventArgs e)
        {
            BtnGo.IsEnabled = false;
            ProgressBar.Visibility = Visibility.Visible;

            var url = "https://www.dntips.ir";
            var client = new HttpClient(); // make sure you have an assembly reference to System.Net.Http.dll
            client.DefaultRequestHeaders.UserAgent.ParseAdd("Test Async");
            Results.Text = await client.GetStringAsync(url);
            BtnGo.IsEnabled = true;
            ProgressBar.Visibility = Visibility.Collapsed;
        }
در این حالت دیگر نیازی به استفاده از ContinueWith و مباحث SynchronizationContext نیست. زیرا تمام آن‌ها به صورت توکار اعمال می‌شوند. به علاوه کدنهایی نیز بسیار خواناتر شده‌است.