نظرات مطالب
اعتبارسنجی مبتنی بر کوکی‌ها در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
باسلام و عرض ارادت
من به مشکل زیر برخورد کردم

و به شکل زیر پیاده سازی CheckArgumentIsNull را در ApplicationDbContext انجام دادم:

public interface IUnitOfWork : IDisposable
    {
        DbSet<TEntity> Set<TEntity>() where TEntity : class;

        int SaveChanges(bool acceptAllChangesOnSuccess);
        int SaveChanges();
        Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken());
        Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken());
        void CheckArgumentIsNull(string v);
void CheckArgumentIsNull(string arg)
        {
            if (arg == null)
                throw new ArgumentNullException(nameof(arg));
        }

آیا کاری که کردم درست است؟

نظرات مطالب
سفارشی کردن ASP.NET Identity در MVC 5
با سلام و احترام 
من دستور زیر رو تو asp.net mvc Identity sample 2 تو اکشن Login  اضافه کردم 
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));         
    var user = UserManager.FindById(User.Identity.GetUserId());
اما user و null میده !
اکشن login
 public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            // This doen't count login failures towards lockout only two factor authentication
            // To enable password failures to trigger lockout, change to shouldLockout: true
            var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            switch (result)
            {

                case SignInStatus.Success:
                    {
                        UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
                        var user = UserManager.FindById(User.Identity.GetUserId());
                        return RedirectToLocal(returnUrl);
                    }
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }
        }

مطالب
ایجاد ویژگی‌های اعتبارسنجی سفارشی در ASP.NET Core 3.1 به همراه اعتبارسنجی سمت کلاینت آن‌ها
اگر بخواهیم یک Attribute سفارشی را برای اعتبارسنجی ایجاد کنیم، معمولا یک کلاس را ایجاد کرده و از ValidationAttribute ارث بری می‌کنیم و سپس متد IsValid آن‌را override میکنیم؛ با توجه به نیازی که به آن Attribute داریم. به عنوان مثال در ادامه یک Attribute را ایجاد کرده‌ایم که عمل مقایسه‌ی دو خاصیت را انجام میدهد و اگر مقدار خاصیتی که ویژگی LowerThan بر روی آن قرار دارد، از مقدار خاصیت دیگری که باید با آن مقایسه شود، کمتر نباشد، یک خطا را به ModelState اضافه میکنیم:
public class LowerThanAttribute : ValidationAttribute
{
    public LowerThanAttribute(string dependentPropertyName)
    {
        DependentPropertyName = dependentPropertyName;
    }

    public string DependentPropertyName { get; set; }
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int? currentPropertyValue = value as int?;
        currentPropertyValue ??= 0;
        var typeInfo = validationContext.ObjectInstance.GetType();
        var dependentPropertyValue = Convert.ToInt32(typeInfo.GetProperty(DependentPropertyName)
                                        .GetValue(validationContext.ObjectInstance, null));

        var displayDependentProperyName = typeInfo.GetProperty(DependentPropertyName)
                                        .GetCustomAttributes(typeof(DisplayAttribute), false)
                                        .Cast<DisplayAttribute>()
                                        .FirstOrDefault()?.Name;

        if (!(currentPropertyValue.Value < dependentPropertyValue))
        {
            return new ValidationResult("مقدار {0} باید کمتر باشد از " + displayDependentProperyName);
        }
        return ValidationResult.Success;
    }
}
ابتدا مقدار خاصیت مورد نظر را که میخواهیم با آن مقایسه شود، با استفاده از رفلکشن گرفته‌ایم و آن را در متغییر dependentPropertyValue ذخیره میکنیم. در ادامه مقدار Name را با استفاده از رفلکشن از DisplayAttribute میخوانیم و سپس عمل مقایسه را انجام میدهیم که اگر مقدار خاصیتی که ویژگی LowerThan بر روی آن قرار دارد، از مقدار خاصیت مورد نظر که مقدار آن را با استفاده از رفلکشن خوانده‌ایم، کمتر نباشد، یک خطا را به ModelState اضافه میکنیم.

اما یک مشکل! این عمل فقط در سمت سرور بررسی میشود و هنگامیکه ModelState.IsValid را در اکشن متد فراخوانی میکنیم، عمل اعتبارسنجی انجام میشود. یعنی همه‌ی داده‌ها به سمت سرور ارسال میشوند و اگر خطایی در ModelState وجود داشته باشد، کاربر مجددا باید داده‌ها را ارسال کند.

اما میتوان با استفاده از اینترفیس IClientModelValidator، عمل اعتبارسنجی را برای این ویژگی در سمت کلاینت انجام داد. برای انجام این کار ابتدا باید از اینترفیس IClientModelValidator ارث بری کنیم و متد AddValidation آن را پیاده سازی کنیم.
public class LowerThanAttribute : ValidationAttribute, IClientModelValidator
{
    public LowerThanAttribute(string dependentPropertyName)
    {
        DependentPropertyName = dependentPropertyName;
    }

    public string DependentPropertyName { get; set; }

    public void AddValidation(ClientModelValidationContext context)
    {
        var displayCurrentProperyName = context.ModelMetadata.ContainerMetadata
                                            .ModelType.GetProperty(context.ModelMetadata.PropertyName)
                                            .GetCustomAttributes(typeof(DisplayAttribute), false)
                                            .Cast<DisplayAttribute>()
                                            .FirstOrDefault()?.Name;

        var displayDependentProperyName = context.ModelMetadata.ContainerMetadata
                                            .ModelType.GetProperty(DependentPropertyName)
                                            .GetCustomAttributes(typeof(DisplayAttribute), false)
                                            .Cast<DisplayAttribute>()
                                            .FirstOrDefault()?.Name;


        MergeAttribute(context.Attributes, "data-val", "true");
        MergeAttribute(context.Attributes, "data-val-lowerthan", $"{displayCurrentProperyName} باید کمتر باشد از {displayDependentProperyName}");
        MergeAttribute(context.Attributes, "data-val-dependentpropertyname", "#" + DependentPropertyName);
    }
    private  bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
    {
        if (attributes.ContainsKey(key))
        {
            return false;
        }
        attributes.Add(key, value);
        return true;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int? currentPropertyValue = value as int?;
        currentPropertyValue ??= 0;
        var typeInfo = validationContext.ObjectInstance.GetType();
        var dependentPropertyValue = Convert.ToInt32(typeInfo.GetProperty(DependentPropertyName)
                                        .GetValue(validationContext.ObjectInstance, null));

        var displayCurrentProperyName = typeInfo.GetProperty(DependentPropertyName)
                                        .GetCustomAttributes(typeof(DisplayAttribute), false)
                                        .Cast<DisplayAttribute>()
                                        .FirstOrDefault()?.Name;

        if (!(currentPropertyValue.Value < dependentPropertyValue))
        {
            return new ValidationResult("مقدار {0} باید کمتر باشد از " + displayCurrentProperyName);
        }
        return ValidationResult.Success;
    }
}
اینترفیس IClientModelValidator، یک متد به نام AddValidation دارد که این امکان را فراهم میکند تا بتوانیم اعتبارسنجی را در سمت کلاینت انجام دهیم. در ادامه باید با استفاده از JQuery اعتبارسنجی مخصوص این ویژگی را در سمت کلاینت پیاده سازی کنیم. در متد AddValidation فقط اسم تابع و پارامتر‌های مورد نیاز در سمت کلاینت را مشخص میکنیم. به عنوان مثال در مثال بالا یک تابع را معرفی کرده‌ایم به نام lowerthan که بعدا باید آنرا در سمت کلاینت پیاده سازی کنیم و نام خاصیتی را که باید با آن مقایسه شود، با نام data-val-dependentpropertyname معرفی کرده‌ایم. در کد زیر، این اعتبار سنجی سمت کلاینت را پیاده سازی کرده ایم. lowerthan نام متدی است که آنرا در متد AddValidation اضافه کردیم. مقدار value همان مقدار خاصیتی است که ویژگی LowerThan بر روی آن قرار دارد و otherPropId نام خاصیتی است که باید با آن مقایسه شود که آنرا از element خوانده‌ایم:
jQuery.validator.addMethod("lowerthan", function (value, element, param) {
    var otherPropId = $(element).data('val-dependentpropertyname');
    if (otherPropId) {
        var otherProp = $(otherPropId);
        if (otherProp) {
            var otherVal = otherProp.val();
            if (parseInt(otherVal) > parseInt(value)) {
                return true;
            }
            return false;
        }
    }
    return true;
});
jQuery.validator.unobtrusive.adapters.addBool("lowerthan");
کدهای جاواسکریپتی بالا را در یک فایل جدید به نام LowerThan.js ذخیره کرده‌ایم که باید آن را به صفحه خود اضافه کنیم:
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script src="~/js/LowerThan.js"></script>
سپس برای استفاده، باید ویژگی LowerThan را بر روی خاصیت مورد نظر قرار دهیم؛ مانند زیر:
public class User
{
    [Required]
    [Display(Name ="نام کاربری")]
    public string Username { get; set; }
    [Required]
    [Display(Name = "سن")]
    public int Age { get; set; }
    [LowerThan(nameof(Age))]
    [Required]
    [Display(Name = "سابقه کار")]
    public int Experience { get; set; }
}
و در نهایت اگر مقدار خاصیت Experience که ویژگی LowerThan بر روی آن قرار دارد، از مقدار خاصیت Age که باید با آن مقایسه شود، کمتر باشد، true برگردانده میشود؛ اما اگر بزرگتر یا مساوی باشد، متن خطایی را که در متد AddValidation اضافه کردیم، نشان داده خواهد شد.
 

نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت چهارم - User Claims
مفهومی در ASP.NET Core Identity وجود دارد به نام AutomaticAuthenticate و AutomaticChallenge که سبب می‌شوند کاربر با وارد کردن آدرس سایت، به صورت خودکار اعتبارسنجی شود (اگر پیشتر کوکی لاگین او وجود داشته باشد). این موارد برای فعالسازی به تنظیمات ذیل نیاز دارند:
در متد ConfigureServices:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
و همچنین در متد Configure:
app.UseAuthentication();
البته اگر از سیستم Identity استفاده می‌کنید، مورد اول به صورت خودکار با فراخوانی متد AddIdentity تنظیم شده‌است. فقط مورد دوم را هم باید اضافه کنید.

زمانی متد CallBackResult فراخوانی می‌شود و از فیلتر Authorize رد خواهید شد که کار اعتبارسنجی کاربر با موفقیت صورت گرفته باشد (همان
AutomaticAuthenticate و AutomaticChallenge فوق). بنابراین اگر تنظیمات اولیه‌ی Identity شما به درستی انجام شده باشد، صرفا مشکل مسیریابی را دارید که مقادیر را نال دریافت می‌کنید.
مطالب
C# 12.0 - Primary Constructors
قابلیتی تحت عنوان Primary Constructors به C# 12 اضافه شده‌است که ... البته جدید نیست! این قابلیت از زمان C# 9، با ارائه‌ی رکوردها، به زبان #C اضافه شد و در طی چند نگارش بعدی، توسعه و تکامل یافت (برای مثال اضافه شدن records for structs به C# 10) تا در C# 12، به کلاس‌های معمولی نیز تعمیم پیدا کرد. این ویژگی را در ادامه با جزئیات بیشتری بررسی می‌کنیم.


Primary Constructors چیست؟

Primary Constructors، قابلیتی است که به C# 12 اضافه شده‌است تا توسط آن بتوان خواص را مستقیما توسط پارامترهای سازنده‌ی یک کلاس تعریف و همچنین مقدار دهی کرد. هدف از آن، کاهش قابل ملاحظه‌ی یکسری کدهای تکراری و مشخص است تا به کلاس‌هایی زیباتر، کم‌حجم‌تر و خواناتر برسیم. برای مثال کلاس متداول زیر را درنظر بگیرید:
public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime HireDate { get; set; }
    public decimal Salary { get; set; }

    public Employee(string firstName, string lastName, DateTime hireDate, decimal salary)
    {
        FirstName = firstName;
        LastName = lastName;
        HireDate = hireDate;
        Salary = salary;
    }
}
در زبان ‍#C، سازنده، متد ویژه‌ای است که در حین ساخت نمونه‌ای از یک کلاس، فراخوانی می‌شود. هدف از آن‌، آغاز و مقدار دهی حالت شیء ایجاد شده‌است که عموما با مقدار دهی خواص آن شیء، انجام می‌شود.
اکنون اگر بخواهیم همین کلاس را با استفاده از ویژگی Primary Constructor اضافه شده به C# 12.0 بازنویسی کنیم، به قطعه کد زیر می‌رسیم:
public class Employee(string firstName, string lastName, DateTime hireDate, decimal salary)
{
    public string FirstName { get; set; } = firstName;
    public string LastName { get; set; } = lastName;
    public DateTime HireDate { get; set; } = hireDate;
    public decimal Salary { get; set; } = salary;
}
و نحوه‌ی نمونه سازی از آن به صورت زیر است:
var employee = new Employee("John", "Doe", new DateTime(2020, 1, 1), 50000);

یک نکته: اگر از Rider و یا ReSharper استفاده می‌کنید، یک چنین Refactoring توکاری جهت سهولت کار، به آن‌ها اضافه شده‌است و به سرعت می‌توان این تبدیلات را توسط آن‌ها انجام داد.




توضیحات:
- متد سازنده در این حالت، به ظاهر حذف شده و به قسمت تعریف کلاس منتقل شده‌است.
- تمام مقدار دهی‌های آغازین موجود در متد سازنده‌ی پیشین نیز حذف شده‌اند و مستقیما به قسمت تعریف خواص، منتقل شده‌اند.
در نتیجه از یک کلاس 15 سطری، به کلاسی 7 سطری رسیده‌ایم که کاهش حجم قابل ملاحظه‌ای را پیدا کرده‌است.

نکته 1: هیچ ضرورتی وجود ندارد که به همراه یک primary constructor، خواصی هم مانند مثال فوق ارائه شوند؛ چون پارامترهای آن در تمام اعضای این کلاس، به همین شکل، قابل دسترسی هستند. در این مثال صرفا جهت بازسازی کد قبلی، این خواص اضافی را مشاهده می‌کنید. یعنی اگر تنها قرار بود، کار تزریق وابستگی‌ها صورت گیرد که عموما به همراه تعریف فیلدهایی جهت انتساب پارامترهای متد سازنده به آن‌ها است، استفاده از یک primary constructor، کدهای فوق را بیش از این هم فشرده‌تر می‌کرد و ... یک سطری می‌شد.

نکته 2: استفاده از پارامترهای سازنده‌ی اولیه، صرفا جهت مقدار دهی خواص عمومی یک کلاس، یک code smell هستند! چون می‌توان یک چنین کارهایی را به نحو شکیل‌تری توسط required properties معرفی شده‌ی در C# 11، پیاده سازی کرد.


بررسی تاریخچه‌ی primary constructors

همانطور که در مقدمه‌ی بحث نیز عنوان شد، primary constructors قابلیت جدیدی نیست و برای نمونه به همراه C# 9 و مفهوم جدید رکوردهای آن، ارائه شد:
public record class Book(string Title, string Publisher);
مثال فوق که به positional syntax هم معروف است، به همراه بکارگیری primary constructors است. در اینجا کامپایلر به صورت خودکار، کار تولید کدهای خواص متناظر را که از نوع get و init دار هستند، انجام می‌دهد. در این حالت به علت استفاده از init accessors، پس از نمونه سازی شیءای از آن، دیگر نمی‌توان مقدار خواص متناظر را تغییر داد.
پس از آن در C# 10، این توسعه ادامه یافت و به امکان تعریف record structها، بسط یافت که در اینجا هم قابلیت تعریف primary constructors وجود دارد:
public record struct Color(int R, int G, int B);
که البته در این حالت برخلاف record classها، کامپایلر، کدی را که برای خواص تولید می‌کند، get و set دار است. در اینجا اگر نیاز است به همان حالت خواص get و init دار رسید، می‌توان یک readonly record struct را تعریف کرد.

پس از این مقدمات، اکنون در C# 12 نیز می‌توان primary constructors را به تمام کلاس‌ها و structهای معمولی هم اعمال کرد؛ با این تفاوت که در اینجا برخلاف رکوردها، کدهای خواص‌های متناظر، به صورت خودکار تولید نمی‌شوند و اگر به آن‌ها نیاز دارید، باید آن‌ها را همانند مثال ابتدای بحث، خودتان به صورت دستی تعریف کنید.


primary constructors کلاس‌ها و structهای معمولی، با primary constructors رکوردها یکی نیست

در C# 12 و به همراه معرفی primary constructors مخصوص کلاس‌ها و structهای معمولی آن، از روش متفاوتی برای دسترسی به پارامترهای تعریف شده، استفاده می‌کند که به آن capturing semantics هم می‌گویند. در این حالت پارامترهای تعریف شده‌ی در یک primary constructor، توسط هر عضوی از آن کلاس قابل استفاده‌است که یکی از کاربردهای آن، ساده کردن تعاریف تزریق وابستگی‌ها است. در این حالت دیگر نیازی نیست تا ابتدا یک فیلد را برای انتساب به پارامتر تزریق شده تعریف کرد و سپس از آن فیلد، استفاده نمود؛ مستقیما می‌توان با همان پارامتر تعریف شده، در متدها و اعضای کلاس، کار کرد.
برای مثال سرویس زیر را که از تزریق وابستگی‌ها، در سازنده‌ی خود استفاده می‌کند، درنظر بگیرید:
public class MyService
{
    private readonly IDepedent _dependent;
  
    public MyService(IDependent dependent)
    {
        _dependent = dependent;
    }
  
    public void Do() 
    {
        _dependent.DoWork();
    }
}
این کلاس در C# 12 به صورت زیر خلاصه شده و پارامتر dependent تعریف شده‌ی در سازنده‌ی اولیه‌ی آن، به همان شکل و بدون نیاز به کد اضافی، در سایر متدهای این کلاس قابل استفاده‌است:
public class MyService(IDependent dependent)
{
    public void Do() 
    {
        dependent.DoWork();
    }
}

البته مفهوم Captures هم در زبان #C جدید نیست و در ابتدا به همراه anonymous methods و بعدها به همراه lambda expressions، معرفی و بکار گرفته شد. برای مثال درون یک lambda expression، اگر از متغیری خارج از آن lambda expressions استفاده شود، کامپایلر یک capture از آن متغیر را تهیه کرده و استفاده می‌کند.

بنابراین به صورت خلاصه primary constructors در رکوردها، با هدف تعریف خواص عمومی فقط خواندنی، ارائه شدند؛ اما primary constructors ارائه شده‌ی در C# 12 که اینبار قابل اعمال به کلاس‌ها و structs معمولی است، بیشتر هدف ساده سازی تعریف کدهای تکراری private fields را دنبال می‌کند. برای نمونه این کدی است که کامپایلر برای primary constructor مثال ابتدای بحث تولید می‌کند و در اینجا نحوه‌ی تولید خودکار این فیلدهای خصوصی را مشاهده می‌کنید:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace CS8Tests
{
  [NullableContext(1)]
  [Nullable(0)]
  public class Employee
  {
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <FirstName>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <LastName>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private DateTime <HireDate>k__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private Decimal <Salary>k__BackingField;

    public Employee(string firstName, string lastName, DateTime hireDate, Decimal salary)
    {
      this.<FirstName>k__BackingField = firstName;
      this.<LastName>k__BackingField = lastName;
      this.<HireDate>k__BackingField = hireDate;
      this.<Salary>k__BackingField = salary;
      base..ctor();
    }

    public string FirstName
    {
      [CompilerGenerated] get
      {
        return this.<FirstName>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<FirstName>k__BackingField = value;
      }
    }

    public string LastName
    {
      [CompilerGenerated] get
      {
        return this.<LastName>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<LastName>k__BackingField = value;
      }
    }

    public DateTime HireDate
    {
      [CompilerGenerated] get
      {
        return this.<HireDate>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<HireDate>k__BackingField = value;
      }
    }

    public Decimal Salary
    {
      [CompilerGenerated] get
      {
        return this.<Salary>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<Salary>k__BackingField = value;
      }
    }
  }
}
بنابراین آیا پارامترهای سازنده‌ی اولیه، به صورت خواص تعریف می‌شوند و قابلیت تغییر میدان دید آن‌ها میسر است؟ پاسخ: خیر. این پارامترها توسط کامپایلر، به صورت فیلدهای خصوصی در سطح کلاس، تعریف و استفاده می‌شوند. یعنی تمام اعضای کلاس، البته منهای سازنده‌های ثانویه، به این پارامترها دسترسی دارند. همچنین، این تولید کد هم بهینه‌است و صرفا برای پارامترهایی انجام می‌شود که واقعا در کلاس استفاده شده باشند؛ درغیر اینصورت، فیلد خصوصی متناظری برای آن‌ها تولید نخواهد شد.

یک نکته: برای مشاهده‌ی یک چنین کدهایی می‌توانید از منوی Tools->IL Viewer برنامه‌ی Rider استفاده کرده و در برگه‌ی ظاهر شده، گزینه‌ی #Low-Level C آن‌را انتخاب نمائید.


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

اگر به کدهای #Low-Level C تولیدی فوق دقت کنید، این کلاس، به همراه یک سازنده‌ی خالی بدون پارامتر (parameter less constructor) نیست و سازنده‌ی پیش‌فرضی (default constructor) برای آن درنظر گرفته نشده‌است ... اما اگر کلاسی به همراه یک primary constructor تعریف شد، می‌توان با استفاده از واژه‌ی کلیدی this، سازنده‌ی ثانویه‌ای را هم برای آن تعریف کرد:
public class Person(string firstName, string lastName) 
{
    public Person() : this("John", "Smith") { }
    public Person(string firstName) : this(firstName, "Smith") { }
    public string FullName => $"{firstName} {lastName}";
}
در اینجا نحوه‌ی تعریف یک Default constructor بدون پارامتر را هم ملاحظه می‌کنید.


امکان ارث‌بری و تعریف سازنده‌ی اولیه

مثال زیر را درنظر بگیرید که در آن کلاس مشتق شده‌ی از کلاس User، یک سازنده‌ی اولیه را تعریف کرده:
public class User
{
    public User(string firstName, string lastName) { }
}

public class Editor(string firstName, string lastName) : User
{
}
در این حالت برنامه با خطای «Base class 'CS8Tests.User' does not contain parameterless constructor» کامپایل نمی‌شود. عنوان می‌کند که اگر کلاس مشتق شده می‌خواهد سازنده‌ی اولیه‌ای داشته باشد، باید کلاس پایه را به همراه یک سازنده‌ی پیش‌فرض بدون پارامتر تعریف کنید.
البته این محدودیت با structها وجود ندارد؛ چون structها، value type هستند و همواره به صورت پیش‌فرض، به همراه یک سازنده‌ی پیش فرض بدون پارامتر، تولید می‌شوند.
یک مثال: قطعه کد متداول ارث‌بری زیر را درنظر بگیرید که در آن، کلاس مشتق شده به کمک واژه‌ی کلید base، امکان تعریف سازنده‌ی جدیدی را یافته و یکی از پارامترهای سازنده‌ی کلاس پایه را مقدار دهی می‌کند:
public class Automobile
{
    public Automobile(int wheels, int seats)
    {
        Wheels = wheels;
        Seats = seats;
    }

    public int Wheels { get; }
    public int Seats { get; }
}

public class Car : Automobile
{
    public Car(int seats) : base(4, seats)
    {
    }
}
این تعاریف در C# 12 به صورت زیر خلاصه می‌شوند:
public class Automobile(int wheels, int seats)
{
    public int Wheels { get; } = wheels;
    public int Seats { get; } = seats;
}

public class Car(int seats) : Automobile(4, seats);

و یا یک نمونه مثال دیگر آن به صورت زیر است که در آن، ذکر بدنه‌ی کلاس در C# 12، الزامی ندارد:
public class MyBaseClass(string s); // no body required

public class Derived(int i, string s, bool b) : MyBaseClass(s)
{
    public int I { get; set; } = i;
    public string B => b.ToString();
}


توصیه به پرهیز از double capturing

با مفهوم capture در این مطلب آشنا شدیم. در مثال زیر دوبار از پارامتر سازنده‌ی age، در دو قسمت عمومی شده، استفاده شده‌است:
public class Human(int age)
{
    // initialization
    public int Age { get; set; } = age;

    // capture
    public string Bio => $"My age is {age}!";
}
در این حالت ممکن است استفاده کننده در طول برنامه، با وضعیت ناخواسته‌ی زیر مواجه شود:
var p = new Human(42);
Console.WriteLine(p.Age); // Output: 42
Console.WriteLine(p.Bio); // Output: My age is 42!

p.Age++;
Console.WriteLine(p.Age); // Output: 43
Console.WriteLine(p.Bio); // Output: My age is 42! // !
در اینجا پس از افزودن مقداری به خاصیت عمومی Age، زمانیکه به مقدار عبارت Bio مراجعه می‌شود، خروجی قبلی را دریافت می‌کنیم!
درک بهتر آن، نیاز به #Low-Level C کلاس Human را دارد:
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace CS8Tests
{
  [NullableContext(1)]
  [Nullable(0)]
  public class Human
  {
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <age>P;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <Age>k__BackingField;

    public Human(int age)
    {
      this.<age>P = age;
      this.<Age>k__BackingField = this.<age>P;
      base..ctor();
    }

    public int Age
    {
      [CompilerGenerated] get
      {
        return this.<Age>k__BackingField;
      }
      [CompilerGenerated] set
      {
        this.<Age>k__BackingField = value;
      }
    }

    public string Bio
    {
      get
      {
        DefaultInterpolatedStringHandler interpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 1);
        interpolatedStringHandler.AppendLiteral("My age is ");
        interpolatedStringHandler.AppendFormatted<int>(this.<age>P);
        interpolatedStringHandler.AppendLiteral("!");
        return interpolatedStringHandler.ToStringAndClear();
      }
    }
  }
}
همانطور که مشاهده می‌کنید، کامپایلر، پارامتر age را دوبار، جداگانه capture کرده‌است:
public Human(int age)
{
   this.<age>P = age;
   this.<Age>k__BackingField = this.<age>P;
   base..ctor();
}
به همین جهت است که ++p.Age، فقط بر روی یکی از فیلدهای capture شده تاثیر داشته و بر روی دیگری خیر. به این مورد، double capturing گفته می‌شود و توصیه شده از آن پرهیز کنید و بجای استفاده‌ی دوباره از پارامتر age، از خود خاصیت Age استفاده نمائید.
نظرات مطالب
ASP.NET MVC #18
سلام آقای نصیری
با توجه به این نوع پیاده سازی[لایه دسترسی به داده‌ها توسط service layer] (+ ) اگر خواسته باشیم نقش‌های یک کاربر را بدست بیاوریم، باید از لایه‌ی سرویس استفاده کنیم؟ یعنی شبیه به تصویر اول در این کامنت (+ ) لازم است که متغیر هایی را از نوع اینترفیس‌های لایه سرویس تعریف و بعد استفاده کنیم؟
چون در صورت استفاده از لایه سرویس ،مشکلاتی در کوئری گرفتنم به وجود میومد. یا بهتره بگم طرز استفاده از اونها رو نمیدونم.
آیا این کد قابل قبوله؟ 
public class CustomRoleProvider : System.Web.Security.RoleProvider
    {
        public override bool IsUserInRole(string username, string roleName)
        {
            //if (username.ToLowerInvariant() == "ali" && roleName.ToLowerInvariant() == "User")
            //    return true;
            // blabla ...  
            return true;
        }

        public override string[] GetRolesForUser(string username)
        {
            using (var context = new PublishingContext())
            {
                var user = context.Users.Where(x => x.Username == username).FirstOrDefault();

                var roles = from ur in user.Roles
                            from r in context.Roles
                            where ur.Id == r.Id
                            select r.Role; //نام نقش
                if (roles != null)
                {
                    return roles.ToArray();
                }
            }
            
            return new string[] {};
        }
}
نظرات مطالب
اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.....
});
پس از آن در یک برنامه‌ی مبتنی بر ASP.NET Core Identity، هر جائیکه فیلتر Authorize خالی وجود دارد، یعنی استفاده‌ی از حالت مبتنی بر کوکی‌ها. جهت استفاده‌ی از اطلاعات اعتبارسنجی مبتنی بر توکن‌ها، ذکر صریح AuthenticationSchemes ضروری است:
روش الف) ذکر Schemes مد نظر در یک Policy جدید
services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
    {
        policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new MinimumAgeRequirement());
    });
});
و سپس استفاده‌ی از آن
[Authorize(Policy = "Over18")]
روش ب) ذکر مستقیم Schemes در فیلتر Authorize
[Authorize(Policy = ...., AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
// Or
[Authorize(Policy = ...., AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme + ", " + JwtBearerDefaults.AuthenticationScheme)]
مطالب
ساخت ربات تلگرامی با #C
 با رشد دنیای تکنولوژی، وسائل هوشمند همراه نیز به سرعت پیشرفته‌تر شدند. در این میان با گسترش زیرساخت اینترنت، رشد شبکه‌های اجتماعی نیز چشمگیر بوده است. یکی از بهترین این‌ها، شبکه‌های تلگرام می‌باشد که با بهره گیری از سرورهای ابری، امنیت و سرعت را برای کاربران به ارمغان آورده است.
چندی پیش موسسان تلگرام با معرفی API‌های کاربردی، به توسعه کنندگان اجازه دادند که با بهره گیری از بستر این شبکه، اقدام به تولید اینترفیسی به اسم بات کنند که با دریافت دستورات سفارشی، عملیات خاصی را انجام دهد.
در واقع تلگرام و متدهای ارائه شده، یک راه ارتباطی بین کاربران و برنامه‌های تولید شده را ایجاد کردند که با قدری ذوق و سلیقه، شاهد بات‌های جالب و کاربردی هستیم.
در این مقاله سعی شده طرز تهیه یک بات با زبان #C توضیح داده شود.
در ابتدا شما باید توسط یکی از بات‌های اصلی تلگرام اقدام به ثبت نام کاربری و تنظیمات بات مورد نظر خودتان نمایید. بات مورد نظر @BotFather می‌باشد که با شروع مکالمه می‌توان با فرستادن دستورات مختلف تنظیمات مختلفی را انجام داد. با شروع مکالمه با بات مورد نظر با دستور /start دستورات زیر قابل انجام می‌باشد:

 You can control me by sending these commands :

/ newbot - create a new bot

/ token - generate authorization token

/ revoke - revoke bot access token

/ setname - change a bot's name

/ setdescription - change bot description

/ setabouttext - change bot about info

/ setuserpic - change bot profile photo

/ setcommands - change bot commands list

/ setjoingroups - can your bot be added to groups ?

/ setprivacy - what messages does your bot see in groups ?

/ deletebot - delete a bot

/ cancel - cancel the current operation
با انجام دستور /newbot در ابتدا نام بات و یوزنیم (دقت کنید یوزرنیم می‌بایست حتما به کلمه‌ی bot ختم شود) را تنظیم کنید.
بعد از تایید نام و یوزر نیم، به شما یک توکن اختصاص داده می‌شود که توسط آن شما شناسایی می‌شوید.
در اینجا شما می‌توانید تنظیمات اضافه‌تری مانند عکس برای پروفایل و غیره را نیز تنظیم کنید.
در مرحله‌ی بعد می‌توانید در همین قسمت دستورات مورد نظر را جهت بات خود تنظیم کنید. برای این کار باید دستور /setcommands را وارد کنید و دستور مورد نظر خود را به فرمت command1 – Description وارد کنید.
مرحله‌ی بعد، تنظیمات برنامه‌ی شما جهت دریافت دستورات وارد شده و انجام عملیات مورد نظر و تولید و ارسال خروجی مورد نظر است.

دریافت دستورات به دو طریق انجام می‌شود:
1. توسط دستور getUpdates می‌توان تمامی کامندهای دریافتی را از سرور تلگرام دریافت کرد و با انجام پروسس‌های لازم، خروجی را به کاربر مورد نظر ارسال کرد.
2. توسط تابع webhook از تلگرام درخواست کرد در صورت دریافت دستور جدید به بات، این دستور را به یک آدرس خاص ارسال کرد.

قابل توجه است که می‌توان فقط از یکی از دو روش فوق استفاده کرد. همچنین در روش دوم حتما سرور مورد نظر باید گواهی ssl تایید شده داشته باشد.
کد زیر دریافت کامندهای یک بات به روش اول می‌باشد :
public class mydata
    {
        public result[] result;
    }
    public class result
    {
        public int update_id { get; set; }
        public message message { get; set; }
    }
    public class message
    {
        public int message_id { get; set; }
        public message_from from { get; set; }
        public message_chat chat { get; set; }
        public int date { get; set; }
        public string text { get; set; }
    }
    public class message_from
    {
        public int ind { get; set; }
        public string first_name { get; set; }
        public string username { get; set; }
    }
    public class message_chat
    {
        public int id { get; set; }
        public string first_name { get; set; }
        public string username { get; set; }
    }
 
public  Void GetUpdates()
        {
 
            WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + yourToken + "/getUpdates");
            req.UseDefaultCredentials = true;
            WebResponse resp = req.GetResponse();
            Stream stream = resp.GetResponseStream();
            StreamReader sr = new StreamReader(stream);
            string s = sr.ReadToEnd();
            sr.Close();
            var jobject = Newtonsoft.Json.Linq.JObject.Parse(s);
            mydata gg = JsonConvert.DeserializeObject<mydata>(jobject.ToString());
            List<result> results = new List<result>();
            foreach (result rs in gg.result)
            {
                results.Add(rs); 
                SendMessage(rs.message.chat.id.ToString(), "hello"+" "+"Dear"+rs.message.chat.first_name); 
            }             
        }
و توسط تابع زیر می‌توان به کاربری که به بات کامند ارسال کرد، پاسخ داد:
public static void SendMessage(string chat_id, string message)
        {
            WebRequest req = WebRequest.Create("https://api.telegram.org/bot" + youToken + "/sendMessage?chat_id=" + chat_id + "&text=" + message);
            req.UseDefaultCredentials = true;
 
            var result = req.GetResponse();
            req.Abort();
        }

لازم به ذکر است خروجی توابع بات‌های تلگرام با فرمت JSON می‌باشد که با نصب پکیج NewTonsoft می‌توان آن را به لیست تبدیل کرد.
rs.message.chat.id، آی دی فردی است که به بات تلگرامی ما مسیج ارسال کرده است.
rs.message.chat.first_name نام فردی است که به بات تلگرام مسیج ارسال کرده است.
همچنین می‌توان در جواب کامند بات، علاوه بر متن، صدا و تصویر را نیز ارسال نمود .

در این لینک و این لینک می‌توان توضیحات بیشتری را در این زمینه مطالعه کرد.
در انتها خوشحال می‌شوم ذوق‌ها و ایده‌های شما را در ساخت بات‌ها با آیدی @iekhtiari مشاهده کنم.
مطالب
توصیه‌هایی در مورد overloading متدها

مطلب زیر چکیده‌ای است از کتاب Framework Design Guidelines در مورد سربارگذاری توابع

الف) از یک نوع خروجی استفاده کنید

مثال زیر را در نظر بگیرید:

public User[] GetGroupMembers(int groupId)
public List<User> GetGroupMembers(string groupName)

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

ب) نام‌های آرگومان‌های توابع سربارگذاری شده شما باید یکسان باشند

در مثال زیر، از اولین آرگومان جهت دریافت شناسه‌ی یک گروه استفاده می‌شود:

public List<User> GetGroupMembers(int groupId)
public List<User> GetGroupMembers(int id, int pageIndex, int pageSize)

اما همانطور که ملاحظه می‌کنید در یکی از groupId استفاده شده و در دیگری از نام id . این روش مزموم است! از آن پرهیز کنید! (هر دو یا باید groupId باشند و یا id . این هماهنگی و یکسان بودن باید در توابع سربارگذاری شده‌ی شما حفظ شود)

ج) ترتیب آرگومان‌ها را در توابع سربارگذاری شده حفظ و رعایت نمائید

لطفا به مثال زیر دقت کنید:

public List<User> GetGroupMembers(int groupId)
public List<User> GetGroupMembers(int pageIndex, int pageSize, int groupId)

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

د) از call forwarding استفاده کنید

جهت توضیح بهتر call forwarding به مثال زیر دقت نمائید:
public List<User> GetGroupMembers(int groupId)
{
return GetGroupMembers(groupId, 0, 10);
}

public List<User> GetGroupMembers(int groupId, int pageIndex, int pageSize)
{
return GetGroupMembers(groupId, pageIndex, pageSize, SortOrder.Ascending);
}

public List<User> GetGroupMembers(int groupId, int pageIndex, int pageSize, SortOrder sortOrder)
{
var query = new GroupQuery();
query.GroupID = groupId;
query.PageIndex = pageIndex;
query.PageSize = pageSize;
query.SortOrder = sortOrder;

return GetGroupMembers(query);
}

public List<User> GetGroupMembers(GroupQuery groupQuery)
{
// Actual implementation to get group members goes here
}

معنای call forwarding این است که هنگام پیاده سازی توابعی از این دست، کوچکترین تابع سربارگذاری شونده باید تابع بزرگتر بعدی خود را صدا بزند و همین‌طور الی آخر که در مثال فوق به خوبی آشکار است. در این مثال چهارمین تابع سربارگذاری شده، بیشترین حوضه‌ی تمرکز را در خود دارد و توابع دیگر می‌توانند از آن بهره جویند بدون اینکه پیاده سازی خاصی را ارائه دهند.

ه) زیاده‌ روی نکنید!

آخرین موردی که توصیه شده این است که در تهیه توابع سربارگذاری شده زیاده روی نکنید. در این نوع موارد باید قانون 80/20 را در نظر گرفت. 2 تا 3 تابع سربارگذاری شده ارائه دهید که 80 درصد کار را انجام می‌دهند و سپس یک تابع سربارگذاری شده‌ی دیگر ارائه دهید که 20 درصد باقیمانده را پوشش دهد.


پ.ن.
در سی شارپ 4 ، با معرفی optional parameters ، شاید کمتر به سربارگذاری نیاز باشد.

مطالب
متد جدید Order در دات نت 7
دات نت 7 به همراه دو متد جدید Order و OrderDescending است که مرتب سازی مجموعه‌های ساده را انجام می‌دهند.


روش متداول مرتب سازی مجموعه‌های ساده تا پیش از دات نت 7

فرض کنید لیستی از اعداد را داریم:
var numbers = new List<int> { -7, 1, 5, -6 };
تا پیش از دات نت 7 با استفاده از متدهای OrderBy و OrderByDescending موجود به همراه LINQ، امکان مرتب سازی صعودی و نزولی این لیست وجود دارد:
var sortedNumbers1 = numbers.OrderBy(n => n);
var sortedNumbers2 = numbers.OrderByDescending(n => n);
که در اینجا ذکر پارامتر keySelector ضروری است:
public static IOrderedEnumerable<TSource> OrderBy<TSource,TKey>(
   [NotNull] this IEnumerable<TSource> source,
   [NotNull] Func<TSource,TKey> keySelector)
هرچند می‌شد طراحی آن ساده‌تر باشد و حداقل برای مجموعه‌های ساده، نیازی به ذکر آن نباشد.


روش جدید مرتب سازی مجموعه‌های ساده در دات نت 7

دات نت 7 به همراه دو متد جدید Order و OrderDescending است که دیگر نیازی به ذکر پارامتر keySelector ذکر شده را ندارند:
var sortedNumbers3 = numbers.Order();
var sortedNumbers4 = numbers.OrderDescending();
و امضای آن‌ها به صورت زیر است:
public static IOrderedEnumerable<T> Order<T>(this IEnumerable<T> source)
public static IOrderedEnumerable<T> OrderDescending<T>(this IEnumerable<T> source)
که در حقیقت دو متد الحاقی جدید قابل اعمال بر روی انواع و اقسام IEnumerableها هستند.


در مورد سایر مجموعه‌های پیچیده چطور؟

فرض کنید کلاس User را:
public class User
{
   public string Name { set; get; }
   public int Age { set; get; }
}
 به همراه لیستی از آن تعریف کرده‌ایم:
List<User> users = new()
                           {
                               new User { Name = "User 1", Age = 34 },
                               new User { Name = "User 2", Age = 24 },
                           };
سؤال: آیا اگر متد Order را بر روی این لیست فراخوانی کنیم:
var orderedUsers = users.Order();
برای مثال این مجموعه بر اساس نام و سن مرتب خواهد شد؟ که پاسخ آن خیر است و همچنین استثنائی را صادر می‌کند بر این مبنا که باید کلاس User، اینترفیس IComparable را پیاده سازی کند تا بتوان آن‌ها را مقایسه کرد؛ برای مثال چیزی شبیه به تغییرات زیر:
public class User : IComparable<User>
{
    public string Name { set; get; }
    public int Age { set; get; }

    public int CompareTo(User? other)
    {
        if (ReferenceEquals(this, other))
        {
            return 0;
        }

        if (ReferenceEquals(null, other))
        {
            return 1;
        }

        var nameComparison = string.Compare(Name, other.Name, StringComparison.Ordinal);
        if (nameComparison != 0)
        {
            return nameComparison;
        }

        return Age.CompareTo(other.Age);
    }
}
که در یک چنین مواردی شاید بهتر باشد از همان متد OrderBy پیشین استفاده کرد که الزامی به پیاده سازی اینترفیس IComparable را ندارد:
var orderedUsers2 = users.OrderBy(user => user.Name).ThenBy(user => user.Age);