نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
هم پیش و هم پس از سه خط زیر در متد Configure تست گرفتم
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
LocalizationPipeline.ConfigureOptions(options.Value);
app.UseRequestLocalization(options.Value);
متاسفانه بعد از اهراز هویت که توسط متد PasswordSignInAsync انجام می‌شود و با اینکه resultIdentity هم مقدار آن Successed می‌باشد.
var resultIdentity = await _signInManager.PasswordSignInAsync(username, password, rememberMe, lockoutOnFailure: true);
ولی باز هم با مشکل مواجه است اطلاعات User بعد از لاگین به صورت شکل زیر است در صورتی که اگر از متد UseRequestLocalization در کانفیگ استفاده نشود و موارد مربوط به Localization فعال نباشد کاربر به درستی لاگین می‌کند و اطلاعات Claim‌ها و بقیه موارد از بانک اطلاعاتی پر می‌شود.
نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت دهم- ذخیره سازی اطلاعات کاربران IDP در بانک اطلاعاتی
با عرض سلام طبق مستندات Identity Server ، ثبت Claims‌ها در تابع  GetProfileDataAsync  بهتر است به صورت زیر نوشته شود:
  public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var subjectId = context.Subject.GetSubjectId();
            var claimsForUser = await _usersService.GetUserClaimsBySubjectIdAsync(subjectId).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList();
             context.AddRequestedClaims(claimsForUser );
        }    
زیرا تابع AddRequestedClaims  تنها Claims هایی که  کلاینت درخواست کرده را باز میگرداند ولی در صورتی که Claims ‌ها به طور مستقیم به IssuedClaims   اضافه شود حتی Claims  هایی که کلاینت درخواست نکرده نیز برگردانده میشود. این مشکل میتواند یک نقص امنیتی به وجود بیاورد که کلاینت از طریق آن میتواند به Claims هایی که به آنها دسترسی ندارد و آنها را درخواست نکرده است دسترسی داشته باشد!
نظرات مطالب
نوشتن Middleware سفارشی در ASP.NET Core
- انتخاب Middleware برای دسترسی به ModelState کار اشتباهی است؛ چون پس از فراخوانی میان‌افزار MVC و پردازش‌های model binding و validation آن است که کار مقدار دهی ModelStateDictionary صورت می‌گیرد. در حالیکه یک میان‌افزار را در هر قسمتی از pipeline می‌توان قرار داد و الزامی ندارد که پس از میان‌افزار MVC باشد.
- اگر نیاز به ModelState است، استفاده از اکشن‌فیلترها برای آن توصیه می‌شود که قابلیت تعریف سراسری هم دارند:
public class ModelStateFeatureFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var state = context.ModelState;
        // store state ...
        await next();
    }
}
یا می‌توان این context.ModelState را در جائی ذخیره کرد و سپس در یک میان‌افزار که پس از میان‌افزار MVC قرار می‌گیرد، آن‌را خواند.
نظرات مطالب
رمزنگاری JWT و افزایش امنیت آن در ASP.NET Core
توسط رخداد OnAuthenticationFailed  خاصیت Events شی JwtBearerOptions می توانید خروجی تولید شده به هنگام اشتباه بودن توکن را سفارشی سازی کنید.
.AddJwtBearer(options =>
{
    //...
    options.TokenValidationParameters = validationParameters;
    options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            //var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents));
            //logger.LogError("Authentication failed.", context.Exception);

            if (context.Exception != null)
            {
                //context.HttpContext.Response.StatusCode = 401;
                await context.HttpContext.Response.WriteAsync("خطا در اجراز هویت");
            }
        }
    }
}  
نظرات مطالب
طراحی گردش کاری با استفاده از State machines - قسمت دوم
- آیا چنین انتقال هایی به شکل زیر کار اصولی می‌باشد؟ 

به عنوان مثال اگر Trigger1 همان رویداد Save ما بوده که با توجه به Domain ای که در آن قرار داریم انجام آن در حالت‌های مشخص شده رویداد معتبری باشد، ماشین حالت بالا صحیح می‌باشد یا اینکه باید با یک رویداد دیگری به حالت State1 برگشته و دوباره انتقال به حالت State2 را انجام دهیم. البته متوجه هستم که به ازای تک تک رویدادها همیشه و نه لزوما باید دکمه متناظری را باتوجه به حالت فعلی شیء مورد، توسط کاربر قابل مشاهده باشد. با توجه به اینکه اگر کاربر نهایی به ازای تک تک رویدادهای موجود، لزوما دکمه‌های متناظری را در فرم مشاهده نکند و نیاز باشد یک دکمه Shortcut مانندی قرار بگیرد که احتمال دارد باعث چندین انتقال شود، Fire کردن چندین رویداد در پشت صحنه، کار اصولی می‌باشد؟

- نحوه استفاده از ماشین حالت ایجاد شده در لایه Application/ServiceLayer به چه شکل خواهد بود؟ حدس و سعی بنده به شکل زیر می‌باشد:
 public InvoiceStateMachine(InvoiceModel model)
        {
            _stateMachine =
                new StateMachine<InvoiceStatus, Trigger>(() => model.Status, state => model.Status = state);
سپس تعریف فیلدی به شکل زیر به منظور داشتن یک رویداد Parameterised، در دل ماشین حالت ایجاد شده:
public Func<InvoiceModel, Task> OnSaveAsync = null;
private readonly StateMachine<InvoiceStatus, Trigger>.TriggerWithParameters<InvoiceModel> _saveTrigger;

کانفیگ آن به شکل زیر:
 _stateMachine.Configure(InvoiceStatus.Pending)
                .OnEntryFromAsync(_saveTrigger, async invoice => await OnSaveAsync(invoice))
سپس در InvoiceService به شکل زیر عمل کرده:
 private void InitializeStateMachine(InvoiceModel model)
        {
            _stateMachine = new InvoiceStateMachine(model)
            {
                OnSaveAsync = async invoiceModel =>
                {
                    var result = model.IsNew() ? await CreateAsync(model) : await EditAsync(model);
                    if (!result.Succeeded)
                        throw new BusinessRuleValidationException(result.Message);
                }
            };
        }

و در نهایت متدی که از بیرون فراخوانی خواهد شد:
[Transactional]
public async Task<Result> SaveAsync(InvoiceModel model)
{
    try
    {
        InitializeStateMachine(model);
        await _stateMachine.TryFireSaveAsync(model);
        return Success();
    }
    catch (BusinessRuleValidationException e)
    {
        return Failed(e.Message);
    }
}

برای انتقال خطاهای ایجاد شده در زمان انتقال، راه حلی به غیر از صدور یک استثناء مشخص و گرفتن آن و ساخت خروجی مورد نظر متد سرویس به ذهنم نمی‌رسد.
نظرات مطالب
از سرگیری مجدد، لغو درخواست و سعی مجدد دریافت فایل‌های حجیم توسط HttpClient
همانطور که در نکته تکمیلی بالا عنوان شده‌است، تمام متدهای بازگشت فایل، پارامتر enableRangeProcessing را نیز به همراه دارند. در صورت استفاده از FileStreamResult، در Response Header، هدر Accept-Ranges وجود ندارد. ولی در صورت استفاده از PhysicalFile  این هدر در Response Header وجود دارد، دلیل چیست؟ 
public async Task<IActionResult> Get () {
     var url = "https://anthonygiretti.blob.core.windows.net/videos/earth.mp4";
      return new FileStreamResult (await this._httpClient.GetStreamAsync (url), "video/mp4") {
         EnableRangeProcessing = true
      };
}

نظرات مطالب
ساخت ربات تلگرامی با #C
1. آپدیتهای دریافتی همیشه فقط شامل Message نمیشه و ممکنه آپدیت دریافتی از نوع CallbackQuery هم باشه
2. کمی بالاتر توضیح داده شده که برای دریافت آپدیت‌های جدید باید پارامتر offset رو هم ارسال کنی. مقدار این پارامتر باید رقم بعدی Id آخرین آپدیت دریافتی باشه یعنی update_id  +1
برای طراحی ربات تلگرام هم بهتر هست که از پکیج‌های آماده استفاده بشه که بالاتر عرض کردم.
یه پروژه کنسول ایجاد کن پکیج telegram.bot رو هم از Nuget به برنامه اضافه کن و کلاس program.cs  رو به صورت زیر پیاده کن
به جای BOT_TOKEN هم توکن ربات خودت رو کپی کن و برنامه رو اجرا کن
using System.Threading.Tasks;
using Telegram.Bot;

namespace Bot.Engine.Console
{
    public class Program
    {
        Api bot;
        string botToken = "BOT_TOKEN";

        public static void Main(string[] args)
        {
            Task.Run(() => RunBot(botToken));

            System.Console.ReadLine();
        }


        /// <summary>
        /// 
        /// </summary>
        public static async Task RunBot(string botToken)
        {
            #region راه اندازی ربات

             bot = new Api(botToken);
            var me = await bot.GetMe();
            if (me != null)
            {
                System.Console.WriteLine("bot started {0}", me.Username);
            }
            else
            {
                System.Console.WriteLine("get bot failed ");
            }

          
            #endregion

            #region شروع گوش دادن به درخواست‌ها var whileCount = 0;
            var offset = 0;

            while (true)
            {
                System.Console.WriteLine("while no {0}", whileCount);

                whileCount += 1;
                try
                {
                    var updates = await bot.GetUpdatesAsync(offset);
                    var updatesCount = updates.Count();
                    System.Console.WriteLine("updates count is {0}", updatesCount);
                    System.Console.WriteLine("================================================================");

                    if (updatesCount > 0)
                    {
                        foreach (var update in updates)
                        {
                            try
                            {
                                offset = update.Id + 1;
                                if (update.Message.Text!=null)
                                {
                                    //echo msg
                                    await bot.SendTextMessageAsync(update.Message.Chat.Id, update.Message.Text);
                                }
                                else
                                {
                                    await bot.SendTextMessageAsync(update.Message.Chat.Id, "لطفا یک پیام متنی بفرستید");
                                }

                            }
                            catch (Exception ex)
                            {
                                bot.SendTextMessage(update.Message.Chat.Id, ex.ToString());
                            }
                        }
                        continue;
                    }


                }
                catch (Exception ex)
                {
                    System.Console.WriteLine("Error Msg = {0}",ex.Message);
                }

            }

            #endregion
        }

    }
}

نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
از Claim‌ها کمک بگیرید (در نهایت در کوکی کاربر ذخیره خواهند شد ) :
 public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUser applicationUser)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
           userIdentity.AddClaim(new Claim("Avatar",applicationUser.Avatar));
            return userIdentity;
        }
متد AddClaim برای کلاس ClaimsIdentity در نظر گرفته شده است.
یا حتی میتوانید یک ClaimsIdentityFactory شخصی سازی شده در نظر بگیرید؛ برای مثال در پروژه " طراحی فریمورک برای کار با Asp.net MVC و EF به صورت NTier  یک نمونه پیاده سازی شده را میتوانید مشاهده کنید.
نظرات مطالب
بازنویسی سطح دوم کش برای Entity framework 6
آقای نصیری من برای صفحه بندی اطلاعات به این صورت کش میکنم اطلاعات رو :

            var source = users
                .Include(d => d.RolesGroup)
                .Skip(skipRecords)
                .Take(recordsPerPage)
                .Cacheable()
                .ToList();

درست عمل میکنه و فقط بار اول به دیتابیس متصل میشه ولی زمانی که یک رکورد رو حذف میکنم و متد SaveAllChanges رو هم صدا میزنم changedEntityNames همیشه خالی برگشت داده میشه.

من به این صورت رکوردی رو حذف میکنم :

 public async Task<IdentityResult> RemoveByIdAsync(int userId)
        {
            var user = FindById(userId);
            var identityResult = await DeleteAsync(user);
            return identityResult;
        }

به نظرتون مشکل از کجاست ؟
نظرات مطالب
انجام کارهای زمانبندی شده در برنامه‌های ASP.NET توسط DNT Scheduler
- «آیا اصلا نیاز به ایجاد تاخیر زمانی هست؟»
بله؛ نیاز هست: «راه‌های کم کردن احتمال اسپم شدن ایمیل‌های ارسالی توسط SMTP Client»  
- «آیا کد زیر درست است؟»
می‌تواند بهتر باشد. مثلا بجای 0<Count بنویسید Any (سریعتر است). یا اگر قرار است فایلی یکجا خوانده شود، بهتر است از متد File.ReadAllText استفاده کنید تا درگیر مباحث dispose کردن منابع نشوید. به علاوه هرچند در اینجا نام Task عنوان شده‌است، اما این‌ها واقعا از نوع کلاس Task دات نت نیستند. بنابراین بجای Task.Delay از Thread.Sleep استفاده کنید. متد Task.Delay یک متد blocking نیست و نحوه‌ی فراخوانی آن باید به صورت await Task.Delay باشد و همانطور که عنوان شد، در اینجا Task ایی نداریم و صرفا اجرای عملیات در یک ترد مجزا است.