پاسخ به پرسش‌ها
آیا استفاده از FullName یک کلاس تضادی با Clean Code دارد؟

تمام Entityهایی که ساختم دارای Schema هستند و تمامی Modelها نیز در پوشه هایی هم نام با Schema قرار داده شده اند. برای مثال من دو کلاس در مسیرهای زیر دارم (Application = schema):

Models.Output.Application.ActionMethods.ActionMethod();
Domain.Entities.Application.ActionMethods.ActionMethod();

اگر فضای نام را using کنم و بخوام در یک متد از هر دو کلاس نمونه سازی کنم خطای زیر صادر میشه (Ambiguous reference):

using Ces.Caspian.Models.Output.Application.ActionMethods;
using Ces.Caspian.Domain.Entities.Application.ActionMethods;

{
  var a = new ActionMethod();
  var b = new ActionMethod();
}

//'ActionMethod' is an ambiguous reference between 'Ces.Caspian.Domain.Entities.Application.ActionMethods.ActionMethod' and 'Ces.Caspian.Models.Output.Application.ActionMethods.ActionMethod'

مگر آنکه در زمان نمونه سازی از نام کامل استفاده کنم:

var a = new Models.Output.Application.ActionMethods.ActionMethod();
var b = new Domain.Entities.Application.ActionMethods.ActionMethod();

تمامی کلاس هایی که در پروژه Model قرار دارند همان نقش DTO را دارند. چه به عنوان ورود اطلاعات و چه به عنوان خروجی. آیا نامگذاری Modelها اشکال دارند؟ در واقع سعی کردم از افزودن پیشوند و پسوند اضافه به نام Modelها پرهیز کنم.

فایل‌های پروژه‌ها
PdfRpt-1.5.zip
- کاهش حجم نهایی فایل تولیدی با کش کردن تصاویر
- به تاریخ نمایش داده شده در DefaultFooter ، پشتیبانی از RTL اضافه شد.
- یک تغییر کوچک: هر جایی PdfPTable بود به PdfGrid تبدیل شد.
- چندین مثال جدید اضافه شدند (
^):
نحوه استفاده از html برای تعریف header و همچنین footer
نحوه تعریف بارکد
و مثال InlineProviders برای ساده سازی تعاریف هدرها و فوترهای سفارشی
مطالب
آموزش LINQ بخش سوم
در ادامه سری آموزشی LINQ  به بررسی متغیرهای Range می‌پردازیم:
4 عنصر یک عبارت پرس و جو عبارتند از:
• علملگرهای LINQ
• کلمات کلیدی Keyword
• متغیر‌های Range

Range Variable : متغیر تعریف شده‌ی در یک محدوده خاص.

  عبارت پرس و جوی زیر را در نظر بگیرد:
var query = from word in list
where word.StartsWith("a")
select word;
در این پرس و جو (from,in,where,select) کلمات کلیدی محسوب می‌شوند. البته where و select می‌توانند به عنوان عملگر محسوب شوند.
شناسه‌ی list یک متغیر محلی است و تنها موردی که باقی می‌ماند شناسه‌ی word است که به آن متغیر Range می‌گوییم. متغیر‌های Range همانند متغیر‌های مرسوم مورد استفاده‌ی در برنامه‌ها هستند که بصورت فقط خواندنی مهیا شده اند. با این اوصاف متغیر‌های Range در ابتدا کمی عجیب به نظر می‌رسند. به این علت که در وسط عبارت پرس و جو معرفی می‌شوند و نیازی به تعریف شدن به روش مرسوم به شکل زیر را ندارند:
 String word;
در این حالت معرفی Word از طریق عبارت from انجام می‌شود. کامپایلر نوع داده‌ی متغیر را از طریق فرآیندی به نام Type Inference مشخص می‌کند. در مثال بالا کامپایلر تشخیص می‌دهد که Word از نوع string  است؛ به این علت که جدا شده از <list<string  می‌باشد.

متغیر word  در دو حالت ممکن است قابل دسترس نباشد :
  • پایان پرس و جو
  • مواجه شدن با کلمه کلیدی into . این کلمه‌ی کلیدی برای اتصال دو Query استفاده می‌شود.

نکته
:در بعضی مواقع باید وضعیت متغیر Range را صریحا مشخص کنیم؛ بطور مثال کد زیر با خطا مواجه خواهد شد:
object[] ints = new object[] { 1, 2, 3 };
           var query = from num in ints
                        where num < 3
                        select num;
در زمان پردازش دستور Where، کامپایلر نمی‌تواند عملگر مقایسه‌ای را برای یک نوع int و یک نوع object اجرا کند. برای حل این مشکل بصورت صریح (Explicit) نوع متغیر Range را مشخص می‌کنیم:
var query = from int num in ints
where num < 3
select num;
همانطور که می‌بینید در این حالت به کامپایلر اعلام می‌کنیم که num از نوع int می‌باشد و cast کردن با موفقیت انجام می‌شود و خروجی همان چیزی است که ما انتظار داریم.

تذکر : بهتر است از تعریف صریح متغیر Range پرهیز کنیم؛ مگر در شرایطی مثل کد بالا .

قطعه کد زیر به‌راحتی کامپایل می‌شود و نیازی به اعلان صریح نوع متغیر range نیست. زیر از طریق مکانیزیم Type Inference نوع متغیر مشخص شده است.
List<string> list = new List<string> {"LINQ","Query","adventure"};
var query = from string word in list
where word.Contains("r")
orderby word ascending
select word;
اعلان صریح متغیر Range باعث می‌شود که پشت پرده، عملیات <Cast<T اتفاق بیافتد و در مواقع غیر ضروری مثل کد فوق ممکن است کارآیی را کاهش دهد. یکی از نقاطی که در صورت پایین بودن کارآیی دستورات LINQ باید بررسی شود همین مورد CAST است. البته تنها استثنایی که در این مورد وجود دارد، توالی‌های غیر جنریک هستند (non generic Enumerable). در این حالت باید از Cast استفاده کرد.
متغیر Range در محدوده‌ی مورد استفاده باید از یک شناسه‌ی یکتا برخوردار باشد.
 string word="test";
List<string> list = new List<string> {"LINQ","Query","adventure"};
var query = from string word in list
where word.Contains("r")
orderby word ascending
select word;
همانطور که مشاهده می‌کنید کامپایلر خطای تعریف دو شناسه‌ی یکتا را در یک محدوده، اعلام می‌کند.

تا اینجا از طریق کلمه‌ی کلیدی from، متغیری را تعریف کردیم. با استفاده از کلمات کلیدی let ،into و join  نیز می‌توان متغیر‌های Range تعریف کرد.

عبارت let

کلمه‌ی کلیدی let این امکان را فراهم می‌کند تا یک متغیر Range جدید را ایجاد کرده و در عبارت‌های بعدی از آن استفاده کنیم. در کد زیر از طریق کلمه‌ی کلیدی let، یک متغیر Range جدید را بنام IsDairy تعریف می‌کنیم که از نوع bool  می‌باشد:
 Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 150},
   new Ingredient {Name = "Flour", Calories = 50},
   new Ingredient {Name = "Butter", Calories = 200}
};

IEnumerable<Ingredient> highCalDairyQuery =
from i in ingredients
let isDairy = i.Name == "Milk" || i.Name == "Butter"
where i.Calories >= 150 && isDairy
select i;

foreach (var ingredient in highCalDairyQuery)
{
   Console.WriteLine(ingredient.Name);
}
  متغیر isDairy در عبارت (clause) بعدی که where باشد، مورد استفاده قرار گرفته است. توجه داشته باشید که متغیر i  تعریف شده‌ی در ابتدای پرس و جو، در بخش Select قابل دسترسی است. دستور let باعث از دسترس خارج شدن متغیر در بخش بعدی نمی‌شود.

در کد زیر قصد داریم عملیات‌های زیر را بر روی توالی ورودی اعمال کنیم:
1- جدا کردن عناصر توالی ورودی بر اساس جدا کننده‌ی "  ," 
2- تبدیل همه‌ی حروف عناصر توالی ایجاد شده به حروف بزرگ
3- جدا کردن عناصر توالی حاصل از مرحله‌ی 2، به شرط برابر بودن با MILK,BUTTER,CHEESE
4- نمایش توالی ایجاد شده
string[] csvRecipes = { "milk,sugar,eggs", "flour,BUTTER,eggs", "vanilla,ChEEsE,oats" };
var dairyQuery = from csvRecipe in csvRecipes
let ingredients = csvRecipe.Split(',')
from ingredient in ingredients
let uppercaseIngredient = ingredient.ToUpper()
where
  uppercaseIngredient == "MILK" ||
  uppercaseIngredient == "BUTTER" ||
  uppercaseIngredient == "CHEESE"
select uppercaseIngredient;

foreach (var dairyIngredient in dairyQuery)
{
   Console.WriteLine($"{dairyIngredient} is dairy");
}
همانطور که مشاهده می‌کنید متغیر ایجاد شده‌ی توسط let می‌تواند یک مقدار عددی (مثال قبل) و یا یک مجموعه را در خود ذخیره کند (مثال فوق).

 عبارت Into 
متغیر جدیدی که توسط این دستور ایجاد می‌شود، می‌تواند نتیجه‌ی حاصل از دستور Select را در خود ذخیره کند. در کد زیر یک نوع بی‌نام ایجاد کرده و در ادامه‌ی پرس و جو از آن استفاده می‌کنیم:
 Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 150},
   new Ingredient {Name = "Flour", Calories = 50},
   new Ingredient {Name = "Butter", Calories = 200}
};

IEnumerable<Ingredient> highCalDairyQuery =
from i in ingredients
select new //نوع بی نام
{
  OriginalIngredient = i,
  IsDairy = i.Name == "Milk" || i.Name == "Butter",
  IsHighCalorie = i.Calories >= 150
}
into temp
where temp.IsDairy && temp.IsHighCalorie
select temp.OriginalIngredient;

foreach (var ingredient in highCalDairyQuery)
{
   Console.WriteLine(ingredient.Name);
}
نکته‌ای که در ابتدای این بحث اشاره شد، در این مثال خود را نشان می‌دهد و آن هم عدم دسترسی به متغیر i در بخش پایانی پرس و جو (select  نهایی) می‌باشد.

در ادامه‌ی این سری آموزشی، به بررسی عبارت join  می‌پردازیم. 
بازخوردهای دوره
آشنایی با نحوه ایجاد یک IoC Container
با وجود اینکه ما خودمان می‌تونیم مانند کد زیر کار وهله سازی را انجام دهیم 
var shopper = new Shopper(new Visa());
shopper.Charge();
چه لزومی به استفاده از IoC Container و کد
var resolver = new Resolver();
//تنظیمات اولیه
resolver.Register<Shopper, Shopper>();
resolver.Register<ICreditCard, Visa>();
//تزریق وابستگی‌ها و وهله سازی
var shopper = resolver.Resolve<Shopper>();
shopper.Charge();
وجود دارد، شاید بگید : "اگر وابستگی اول به وابستگی دوم برای وهله سازی نیاز دارد، کار وهله سازی وابستگی‌های وابستگی دوم نیز به صورت خودکار انجام خواهند شد. " میشه یه مثال ملموس‌تر بزنیم.
من یه خورده گیج شدم!
بازخوردهای دوره
متدهای async تقلبی
برای استفاده از یک وب سرویس، اگر به شکل زیر در یک ClassLibrary استفاده شود، آیا واقعا غیر همزمان عمل می‌شود یا تقلبی محسوب می‌شود؟
public bool SendSms()
{
    try
    {
        var result = false;
        foreach (var nomber in _fromNombers)
        {
            if (result) continue;
            if (_smsService.SendGroupSmsSimple(Model.Signature, nomber, Model.Numbers, Model.Body, false,
                string.Empty) == 1)
                result = true;
        }
        return result;
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);
        return false;
    }
}

private Task SendSmsAsync()
{
    return Task.Run(() => SendSms());
}
و مثلا در یک ConsoleApp:
static void Main(string[] args)
{
    var r = SendAsync();
    r.Wait();
}

private static async Task SendAsync()
{
    var model = new SmsModel
    {
        Body = "سلام",
        Numbers = new[] { "0917*******" }
    };

    var service = new SmsService(model);

    await service.SendSmsAsync();
}

نظرات مطالب
معرفی System.Text.Json در NET Core 3.0.
نمایش رسمی null در جاوا اسکریپت به این صورت است:
{ "Value": null }
بنابراین اگر چنین اطلاعاتی به سمت سرور ارسال شود، به درستی پردازش خواهد شد؛ اگرنه باید از تبدیلگر زیر استفاده کنید:
    public class StringJsonConverter : JsonConverter<byte?>
    {
        public override byte? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return reader.TokenType switch
            {
                JsonTokenType.Number => reader.GetByte(),
                JsonTokenType.Null => null,
                JsonTokenType.String =>
                    byte.TryParse(reader.GetString(), NumberStyles.Number, CultureInfo.InvariantCulture, out var parsed) ? parsed : (byte?)null,
                _ => throw new ArgumentOutOfRangeException(nameof(reader), reader.TokenType, "Cannot parse unexpected JSON token type.")
            };
        }

        public override void Write(Utf8JsonWriter writer, byte? value, JsonSerializerOptions options)
        {
            if (value.HasValue)
            {
                writer.WriteNumberValue(value.Value);
            }
            else
            {
                writer.WriteNullValue();
            }
        }
    }
نظرات مطالب
چگونگی گزارشگیری از Business Objects مانند List توسط StimulSoft
برای عدم نمایش یک صفحه از کد زیر استفاده کنید:
mainReport.Pages[0].Enabled = false;
mainReport.Pages["PageName"].Enabled = false;
ولی این کار شیوه درستی نیست.
بهتر است برای هر گزارش یک فایل mrt درست کنید و در هنگام گزارش‌گیری با توجه به نوع گزارش، فایل mrt مورد نظر را لود کرده و اطلاعات مورد نیاز را به آن ارسال کنید.
در مثال بالا می‌توانید دو فایل ProductsReport.mrt و CustomersReport.mrt تولید کنید و در هر کدام BusinessObject مورد استفاده خود گزارش را تولید کنید و با توجه به شرط خود فایل مورد نظر را لود  کنید:
var mrtName = "";
object businessObject = new object();
if (cusomerReportCondition)
{
    mrtName = "CustomersReport.mrt";
    businessObject = getCustomerReportData();
}
else if (productsReportCondition)
{
    mrtName = "ProductsReport.mrt";
    businessObject = getProductsReportData();
}
//else if ...

mainReport.Load(mrtName);
mainReport.RegBusinessObject("businessObjectName", businessObject);
به عنوان یک Best Practice بهتر است برای جمع آوری اطلاعات هر گزارش، یک فایل cs جداگانه تهیه کنید به طور مثال ProductsReport.cs و CustomersReport.cs؛ و تمام فایل‌های cs گزارش خود را به یک پروژه‌ی دیگر مانند ProjectName.Reports انتقال دهید.
نظرات مطالب
ثبت استثناهای مدیریت شده توسط ELMAH
باتشکر؛
با توجه به این که لایه‌های زیرین میبایست با Message با لایه بالاتر در ارتباط باشند و این خطاها در لایه بالاتر لاگ شوند ؛آیا ارجاع به اسمبلی Elmah در DataLayer کار درستی میباشد. برای مثال در یک برنامه Asp.net MVC برای نمایش خطاهای همزمانی به شکل زیر عمل کردن موردی ندارد؟
public async Task<string> SaveAllChangesAsync(bool invalidateCacheDependencies = true, Guid? auditUserId = null)
        {
            try
            {
                if (auditUserId.HasValue)
                    AuditFields(auditUserId.Value);
                await SaveChangesAsync();
                if (!invalidateCacheDependencies) return string.Empty;
                var changedEntityNames = GetChangedEntityNames();
                new EFCacheServiceProvider().InvalidateCacheDependencies(changedEntityNames);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
                return ConcurrencyMessage;
            }
            return string.Empty;
        }

پاسخ به بازخورد‌های پروژه‌ها
چند دیتا سورس به عنوان ورودی داده
خیلی خیلی ممنونم  از توضیحات شما
البته من در اس کیو ال عمل Pivot  رو انجام میدادم که با توضیحات شما اون رو به این روش تغییر دادم
الان من وقتی این کار رو انجام میدم دیباگ که میکنم دیتا مورد نظر من ایجاد میشه اما این خطا به من داده میشه
Source should be the result of the CrosstabExtension.Pivot method here 
و کد به این صورت نوشته شده است :
 .MainTableDataSource(dataSource =>
                                  {
                                      var crossTabList = Evaluations_Result.Pivot(x => new
                                      {
                                          گروه_سوال = x.QuestionGroupName,
                                          سوال = x.QuestionName,
                                          وزن = x.QuestionWeight.Value
                                      },
                                          x1 => x1.IndexName,
                                          x2 => x2.Count(x => x.IndexName != null));
                                      dataSource.Crosstab(crossTabList);                                    
                                  })
                                  .MainTableAdHocColumnsConventions(adHocColumnsConventions =>
                                  {
                                      adHocColumnsConventions.ShowRowNumberColumn(true);
                                      adHocColumnsConventions.RowNumberColumnCaption("ردیف");
                                  })
                                  .MainTableSummarySettings(summarySettings =>
                                  {
                                      summarySettings.OverallSummarySettings("جمع کل");
                                      summarySettings.PageSummarySettings("جمع صفحه");
                                  })
                                  .MainTableEvents(events =>
                                  {
                                      events.DataSourceIsEmpty(message: "اطلاعاتی برای نمایش وجود ندارد.");
}
و  Evaluations_Result پاسخ یک StoreProcedure است که از سمت دیتابیس میاد
نظرات مطالب
اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
با تشکر . یعنی موقعی که توکن ایجاد میشود از claimها هم برای ایجاد آن استفاده میشود؟در اینجا
  private async Task<(string AccessToken, IEnumerable<Claim> Claims)> createAccessTokenAsync(User user)
        {
            var claims = new List<Claim>
            {
                // Unique Id for all Jwt tokes
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString(), ClaimValueTypes.String, _configuration.Value.Issuer),
                // Issuer
                new Claim(JwtRegisteredClaimNames.Iss, _configuration.Value.Issuer, ClaimValueTypes.String, _configuration.Value.Issuer),
                // Issued at
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64, _configuration.Value.Issuer),
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), ClaimValueTypes.String, _configuration.Value.Issuer),
                new Claim(ClaimTypes.Name, user.Username, ClaimValueTypes.String, _configuration.Value.Issuer),
                new Claim("DisplayName", user.DisplayName, ClaimValueTypes.String, _configuration.Value.Issuer),
                // to invalidate the cookie
                new Claim(ClaimTypes.SerialNumber, user.SerialNumber, ClaimValueTypes.String, _configuration.Value.Issuer),
                // custom data
                new Claim(ClaimTypes.UserData, user.Id.ToString(), ClaimValueTypes.String, _configuration.Value.Issuer)
            };

           // add userclaims for permission
            var clmsuser= await _rolesService.FindUserClaimesAsync(user.Id);
            foreach (var cls in clmsuser)
            {
                claims.Add(new Claim(cls.ClaimType,cls.ClaimValue,ClaimValueTypes.String,_configuration.Value.Issuer));
            }
           
            // add roles
            var roles = await _rolesService.FindUserRolesAsync(user.Id);
            foreach (var role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role.Name, ClaimValueTypes.String, _configuration.Value.Issuer));
            }

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.Value.Key));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var now = DateTime.UtcNow;
            var token = new JwtSecurityToken(
                issuer: _configuration.Value.Issuer,
                audience: _configuration.Value.Audience,
                claims: claims,
                notBefore: now,
                expires: now.AddMinutes(_configuration.Value.AccessTokenExpirationMinutes),
                signingCredentials: creds);
            return (new JwtSecurityTokenHandler().WriteToken(token), claims);
        }

و هر تغییر در claimها در سمت کاربر برابر میشود با  برگشت توکن دستکاری شده از سمت سرور و مسدود شدن دسترسی؟