پاسخ به بازخورد‌های پروژه‌ها
نحوه سفارشی سازی ویو های این پروژه
با سلام مجدد 
من هنوز مشکل در سفارشی سازی ویوهای این پروژه رو دارم با این مقاله
و نکته زیر 
-اگر تغییری در فایل‌های View، در تعداد و نام آن‌ها صورت گرفت، روی فایل T4MVC.tt کلیک راست کرده و گزینه‌ی اجرای آن‌را انتخاب کنید. پس از این‌کار، مجددا کامپایل پروژه را فراموش نکنید. 
وقتی راست روی T4MVC.tt راست کلیک و run custom tool را می‌زنم اخطار زیر را دریافت می‌کنم 
[Security Warning]
 
Running this text template can potentially harm your computer. Do not run it if you
obtain if rtom an untrusted source.
 
Click OK. to run the template.
Click Cancel top stop the process.
 
[X] Do not show this message again
 
[OK]  [Cancel]
و با زدن ok کلی خطا میده . لطفا راهنمایی بفرمایید.
پاسخ به بازخورد‌های پروژه‌ها
خطای sql?
سلام؛
- در مطلب قبلی که ارسال کردید چون لاگین نکرده بودید، امکان ویرایش نداشتید. ولی خوب، می‌تونستید در ادامه آن، بحث را دنبال کنید.
- نگارش 1.8 مدتی هست که منتشر شده. بهتر است از آن استفاده کنید.
- ذکر قسمت parametersValues کاملا اختیاری است. اگر پارامتری ندارید یا اگر شرطی در عبارت SQL خودتون ندارید، آن‌را ذکر نکنید:
.MainTableDataSource(dataSource =>
                {
                    dataSource.GenericDataReader(
                        providerName: "System.Data.SQLite",
                        connectionString: "Data Source=" + System.AppDomain.CurrentDomain.BaseDirectory + "\\data\\database.sqlite",
                        sql: @"SELECT id,name,family,mark
                               FROM Student
                                WHERE id='1'"
                    );
                })
- همچنین اگر شرطی رو تعریف کردید، بهتر است از پارامترها استفاده کنید که به صورت خودکار در اینجا پردازش خواهند شد:
.MainTableDataSource(dataSource =>
                {
                    dataSource.GenericDataReader(
                        providerName: "System.Data.SQLite",
                        connectionString: "Data Source=" + System.AppDomain.CurrentDomain.BaseDirectory + "\\data\\database.sqlite",
                        sql: @"SELECT id,name,family,mark
                               FROM Student
                                WHERE id= @p1",                             
                        parametersValues: new object[] { 1 /*مقدار پارامتر اول*/ }
                    );
                })
پروژه‌ها
شمسی ساز تاریخ اکسپلورر ویندوز
چقدر خوب می‌شد اگر تاریخ و ساعت کنار صفحه ویندوز، شمسی بود


یا اگر لیست فایل‌ها را بررسی می‌کردیم، ستون تاریخ آن‌ها نیز قابل درک بود


و یا اگر به خواص یک فایل مراجعه می‌کردیم، تاریخ ایجاد آن نیز شمسی بود


- با استفاده از برنامه سورس باز ExplorerPCal.exe می‌توانید به این قابلیت‌ها دسترسی پیدا کنید.
- این برنامه برای اجرا نیاز به دات نت فریم ورک 4 دارد. بنابراین بر روی ویندوزهای XP SP3 به بعد قابل اجرا است. ضمنا برنامه ExplorerPCal.exe با هر دو نگارش 32 بیتی و 64 بیتی ویندوز سازگار است.
- برای اجرای آن تنها کافی است فایل «ExplorerPCal.exe» را اجرا کنید. همچنین بهتر است برنامه را در درایو C کپی نکنید.
- برای حذف آن از سیستم، نیاز است پوشه‌ی مربوطه را حذف نمائید. در این حال اگر ویندوز پیام در حال استفاده بودن فایلی را می‌دهد، ویندوز را یکبار ری‌استارت کنید و پس از آن فایل‌های باقیمانده را بدون مشکل می‌توانید حذف نمائید.
- برای ویندوز 10 به این مطلب مراجعه کنید.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 17 - بررسی فریم ورک Logging
یک نکته‌ی تکمیلی: رفع خطای «A circular dependency was detected for the service of type Microsoft.Extensions.Logging.ILoggerFactory»

اگر در حال توسعه‌ی یک لاگر سفارشی با پیاده سازی ILoggerProvider هستید (مانند)، در سازنده‌ی آن سرویسی را تزریق نکنید که از یک <ILogger<T استفاده می‌کند! در غیراینصورت در زمان اجرا با خطای «یک وابستگی حلقوی یافت شد»، برنامه خاتمه خواهد یافت؛ چون ILogger تزریق شده هم به همین سرویس جاری اشاره می‌کند! برای رفع آن، یک IServiceProvider را به سازنده‌ی ILoggerProvider سفارشی تزریق کرده و سپس به این نحو، یک لاگر را از آن استخراج کنید:
// we want to prevent a circular dependency between ILoggerFactory and CustomLoggers
private ILoggerFactory? _loggerFactory;
private ILoggerFactory LoggerFactory => _loggerFactory ??= serviceProvider.GetRequiredService<ILoggerFactory>();

نکته: الگویی را که در اینجا با ?? مشاهده می‌کنید، روشی برای کش کردن اطلاعات دریافتی در یک سرویس Singleton است.
مطالب
آشنایی با SplitQuery در EF Core 5x
در دیتابیس‌های رابطه‌ای، داده‌ها(رکوردها)ی مرتبط، با استفاده از Join بدست آورده می‌شوند و بعضا نیاز هست برای رسیدن به یک داده‌ی مورد نیاز، باید چندین Join بین جداول مختلف به کار برده شود. در Entity Framework ، زمانیکه قصد بدست آوردن داده‌های مرتبط را داریم، از Include  استفاده می‌کنیم که در نهایت منجر به همان left Join می‌شود.
برای درک بهتر و توضیح راحت‌تر، فرض کنید بر روی دیتابیس سایت جاری، قصد داریم لیست هر کاربر را به همراه مقالاتی که در سایت منتشر کرده‌است، بدست بیاوریم. برای اینکار قطعه کد زیر را خواهیم داشت :
  var users = context.Users.Include(x => x.Articles).ToList();
دستور فوق، منجر به تولید T-SQL زیر خواهد شد:
SELECT [u].[Id], [u].[FirstName], [u].[LastName], [a].[Id], [a].[Approved], [a].[AuthorId], [a].[Body], [a].[PubDate], [a].[Subject]
FROM [Users] AS [u]
LEFT JOIN [Articles] AS [a] ON [u].[Id] = [a].[AuthorId]
ORDER BY [u].[Id], [a].[Id]
اجرای این دستور، خروجی زیر را به همراه دارد:

شکل یک

همانطور که در عکس فوق مشاهده میکنید، کاربر با شناسه‌ی 1، ده مقاله را منتشر کرده‌است که به ازای تعداد مقالات، سه فیلد شناسه کاربر، نام و نام خانوادگی، تکرار شده‌است و همین اتفاق برای کاربر با شناسه‌ی 2 هم تکرار شده‌است. قطعا در اکثر نرم افزارها، نیاز به چنین کوئری‌ها و داده‌هایی زیاد است و جلوگیری از این تکرار داده‌ها، می‌تواند بر روی کارایی نرم افزار تاثیر گذار باشد.


Cartesian explosion

اجرای یک Join بین جداول با رابطه‌ی one to many، منجر به تکرار ستون‌های جدول طرف one، به تعداد رکورد‌های مرتبط می‌شود. این اتفاق باعث هدر رفت منابع و همچنین کند شدن اجرای کوئری خواهد شد که این مشکل تحت عنوان Cartesian explosion problem شناخته می‌شود.


از نسخه EF Core5.0، امکانی اضافه شده‌است که کمک می‌کند این مشکل را برطرف کنیم و سرعت اجرای کوئری‌ها سریع‌تر شود. Entity Framework به صورت پیش فرض، کوئری‌ها را در قالب یک دستور (یک رفت و برگشت) انجام میدهد، اما میتوان این رفتار را با استفاده از قابلیت SplitQuery تغییر داد.


متد ()SplitQuery

با استفاده از این متد، به Entity Framework الزام میکنیم که بجای استفاده از Join در یک کوئری، کوئری‌های جداگانه‌ای را بر روی دیتابیس اجرا کند. برای کوئری اول که در بالا نوشتیم، به صورت زیر می‌توانیم SplitQuery را اعمال کنیم:

 var users = context.Users.AsSplitQuery().Include(x => x.Articles).ToList();

کوئری حاصل از کد فوق به صورت زیر می‌باشد:

-- First Part  
 SELECT [u].[Id], [u].[FirstName], [u].[LastName]
      FROM [Users] AS [u]
      ORDER BY [u].[Id]
-- Second Part
   SELECT [a].[Id], [a].[Approved], [a].[AuthorId], [a].[Body], [a].[PubDate], [a].[Subject], [u].[Id]
      FROM [Users] AS [u]
      INNER JOIN [Articles] AS [a] ON [u].[Id] = [a].[AuthorId]
      ORDER BY [u].[Id]

همانطور که مشاهده می‌کنید، دو کوئری تولید شده است که کوئری اول برای دریافت لیست کاربران و کوئری دوم برای لیست مقالات تولید شده‌است. این تغییر باعث شده‌است که فیلدهای مورد نیاز از جدول کاربران، به تعداد مقالات هر کاربر تکرار نشود.

شکل 2- خروجی حاصل بعد از اجرا به صورت SplitQuery


فعال سازی به صورت سراسری

همانطور که بیان شد، EF به صورت پیش فرض  کوئری‌ها را در قالب یک درخواست اجرا می‌کند. اگر تمایل دارید خاصیت SplitQuery بر روی تمامی کوئری‌ها اعمال شود، می‌توانید به صورت زیر این امکان را به صورت سراسری اعمال نمایید.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=EFQuerying;",
            o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}

اگر SplitQuery را به صورت سراسری فعال کردید و نیاز داشتید جایی یک کوئری را به همان روش SignleQuery اجرا کنید، میتوانید از متد SingleQuery به صورت زیر استفاده نمایید.

var users = context.Users.AsSingleQuery().Include(x => x.Articles).ToList();


عکس زیر مقایسه ای بین اجرای کوئری‌ها به صورت Single و Split می‌باشد:

مبنع:  thinktecture  



در رابطه با SplitQuery موارد زیر مطرح می‌باشد :

  • زمانیکه کوئری تبدیل به دو یا چند کوئری می‌شود، ممکن است بعد از اجرا کوئری اول و قبل از اجرای کوئری دوم، یک به روزرسانی انجام شود که ممکن است consistency نقض شود.
  • در این حالت، چندین درخواست و رفت و برگشت اجرا می‌شود که همین می‌تواند باعث تاخیر و افزایش زمان گردد.
مطالب
مقایسه بین حلقه های تکرار (Lambda ForEach و for و foreach)
به حلقه‌های تکرار زیر دقت کنید.
#1 حلقه for با استفاده از متغیر Count لیست
var ListOfNumber = new List<int>() { 100, 200, 300 , 400 , 500  };
for ( int i = 0 ; i < ListOfNumber.Count ; i++ )
{
       Console.WriteLine( ListOfNumber[i] );
}
#2حلقه for با استفاده از متغیر یا مقدار صریح
var ListOfNumber = new List<int>() { 100, 200, 300 , 400 , 500  };        
for ( int i = 0 ; i < 5 ; i++ )
{
       Console.WriteLine( ListOfNumber[i] );
}
#3 foreach ساده که احتمالا خیلی از شماها از اون استفاده می‌کنید.
var ListOfNumber = new List<int>() { 100, 200, 300 , 400 , 500 };       
foreach ( var number in ListOfNumber )
{
    Console.WriteLine( number );
}
#4 Lambda ForEach که مورد علاقه بعضی‌ها از جمله خود من است.
var ListOfNumber = new List<int>() { 100, 200, 300  , 400 , 500 };
ListOfNumber.ForEach( number => 
{
     Console.WriteLine( number );
});
به نظر شما حلقه‌های بالا از نظر کارایی چه تفاوتی با هم دارند؟
تمام حلقه‌های بالا یک خروجی رو چاپ خواهند کرد ولی اگر فکر می‌کنید که هیچ تفاوتی ندارند سخت در اشتباه هستید.
هر 4 حلقه تکرار بالا رو در 21 حالت مختلف با شریط یکسان در یک سیستم تست کردیم و نتایج زیر حاصل شد.(منظور از نتایج مدت زمان اجرای هر حلقه است)

تعداد تکرار
 #1 for با استفاده از متغیر Count لیست
 #2 for-استفاده از متغیر
#3 foreach
#4 Lambda ForEach 
  1000
 
 0.000008  0.000007   0.000014   0.000012  
 2000 0.000014  0.000013  
 0.000026   0.000022  
 3000 0.000019   0.000016   0.000036   0.000028  
 4000 0.000024   0.000022   0.000047   0.000035  
 5000 0.000029  0.000025  
 0.000058   0.000043  
 10,0000.000059  
0.000047  
0.000117  
0.000081  
 20,0000.000128  
0.000093  
0.000225  
0.000161  
 30,0000.000157  
0.000141  
0.000336  
0.000233  
 40,0000.000221  
0.000180  
0.000442  
0.000310  
 50,0000.000263  
0.000236  
0.000553  
0.000307  
 100,0000.000530  
0.000443  
0.001103  
0.000773  
 200,0000.001070  
0.000879  
0.002194  
0.001531  
 300,0000.001641  
0.001345  
0.003281  
0.002308  
 400,0000.002233  
0.001783  
0.004388  
0.003083  
 500,0000.002615  
0.002244  
0.005521  
0.003873  
 1,000,0000.005303  
0.004520  
0.011072  
0.007767  
 2,000,0000.010543  
0.009074  
0.022127  
0.015536  
 3,000,0000.015738  
0.013569  
0.033186  
0.023268  
 4000,0000.021039  
0.018113  
0.044335  
0.031188  
 5000,0000.026280  
0.022593  
0.055521  
0.038793  
 10,000,0000.052528 
0.046090  
0.111517  
0.078482  

بررسی نتایج :
  • سریع‌ترین حلقه تکرار حلقه for  با استفاده از متغیر معمولی به عنوان تعداد تکرار حلقه است.
  • رتبه دوم برای حلقه for همراه با استفاده از خاصیت Count لیست مورد نظر بوده است. دلیلش هم اینه که سرعت دستیابی کامپایلر به متغیر‌های معمولی حتی تا 3برابر سریع‌تر از دسترسی به متد get خاصیت هاست.
  • مهم‌ترین نکته این است که Lambda ForEach عمکردی بسیار بهتری نسبت به foreach معمولی داره.

پس هر گاه قصد اجرای حلقه ForEach رو برای لیست  دارید  و سرعت اجرا هم براتون اهمیت داره بهتره که از Lambda ForEach استفاده کنید. حالا به کد زیر دقت کنید:

   int[] arrayOfNumbers = new int[] {100 , 200 , 300 , 400 , 500 };

   Array.ForEach<int>( arrayOfNumbers, ( int counter ) => { Console.WriteLine( counter ); } );
من همون حلقه بالا رو به صورت آرایه پیاده سازی کردم و برای اجرای حلقه از دستور Array.ForEach که عملکردی مشابه با List.ForEach داره استفاده کردم که نتیجه به دست اومده نشون داد که  Array.ForEach از نظر سرعت به مراتب از foreach معمولی کند‌تر عمل می‌کنه.دلیلش هم اینه که کامپایلر هنگام کار با آرایه‌ها و اجرای اون‌ها به صورت حلقه، کد IL خاصی رو تولید می‌کنه که مخصوص کار با آرایه هاست و سرعت اون به مراتب از سرعت کد IL تولید شده برای IEnumerator‌ها پایین تره.
مطالب
نحوه‌ی محاسبه‌ی هش کلمات عبور کاربران در ASP.NET Identity
روش‌های زیادی برای ذخیره سازی کلمات عبور وجود دارند که اغلب آن‌ها نیز نادرست هستند. برای نمونه شاید ذخیره سازی کلمات عبور، به صورت رمزنگاری شده، ایده‌ی خوبی به نظر برسد؛ اما با دسترسی به این کلمات عبور، امکان رمزگشایی آن‌ها، توسط مهاجم وجود داشته و همین مساله می‌تواند امنیت افرادی را که در چندین سایت، از یک کلمه‌ی عبور استفاده می‌کنند، به خطر اندازد.
در این حالت هش کردن کلمات عبور ایده‌ی بهتر است. هش‌ها روش‌هایی یک طرفه هستند که با داشتن نتیجه‌ی نهایی آن‌ها، نمی‌توان به اصل کلمه‌ی عبور مورد استفاده دسترسی پیدا کرد. برای بهبود امنیت هش‌های تولیدی، می‌توان از مفهومی به نام Salt نیز استفاده نمود. Salt در اصل یک رشته‌ی تصادفی است که پیش از هش شدن نهایی کلمه‌ی عبور، به آن اضافه شده و سپس حاصل این جمع، هش خواهد شد. اهمیت این مساله در بالا بردن زمان یافتن کلمه‌ی عبور اصلی از روی هش نهایی است (توسط روش‌هایی مانند brute force یا امتحان کردن بازه‌ی وسیعی از عبارات قابل تصور).
اما واقعیت این است که حتی استفاده از یک Salt نیز نمی‌تواند امنیت بازیابی کلمات عبور هش شده را تضمین کند. برای مثال نرم افزارهایی موجود هستند که با استفاده از پرداش موازی قادرند بیش از 60 میلیارد هش را در یک ثانیه آزمایش کنند و البته این کارآیی، برای کار با هش‌های متداولی مانند MD5 و SHA1 بهینه سازی شده‌است.


روش هش کردن کلمات عبور در ASP.NET Identity

ASP.NET Identity 2.x که در حال حاضر آخرین نگارش تکامل یافته‌ی روش‌های امنیتی توصیه شده‌ی توسط مایکروسافت، برای برنامه‌های وب است، از استانداردی به نام RFC 2898 و الگوریتم PKDBF2 برای هش کردن کلمات عبور استفاده می‌کند. مهم‌ترین مزیت این روش خاص، کندتر شدن الگوریتم آن با بالا رفتن تعداد سعی‌های ممکن است؛ برخلاف الگوریتم‌هایی مانند MD5 یا SHA1 که اساسا برای رسیدن به نتیجه، در کمترین زمان ممکن طراحی شده‌اند.
PBKDF2 یا password-based key derivation function جزئی از استاندارد RSA نیز هست (PKCS #5 version 2.0). در این الگوریتم، تعداد بار تکرار، یک Salt و یک کلمه‌ی عبور تصادفی جهت بالا بردن انتروپی (بی‌نظمی) کلمه‌ی عبور اصلی، به آن اضافه می‌شوند. از تعداد بار تکرار برای تکرار الگوریتم هش کردن اطلاعات، به تعداد باری که مشخص شده‌است، استفاده می‌گردد. همین تکرار است که سبب کندشدن محاسبه‌ی هش می‌گردد. عدد معمولی که برای این حالت توصیه شده‌است، 50 هزار است.
این استاندارد در دات نت توسط کلاس Rfc2898DeriveBytes پیاده سازی شده‌است که در ذیل مثالی را در مورد نحوه‌ی استفاده‌ی عمومی از آن، مشاهده می‌کنید:
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
 
namespace IdentityHash
{
    public static class PBKDF2
    {
        public static byte[] GenerateSalt()
        {
            using (var randomNumberGenerator = new RNGCryptoServiceProvider())
            {
                var randomNumber = new byte[32];
                randomNumberGenerator.GetBytes(randomNumber);
                return randomNumber;
            }
        }
 
        public static byte[] HashPassword(byte[] toBeHashed, byte[] salt, int numberOfRounds)
        {
            using (var rfc2898 = new Rfc2898DeriveBytes(toBeHashed, salt, numberOfRounds))
            {
                return rfc2898.GetBytes(32);
 
            }
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            var passwordToHash = "VeryComplexPassword";
            hashPassword(passwordToHash, 50000);
            Console.ReadLine();
        }
 
        private static void hashPassword(string passwordToHash, int numberOfRounds)
        {
            var sw = new Stopwatch();
            sw.Start();
            var hashedPassword = PBKDF2.HashPassword(
                                        Encoding.UTF8.GetBytes(passwordToHash),
                                        PBKDF2.GenerateSalt(),
                                        numberOfRounds);
            sw.Stop();
            Console.WriteLine();
            Console.WriteLine("Password to hash : {0}", passwordToHash);
            Console.WriteLine("Hashed Password : {0}", Convert.ToBase64String(hashedPassword));
            Console.WriteLine("Iterations <{0}> Elapsed Time : {1}ms", numberOfRounds, sw.ElapsedMilliseconds);
        }
    }
}
شیء Rfc2898DeriveBytes برای تشکیل، نیاز به کلمه‌ی عبوری که قرار است هش شود به صورت آرایه‌ای از بایت‌ها، یک Salt و یک عدد اتفاقی دارد. این Salt توسط شیء RNGCryptoServiceProvider ایجاد شده‌است و همچنین نیازی نیست تا به صورت مخفی نگه‌داری شود. آن‌را  می‌توان در فیلدی مجزا، در کنار کلمه‌ی عبور اصلی ذخیره سازی کرد. نتیجه‌ی نهایی، توسط متد rfc2898.GetBytes دریافت می‌گردد. پارامتر 32 آن به معنای 256 بیت بودن اندازه‌ی هش تولیدی است. 32 حداقل مقداری است که بهتر است انتخاب شود.
پیش فرض‌های پیاده سازی Rfc2898DeriveBytes استفاده از الگوریتم SHA1 با 1000 بار تکرار است؛ چیزی که دقیقا در ASP.NET Identity 2.x بکار رفته‌است.


تفاوت‌های الگوریتم‌های هش کردن اطلاعات در نگارش‌های مختلف ASP.NET Identity

اگر به سورس نگارش سوم ASP.NET Identity مراجعه کنیم، یک چنین کامنتی در ابتدای آن قابل مشاهده است:
 /* =======================
* HASHED PASSWORD FORMATS
* =======================
*
* Version 2:
* PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
* (See also: SDL crypto guidelines v5.1, Part III)
* Format: { 0x00, salt, subkey }
*
* Version 3:
* PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
* Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
* (All UInt32s are stored big-endian.)
*/
در نگارش دوم آن از الگوریتم PBKDF2 با هزار بار تکرار و در نگارش سوم با 10 هزار بار تکرار، استفاده شده‌است. در این بین، الگوریتم پیش فرض HMAC-SHA1 نگارش‌های 2 نیز به HMAC-SHA256 در نگارش 3، تغییر کرده‌است.
در یک چنین حالتی بانک اطلاعاتی ASP.NET Identity 2.x شما با نگارش بعدی سازگار نخواهد بود و تمام کلمات عبور آن باید مجددا ریست شده و مطابق فرمت جدید هش شوند. بنابراین امکان انتخاب الگوریتم هش کردن را نیز پیش بینی کرده‌اند.

در نگارش دوم ASP.NET Identity، متد هش کردن یک کلمه‌ی عبور، چنین شکلی را دارد:
public static string HashPassword(string password, int numberOfRounds = 1000)
{
    if (password == null)
        throw new ArgumentNullException("password");
 
    byte[] saltBytes;
    byte[] hashedPasswordBytes;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, numberOfRounds))
    {
        saltBytes = rfc2898DeriveBytes.Salt;
        hashedPasswordBytes = rfc2898DeriveBytes.GetBytes(32);
    }
    var outArray = new byte[49];
    Buffer.BlockCopy(saltBytes, 0, outArray, 1, 16);
    Buffer.BlockCopy(hashedPasswordBytes, 0, outArray, 17, 32);
    return Convert.ToBase64String(outArray);
}
تفاوت این روش با مثال ابتدای بحث، مشخص کردن طول salt در متد Rfc2898DeriveBytes است؛ بجای محاسبه‌ی اولیه‌ی آن. در این حالت متد Rfc2898DeriveBytes مقدار salt را به صورت خودکار محاسبه می‌کند. این salt بجای ذخیره شدن در یک فیلد جداگانه، به ابتدای مقدار هش شده اضافه گردیده و به صورت یک رشته‌ی base64 ذخیره می‌شود. در نگارش سوم، از کلاس ویژه‌ی RandomNumberGenerator برای محاسبه‌ی Salt استفاده شده‌است.
بازخوردهای دوره
ارتباطات بلادرنگ و SignalR
- کار ما در اینجا پشتیبانی مقالات سایت‌های دیگر نیست. ضمنا در قسمت اول، یک مبحث کاملا خارج از موضوع را نباید ارسال کنید. در ذیل هر مطلب عنوان شده برای این نوع موارد مرتبط به دوره، لطفا به قسمت پرسش و پاسخ دوره مراجعه کنید.
- من از جزئیات کار شما اطلاعی ندارم. نه خطایی را عنوان کردید و نه پروژه‌ای برای دیباگ پیوست شده. ایشان عنوان کرده که اجرا می‌شود؛ یک فیلم هم پیوست کرده. ضمنا با jQuery Ajax کار کرده قسمتی را. یعنی یک سری پیشنیاز دیگر را هم باید به پروژه و صفحه اضافه کنید. در کل از راه دور و بدون دیدن کار شما نمی‌شود نظر داد (کل کار البته).
- در آن مقاله سایت ثالث، dependency_OnChange فقط زمانی رجیستر می‌شود که GetData یکبار فراخوانی شود. ضمنا این کد نشتی حافظه دارد. چون مدام دارد new OnChangeEventHandler را ایجاد می‌کند بدون اینکه فکری برای حذف موارد ثبت شده کند. همچنین JobInfoRepository را در سطح یک Web API Controller وهله سازی کرده. یعنی این وهله به ازای هر درخواست رسیده یکبار ایجاد می‌شود (ونه اینکه یکبار ایجاد شده و بارها استفاده شود) و به این ترتیب یکبار دیگر نیز OnChangeEventHandler رجیستر خواهد شد. خلاصه اینکه روش مناسبی نبوده و توصیه نمی‌شود.

مطلبت حذف شد. تکرار کنی خودت هم حذف می‌شی. شک نداشته باش.
بازخوردهای دوره
بررسی قسمت‌های مختلف قالب پروژه WPF Framework تهیه شده
فقط سه مورد (DbEntityValidationException, DbUpdateException, DbUpdateConcurrencyException)  از استثناهای ویژه EF Code first در متد ApplyAllChanges بررسی شدند به همراه خطاهای اعتبارسنجی. مابقی استثناها در صورت رخ دادن به لایه‌های بالاتر منتشر خواهند شد (چون catch نشدند).
همچنین کدهای تکراری نحوه نمایش فقط این سه مورد ویژه و بررسی خطاهای اعتبارسنجی، ضرورتی به تکرار یا بررسی در کل برنامه را ندارد. نیازی نیست در کل برنامه if/else نوشت که بررسی شود آیا خطای اعتبارسنجی هست یا خیر، زمانیکه می‌شود آن‌را به صورت مرکزی و پاکیزه، مدیریت کرد و مدیریت این‌ها هم حالت خاص دیگری ندارد (باید لاگ شوند و باید به اطلاع کاربر رسانده شوند که هر دو مورد در اینجا خودکار است). حداکثر این است که از نحوه نمایش آن راضی نیستید. کار سورس باز است. تغییرش بدید. این روش و این صفحه دیالوگ مطابق سلیقه من طراحی شده.
به علاوه در لایه‌های بالاتر نیز نیازی به بررسی سایر استثناها نیست چون این موارد در فایل App.xaml.cs در بالاترین سطح ممکن دریافت و لاگ می‌شوند؛ همچنین به کاربر هم نمایش داده خواهند شد (در متدهای appDispatcherUnhandledException و currentDomainUnhandledException).

البته این برنامه دسکتاپ است که یک چنین اجازه‌ای رو می‌ده. در برنامه‌های وب این موارد توسط ELMAH لاگ خواهند شد و به کاربر پیغام کلی خطایی رخ داده نمایش داده می‌شود.
نظرات اشتراک‌ها
کتاب رایگان ساخت Mobile Websites با MVC4
من تا حد امکان از طرح مسایل وارز در این سایت پرهیز می‌کنم و از دیگران هم درخواست کردم اینکار را انجام ندهند (در طرح مقالات یا لینک‌های سایت). لینک‌های کتاب‌ها یا مجلاتی را هم که در این سایت مشاهده می‌کنید اکثرا رایگان هستند مانند همین مطلب جاری.
علت چیست؟ پرداختن بیش از حد به وارز شما را از تولید محتوا دور می‌کند و صرفا تبدیل خواهید شد به یک مصرف کننده ساده نهایی.
هنر این است که مثلا از محصولات تلریک استفاده می‌کنید؟ آیا می‌تونید در مورد نحوه طراحی API اون نصف صفحه مطلب بنویسید؟ یا اینکه نه، فقط یک مصرف کننده جزء هستید که به دنبال کرک و برنامه مفت.
آیا می‌تونید از 10 تا کتاب سی شارپی که در مورد پردازش موازی دانلود کردید 5 تا مقاله دربیارید؟
از 100 گیگ فیلم آموزشی که دانلود کردید 10 تا مطلب منتشر کنید؟
هدف کلی این سایت هم همین است. آیا می‌تونید محتوا تولید کنید؟ یا اینکه فقط کوش، فقط می‌خوام، فقط نیست. فقط زود باش.


پ.ن.
مادر تمام سایت‌های وارز مرتبط با برنامه نویسی، سایتی است با آدرس «board4allcz.eu». اگر کمی دقت کنید، تعداد ایرانی‌های فعال در آن هم بسیار زیاد است! ولی واقعا ضرورتی نداره کار یک سایت دیگر رو ما اینجا تکرار کنیم.