نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
با تشکر از مطلب شما.
بنده در پروژه ای دقیقا طبق موارد توضیح داده شده در این مقاله (و سایر پروژه‌های موجود در سایت جاری) عمل کردم ام . مشکلی که دارم کوکی مربوطه موجود می‌باشد (با استفاده از فایبر باگ چک کردم) ولی کاربر در کمتر از ده دقیقه کاربر خارج میشود در صورتی که کوکی هنوز معتبر و زمان منقضی شدن آن به اتمام نرسیده است . تنظیمات به صورت خلاصه به صورت زیر می‌باشد :
   app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                 ExpireTimeSpan = TimeSpan.FromDays(30),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = StoreObjectFactory.Container.GetInstance<IApplicationUserManagerService>().OnValidateIdentity()
                },
                SlidingExpiration = true,
                CookieName = "iasaccount"
            });

  public Func<CookieValidateIdentityContext, Task> OnValidateIdentity()
        {
            return SecurityStampValidator.OnValidateIdentity<ApplicationUserManagerService, User, Guid>(
                         validateInterval: TimeSpan.FromDays(3),
                         regenerateIdentityCallback: (manager, user) => GenerateUserIdentityAsync(manager, user),
                         getUserIdCallback: id => id.GetGuidUserId());
        }

و :
            var result = await _signInManagerService.PasswordSignInAsync(loginViewModel.Email, loginViewModel.Password, loginViewModel.RememberMe, shouldLockout: false);

آیا تنظیمات دیگه ای لازم هست که انجام شود ؟
نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت ششم - کار با User Claims
همه نکات رو بررسی کردم. البته موضوع اینکه من اومدم IdentityServer رو در کنار Asp.net Identity استفاده کردم و دیتای user  رو از جدول مربوطه میخونم و عملیات لاگین رو هم به شکل زیر انجام دادم . 
  if (ModelState.IsValid)
        {
            // find user by username
            var user = await _signInManager.UserManager.FindByNameAsync(Input.Username);

            // validate username/password using ASP.NET Identity
            if (user != null && (await _signInManager.CheckPasswordSignInAsync(user, Input.Password, true)) == SignInResult.Success)
            {
                await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId));

                // only set explicit expiration here if user chooses "remember me". 
                // otherwise we rely upon expiration configured in cookie middleware.
                AuthenticationProperties props = null;
                if (LoginOptions.AllowRememberLogin && Input.RememberLogin)
                {
                    props = new AuthenticationProperties
                    {
                        IsPersistent = true,
                        ExpiresUtc = DateTimeOffset.UtcNow.Add(LoginOptions.RememberMeLoginDuration)
                    };
                };

                // issue authentication cookie with subject ID and username
                var isuser = new IdentityServerUser(user.Id)
                {
                    DisplayName = user.UserName
                };

                await HttpContext.SignInAsync(isuser, props);

                if (context != null)
                {
                    if (context.IsNativeClient())
                    {
                        // The client is native, so this change in how to
                        // return the response is for better UX for the end user.
                        return this.LoadingPage(Input.ReturnUrl);
                    }

                    // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
                    return Redirect(Input.ReturnUrl);
                }

                // request for a local page
                if (Url.IsLocalUrl(Input.ReturnUrl))
                {
                    return Redirect(Input.ReturnUrl);
                }
                else if (string.IsNullOrEmpty(Input.ReturnUrl))
                {
                    return Redirect("~/");
                }
                else
                {
                    // user might have clicked on a malicious link - should be logged
                    throw new Exception("invalid return URL");
                }
            }

            await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
            ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
        }
ممکنه به خاطر این مورد باشه که sub رو تو claim ندارم.؟
مگر غیر اینکه با تعریف new IdentityResources.OpenId در IdentityResources و تنظیم AllowScope کلاینت به IdentityServerConstants.StandardScopes.OpenId خود IdentityServer الزاما SubjectId رو در claim به ما برگردونه؟
نظرات مطالب
Blazor 5x - قسمت هشتم - مبانی Blazor - بخش 5 - تامین محتوای نمایشی کامپوننت‌های فرزند توسط کامپوننت والد
یک نکته‌ی تکمیلی: امکان داشتن متدهایی با خروجی تگ‌دار در برنامه‌های Blazor (یا تعریف کامپوننت‌های پویا)

اگر با React کار کرده باشید، یک چنین کدهایی اساس آن‌را تشکیل می‌دهند:
import React from "react";

const Rentals = () => {
  return <h1>Rentals</h1>;
};

export default Rentals;
در اینجا کامپوننتی به نام Rentals تعریف شده‌است که خروجی آن ... یک تگ HTML ای است و برای تشکیل آن نیازی به استفاده از "" و چسباندن رشته‌ها نبوده‌است. دقیقا شبیه به یک چنین کاری را می‌توان در برنامه‌های Blazor نیز انجام داد که به آن «Razor template syntax» و یا «templated components» هم گفته می‌شود:
@page "/razor"

@template
@ItemTemplate(emp)

@code {

    RenderFragment template = @<p>The time is @DateTime.Now.</p>;
    RenderFragment<Employee> ItemTemplate = (item) => @<p>Employee name is @item.Name.</p>;

    Employee emp = new Employee { Name = "Test" };

    public class Employee
    {
        public string Name;
    }
}
در اینجا همانطور که مشاهده می‌کنید، امکان انتساب یک قالب HTML ای شروع شده‌ی با @ به یک RenderFragment وجود دارد که حتی می‌تواند جنریک هم باشد و وهله‌ای از این شیء جنریک را به صورت یک lambda expression دریافت کند. روش درج آن‌ها را در صفحه نیز مشاهده می‌کنید که همانند فراخوانی متدها است و برای نمونه ItemTemplate جنریک، وهله‌ای از فیلد emp تعریف شده‌ی در قسمت code را دریافت کرده و در صفحه نمایش می‌دهد.
یا حتی می‌توان از RenderFragment برای وهله سازی پویای یک کامپوننت مانند SurveyPrompt، مقدار دهی خاصیت Title آن و درج آن در صفحه به صورت زیر هم استفاده کرد:
 @page "/"
  
 @CreateDynamicComponent()

 @code {
     RenderFragment CreateDynamicComponent() => builder =>
     {
         builder.OpenComponent(0, typeof(SurveyPrompt));
         builder.AddAttribute(1, "Title", "Some title");
         builder.CloseComponent();
     };
 }
نظرات مطالب
EF Code First #7
سلام
در action  مربوط به Edit  که در بالا امده است فیلد Roles برابر null  می‌باشد . دلیل این رو نمی‌دانم شاید مشکل از bind فرم هست اما entity  که در متد Get مقدار دهی میشه Roles مقدار دارد .
در مورد attach  میشه توضیح بدید . البته با این هم نتونستم کاری انجام بدم .
در اخر ناچار شدم ابتدا User رو یک بار find  کنم و سپس با مقدار مدل مقدار دهی کنم به نظر خودم که کار درستی نیست . خوشحال میشم که با راه حل اساسی آشنا بشم .
امکانش هست .

کدها رو به این صورت تغییر دادم که مشکلم برطرف شد ولی فکر میکنم که بدون find  هم باید راه حلی باشه که بلد نیستم
        [HttpPost]
        public ActionResult Edit(User user, string[] tags)
        {
            User Currentuser = db.Users.Find(user.Id);

            Currentuser.FirstName = user.FirstName;
            Currentuser.LastName = user.LastName;
            if (ModelState.IsValid)
            {
                List<Role> roles = new List<Role>();
                if (Currentuser.Roles != null && Currentuser.Roles.Any())
                    Currentuser.Roles.Clear();
                foreach (var item in tags)
                {
                    Role role = db.Roles.Find(long.Parse(item));
                    roles.Add(role);
                }
                Currentuser.Roles = roles;

                db.Entry(Currentuser).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(Currentuser);
        }




مطالب
نوشتن پرس و جو در Entity Framework‌ با استفاده از LINQ To Entity قسمت دوم
در قسمت قبل با نحوه اجرای پرس و جو آشنا شدید و همچنین به بررسی متدهای Find و Single و First و تفاوت‌های آنها پرداختیم. در این قسمت با خصوصیت Local و متد Load آشنا خواهیم شد. همانطور که در قسمت قبل دیدید، مقادیر اولیه‌ای برای Database و جداولمان مشخص کردیم. برای جدول Customer این داده‌ها را داشتیم:
 FamilyName  ID
 Nasiri Vahid یک مقدار Guid
 Akbari Mohsen یک مقدار Guid 
 Jamshidi Mohsen یک مقدار Guid 
ID توسط Database تولید می‌شوند به همین دلیل از ذکر مقداری مشخص خودداری شده است.
به کد زیر دقت کنید:
private static void Query7()
{
    using (var context = new StoreDbContext())
    {
        // Add
        context.Customers.Add(new Customer { Name = "Ali", Family = "Jamshidi" });

        // change
        var customer1 = context.Customers.Single(c => c.Family == "Jamshidi");
        customer1.Name = "Mohammad";

        // Remove
        var customer2 = context.Customers.Single(c => c.Family == "Akbari");
        context.Customers.Remove(customer2);

        var customers = context.Customers.Where(c => c.Name != "Vahid");

        foreach (var cust in customers)
        {
            Console.WriteLine("Customer Name: {0}, Customer Family: {1}", cust.Name, cust.Family)
        }
    }
}
همانطور که مشاهده می‌کنید عمل اضافه، تغییر و حذف روی Customer انجام شده ولی هنوز هیچ تغییری در Database ذخیره نشده است. آخرین پرس و جو چه نتیجه ای را دربر خواهد داشت؟
 Customer Name: Mohammad, Customer Family: Jamshidi
Customer Name: Mohsen, Customer Family: Akbari
بله، فقط تغییر یک موجودیت در نظر گرفته شده است ولی اضافه و حذف نه! 
نتیجه مهمی که حاصل می‌شود این است که در پرس و جوهایی که روی Database اجرا می‌شوند سه مورد را باید در نظر داشت:
  • داده هایی که اخیرا به DbContext اضافه شده‌اند ولی هنوز در Database ذخیره نشده‌اند، درنظر گرفته نخواهند شد.
  • داده هایی که در DbContext حذف شده‌اند ولی در Database  هستند، در نتیجه پرس و جو خواهند بود.
  • داده هایی که قبلا از database توسط پرس و جوی دیگری گرفته شده و تغییر کرده‌اند، آن تغییرات در نتیجه پرس و جو موثر خواهند بود.
پس پرس و جو‌های LINQ ابتدا روی database انجام می‌شوند و id‌های بازگشت داده شده با id‌های موجود در DbContext مطابقت داده می‌شوند یا در DbContext وجود دارند که در این صورت آن موجودیت بازگشت داده می‌شود یا وجود ندارند که در این صورت موجودیتی که از Database خوانده شده، بازگشت داده می‌شوند.
برای درک بیشتر کد زیر را در نظر بگیرید:
private static void Query7_1()
{
    using (var context = new StoreDbContext())
    {
        // Add
        context.Customers.Add(new Customer { Name = "Ali", Family = "Jamshidi" });

        // change
        var customer1 = context.Customers.Single(c => c.Family == "Jamshidi");
        customer1.Name = "Vahid";

        // Remove
        var customer2 = context.Customers.Single(c => c.Family == "Akbari");
        context.Customers.Remove(customer2);

        var customers = context.Customers.Where(c => c.Name != "Vahid");

        foreach (var cust in customers)
        {
            Console.WriteLine("Customer Name: {0}, Customer Family: {1}", cust.Name, cust.Family)
        }
    }
}
این کد همان کد قبلی است اما نام customer1 در DbContext (که Mohsen بوده در Database) به Vahid تغییر کرده و پرس و جو روی نام هایی زده شده است که Vahid نباشند خروجی به صورت زیر خواهد بود:
 Customer Name: Vahid, Customer Family: Jamshidi
Customer Name: Mohsen, Customer Family: Akbari
 Vahid در خروجی آمده در صورتیکه در شرط صدق نمی‌کند چراکه پرس و جو روی Database زده شده، جاییکه نام این مشتری Mohsen بوده اما موجودیتی بازگشت داده شده که دارای همان Id هست اما در DbContext دستخوش تغییر شده است.

Local: همانطور که قبلا اشاره شد خصوصیتی از DbSet می‌باشد که شامل تمام داده هایی هست که:
  • اخیرا از database پرس و جو شده است (می‌تواند تغییر کرده یا نکرده باشد)
  • اخیرا به Context اضافه شده است (توسط متد Add)
دقت شود که Local شامل داده‌هایی که از database خوانده شده و از Context، حذف (Remove) شده‌اند، نمی‌باشد.
نوع این خصوصیت ObservableCollection می‌باشد که می‌توان از آن برای Binding در پروژه‌های ویندوزی استفاده کرد.
به کد زیر دقت کنید:
private static void Query8()
{
    using (var context = new StoreDbContext())
    {
        // Add
        context.Customers.Add(new Customer { Name = "Ali", Family = "Jamshidi" });

        // change
        var customer1 = context.Customers.Single(c => c.Family == "Jamshidi");
        customer1.Name = "Mohammad";

        // Remove
        var customer2 = context.Customers.Single(c => c.Family == "Akbari");
        context.Customers.Remove(customer2);

        var customers = context.Customers.Local;

        foreach (var cust in customers)
        {
            Console.WriteLine("Customer Name: {0}, Customer Family: {1}", cust.Name, cust.Family);
        }
    }
}
کد بالا شبیه به کد قبلی می‌باشد با این تفاوت که در انتها foreach روی Local زده شده است. خروجی به صورت زیر خواهد بود:
Customer Name: Ali, Customer Family: Jamshidi
Customer Name: Mohammad, Customer Family: Jamshidi
همانطور که ملاحظه می‌کنید Local شامل Ali Jamshidi که اخیرا اضافه شده (ولی در Database ذخیره نشده) و Mohammad Jamshidi که از Database خوانده شده و تغییر کرده، می‌باشد اما شامل Mohsen Akbari که از Database خوانده شده اما در Context حذف شده است، نمی‌باشد.
می‌توان روی Local نیز پرس و جوی اجرا کرد. در این صورت از پروایدر LINQ To Object استفاده خواهد شد و درنتیجه دست بازتر هست و تمام امکانات این پروایدر می‌توان استفاده کرد.

Load: یکی دیگر از مواردی که باعث اجرای پرس و جو می‌شود متد Load می‌باشد که یک Extension Method می‌باشد. این متد در حقیقت یک پیمایش روی پرس و جو انجام می‌دهد و باعث بارگذاری داده‌ها در Context می‌شود. مانند استفاده از ToList البته بدون ساختن List که سربار ایجاد می‌کند.
private static void Query9()
{
    using (var context = new StoreDbContext())
    {
        var customers = context.Customers.Where(c => c.Name == "Mohsen");
        customers.Load();

        foreach (var cust in context.Customers.Local)
        {
            Console.WriteLine("Customer Name: {0}, Customer Family: {1}", cust.Name, cust.Family);
        }
    }
    // Output:
    // Customer Name: Mohsen, Customer Family: Akbari
    // Customer Name: Mohsen, Customer Family: Jamshidi
}

مطالب
INPC استاندارد با بهره گیری از صفت CallerMemberName
یکی از Attribute‌های بسیار کاربردی که در سی شارپ 5 اضافه شد CallerMemberNameAttribute بود. این صفت به یک متد اجازه میدهد که از فراخواننده‌ی خود مطلع شود. این صفت را می‌توان بر روی یک پارامتر انتخابی که مقدار پیش‌فرضی دارد اعمال نمود.

استفاده از این صفت هم بسیار ساده است:

private void A ( [CallerMemberName] string callerName = "") 
{
  Console.WriteLine("Caller is " + callerName);
}

private static void B()
{
        // let's call A
        A();
}
در کد فوق، متد A به راحتی می‌تواند بفهمد چه کسی آن را فراخوانی کرده است. از جمله کاربردهای این صفت در ردیابی و خطایابی است.

ولی یک استفاده‌ی بسیار کاربردی از این صفت، در پیاده سازی رابط INotifyPropertyChanged می‌باشد.

معمولا هنگام پیاده سازی INotifyPropertyChanged کدی شبیه به این را می‌نویسیم:

    public class PersonViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                this.name = value;
                OnPropertyChanged("Name");
            }
        }
    }

یعنی در Setter معمولا نام ویژگی ای را که تغییر کرده است، به متد OnPropertyChanged می‌فرستیم تا اطلاع رسانی‌های لازم انجام پذیرد. تا اینجای کار همه چیز خوب و آرام است. اما به محضی که کد شما کمی طولانی شود و شما به دلایلی نیاز به Refactor کردن کد و احیانا تغییر نام ویژگی‌ها را پیدا کنید، آن موقع مسائل جدیدی بروز پیدا می‌کند.

برای مثال فرض کنید پس از نوشتن کلاس PersonViewModel تصمیم می‌گیرد نام ویژگی Name را به FirstName تغییر دهید؛ چرا که می‌خواهید اجزای نام یک شخص را به صورت مجزا نگهداری و پردازش کنید. پس احتمالا با زدن کلید F2 روی فیلد name آن را به firstName و ویژگی Name را به FirstName تغییر نام می‌دهید. همانند کد زیر:

private string firstName;
public string FirstName
{
            get { return firstName; }
            set
            {
                this.firstName = value;
                OnPropertyChanged("Name");
            }
}

برنامه را کامپایل کرده و در کمال تعجب می‌بینید که بخشی از برنامه درست رفتار نمی‌کند و تغییراتی که در نام کوچک شخص توسط کاربر ایجاد می‌شود به درستی بروزرسانی نمی‌شوند. علت ساده است: ما کد را به صورت اتوماتیک Refactor کرده ایم و گزینه‌ی Include String را در حین Refactor، در حالت پیشفرض غیرفعال رها کرده‌ایم. پس جای تعجبی ندارد که در هر جای کد که رشته‌ای به نام "Name" با ماهیت نام شخص داشته ایم، دست نخورده باقی مانده است. در واقع در کد تغییر یافته، هنگام تغییر FirstName، ما به سیستم گزارش می‌کنیم که ویژگی Name (که اصلا وجود ندارد) تغییر یافته است و این یعنی خطا.

حال احتمال بروز این خطا را در ViewModel هایی با ده‌ها ویژگی و ترکیب‌های مختلف در نظر بگیرید. پس کاملا محتمل است و برای خیلی از دوستان این اتفاق رخ داده است.

و اما راه حل چیست؟ به کارگیری صفت CallerMemberName

بهتر است که یک کلاس انتزاعی برای تمام ViewModel‌های خود داشته باشیم و پیاده سازی جدید INPC را در درون آن قرار دهیم تا براحتی VM‌های ما از آن مشتق شوند:

public abstract class ViewModelBase : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            OnPropertyChangedExplicit(propertyName);
        }

        protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
        {
            var memberExpression = (MemberExpression)projection.Body;
            OnPropertyChangedExplicit(memberExpression.Member.Name);
        }

        void OnPropertyChangedExplicit(string propertyName)
        {
            this.CheckPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #region Check property name

        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void CheckPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
                throw new Exception(String.Format("Could not find property \"{0}\"", propertyName));
        }

        #endregion // Check property name
}

در این کلاس، ما پارامتر propertyName را از متد OnPropertyChanged، توسط صفت CallerMemberName حاشیه نویسی کرده‌ایم. این کار باعث می‌شود در Setter‌های ویژگی‌ها، به راحتی بدون نوشتن نام ویژگی، عملیات اطلاع رسانی تغییرات را انجام دهیم. بدین صورت که کافیست متد OnPropertyChanged بدون هیچ آرگومانی در Setter فراخوانی شود و صفت CallerMemberName به صورت اتوماتیک نام ویژگی ای که فراخوانی از درون آن انجام شده است را درون پارامتر propertyName قرار می‌دهد.

پس کلاس PersonViewModel را به صورت زیر می‌توانیم اصلاح و تکمیل کنیم:

public class PersonViewModel : ViewModelBase
{
        private string firstName;
        public string FirstName
        {
            get { return firstName; }
            set
            {
                this.firstName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        private string lastName;
        public string LastName
        {
            get { return lastName; }
            set
            {
                this.lastName = value;

                OnPropertyChanged();
                OnPropertyChanged(() => this.FullName);
            }
        }

        public string FullName
        {
            get { return string.Format("{0} {1}", FirstName, LastName); }
        }
}
همانطور که می‌بینید متد OnPropertyChanged بدون آرگومان فراخوانی میشود. اکنون اگر شما اقدام به Refactor کردن کد خود بکنید دیگر نگرانی از بابت تغییر نکردن رشته‌ها و کامنت‌ها نخواهید داشت و مطمئن هستید، نام ویژگی هر چیزی که باشد، به صورت خودکار به متد ارسال خواهد شد.

کلاس ViewModelBase یک پیاده سازی دیگر از OnPropetyChanged هم دارد که به شما اجازه می‌دهد با استفاده دستورات لامبدا، OnPropertyChanged را برای هر یک از اعضای دلخواه کلاس نیز فراخوانی کنید. همانطور که در مثال فوق می‌بینید، تغییرات نام خانوادگی در نام کامل شخص نیز اثرگذار است. در نتیجه به وسیله‌ی یک Func به راحتی بیان می‌کنیم که FullName هم تغییر کرده است و اطلاع رسانی برای آن نیز باید صورت پذیرد.

برای استفاده از صفت CallerMemberName باید دات نت هدف خود را 4.5 یا 4.6 قرار دهید.

ارجاع:
Raise INPC witout string name
نظرات مطالب
C# 7 - Ref Returns and Ref Locals
یک نکته‌ی تکمیلی: امکان تعریف خروجی از نوع ref readonly در C# 7.2

modifier جدیدی در C# 7.2 به نام ref readonly جهت تعریف نوع خروجی متدها نیز معرفی شده‌است. به این ترتیب یک متد می‌تواند بازگشت ارجاعی به اطلاعاتی موجود را بیان و همچنین فراخوان را از تغییر آن منع کند.
البته فراخوان می‌تواند تصمیم گیری کند که آیا یک کپی و یا یک ارجاع فقط خواندنی را از این متد ویژه دریافت کند. به این معنا که خروجی از نوع ref readonly، فراخوان را ملزم به تعریف یک متغیر محلی از نوع ref readonly نمی‌کند.

در مثال زیر، متد ReturnBiggestA یک خروجی کپی را باز می‌گرداند و متد ReturnBiggestARefReadonly دقیقا ارجاعی را به DataInfo اصلی بازگشت می‌دهد و با آن یکی است:
namespace CS72Tests
{
    public struct DataInfo
    {
        public double A;
    }

    public class RefReadonlyExamples
    {
        public DataInfo ReturnBiggestA(in DataInfo data1, in DataInfo data2)
        {
            return data1.A > data2.A ? data1 : data2;
        }

        public ref readonly DataInfo ReturnBiggestARefReadonly(in DataInfo data1, in DataInfo data2)
        {
            if (data1.A > data2.A)
            {
                return ref data1;
            }
            return ref data2;
        }

        public void TestingRefReadonly()
        {
            var data1 = new DataInfo { A = 0 };
            var data2 = new DataInfo { A = 100 };

            var biggest = ReturnBiggestA(data1, data2);
            biggest.A = 42;


            var biggest2 = ReturnBiggestARefReadonly(data1, data2);
            biggest2.A = 99;


            ref readonly var biggest3 = ref ReturnBiggestARefReadonly(data1, data2);
            biggest3.A = 99; // ERROR: The left-hand side of an assignment must be a variable, property or indexer
        }
    }
}
- در این فراخوانی‌ها، biggest یک کپی از data2 را باز می‌گرداند. به همین جهت می‌توان A آن‌را تغییر داد.
- در اولین فراخوانی ReturnBiggestARefReadonly، با تعریف خروجی به صورت var biggest2، یک کپی از data2 را دریافت کرده‌ایم. به همین جهت A آن قابل تغییر است.
- اما در دومین فراخوانی ReturnBiggestARefReadonly، چون خروجی آن‌را از نوع ref readonly var دریافت کرده‌ایم، این خروجی به data2 اصلی اشاره می‌کند و همچنین فقط خواندنی است. بنابراین سطر بعدی آن که A را تغییر می‌دهد، مجاز نیست.

پ.ن
در ابتدا قصد داشتند ref readonly را برای تعریف پارامترهای value type نیز بکار برند، اما این تصمیم با معرفی پارامترهای از نوع in جایگزین شد. به همین جهت ممکن است مقالات قدیمی‌تر C# 7.2 را با تعریف متدهایی مانند ذیل نیز مشاهده کنید که در نگارش آخر C# 7.2، تمام این‌ها به in تغییر کرده‌اند:
public static void Add(ref readonly int x, ref readonly int y, ref int z)
{
   z = x + y + z;
}
مطالب
بررسی مفهوم Captured Variable در زبان سی شارپ
Capturing Outer Variables  
یک عبارت لامبدا می‌تواند از متغیرهای محلی و یا پارامترهای متدی که در آن تعریف شده است، استفاده نماید (Outer Variables). این متغیرها را captured variables می‌نامند. عبارت لامبدایی که از این متغیرها استفاده می‌کند، closure نامیده می‌شود. برای مثال:
static void Main()
{
 int factor = 2;
 Func<int, int> multiplier = n => n * factor;
 Console.WriteLine (multiplier (3)); // 6
}
در کد فوق multiplier یک delegate می‌باشد که ورودی صحیح n را گرفته و در مقدار factor ضرب کرده و بر می‌گرداند.

عبارت لامبدا زمانی ارزیابی می‌شود که delegate متناظر فراخوانی (Invoke) گردد؛ نه زمانیکه متغیر اصطلاحا capture می‌شود:
int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 10;
Console.WriteLine (multiplier (3)); // 30
در کد فوق در زمانی که multiplier فراخوانی می‌شود مقدار factor برابر 10 ارزیابی شده و لذا عدد 30 چاپ خواهد شد.

عبارات لامبدا خود می‌توانند captured variable‌ها را تغییر دهند:
int seed = 0;
Func<int> natural = () => seed++;
Console.WriteLine (natural()); // 0
Console.WriteLine (natural()); // 1
Console.WriteLine (seed); // 2
در کد فوق natural یک delegate بدون ورودی و با یک خروجی integer می‌باشد. در ابتدا متغیر محلی seed تعریف شده و با مقدار اولیه 0 مقداردهی می‌شود. با هر بار اجرای natural مقدار seed به اندازه 1 واحد افزایش می‌یابد.
طول عمر(lifetime) متغیرهای captured شده در حد طول عمر delegate افزایش پیدا می‌کند. در مثال زیر متغیر محلی seed در حالت معمول، محدوده دیدی (scope) در حد تعریف این متغیر تا پایان اجرای متد دارد. اما از آنجاییکه در اینجا متغیر captured شده است، طول عمر آن در حدا طول عمر delegate افزایش می‌یابد: theNatural
static Func<int> Natural()
{
 int seed = 0;
 return () => seed++; // Returns a closure
}
static void Main()
{
 Func<int> theNatural = Natural();
 Console.WriteLine (theNatural ()); // 0
 Console.WriteLine (theNatural ()); // 1
}
اگر متغیر seed را در بدنه عبارت لامبدا تعریف نماییم، این متغیر برای هر بار اجرای delegate یکتا خواهد بود:
static Func<int> Natural()
{
 return() => { int seed = 0; return seed++; };
}
static void Main()
{
 Func<int> natural = Natural();
 Console.WriteLine (natural()); // 0
 Console.WriteLine (natural()); // 0
}

نکته: پیاده سازی پروسه Capture شدن متغیر، به این صورت است که این متغیرها به عنوان یک فیلد از یک کلاس (با سطح دسترسی private) در نظر گرفته می‌شوند. زمانیکه متد فراخوانی شد، کلاس مزبور وهله سازی شده و طول عمر آن به  طول عمر delegate گره می‌خورد.

Capturing iteration variables
در حلقه for، وقتی که متغیر حلقه توسط یک عبارت لامبدا capture می‌گردد، #C با آن متغیر طوری رفتار می‌کند که گویی در خارج از حلقه تعریف شده‌است و این بدان معناست که در هر بار تکرار حلقه، مقدار یکسانی برای متغیر در نظر گرفته می‌شود. کد زیر 333 را در خروجی چاپ می‌کند(بجای 012). 
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
actions [i] = () => Console.Write (i);
foreach (Action a in actions) a(); // 333
دلیل این موضوع این است که در هنگام اجرای delegate ها، هر delegate مقدار i را برابر مقدار آن در زمان اجرا می‌بیند و این مقدار در زمان اجرا برابر با 3 می‌باشد.
با نوشتن کد زیر می‌توان درک بهتری از موضوع پیدا کرد. 
Action[] actions = new Action[3];
int i = 0;
actions[0] = () => Console.Write (i);
i = 1;
actions[1] = () => Console.Write (i);
i = 2;
actions[2] = () => Console.Write (i);
i = 3;
foreach (Action a in actions) a(); // 333
اگر بخواهیم خروجی 012 چاپ شود راه حل به شرح زیر خواهد بود:
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
{
 int loopScopedi = i;
 actions [i] = () => Console.Write (loopScopedi);
}
foreach (Action a in actions) a(); // 012
زیرا هر متغیر loopScopedi در هر بار تکرار حلقه مجددا تعریف می‌گردد و لذا هر بار متغیر متفاوتی capture می‌گردد.
مطالب
بررسی ORM های مناسب جهت استفاده در اندروید
با آمدن ORM‌ها به دنیای برنامه نویسی، کار برنامه نویسی نسبت به قبل ساده‌تر و راحت‌تر شد. عدم استفاده کوئری‌های دستی، پشتیبانی از چند دیتابیس و از همه مهمتر و اصلی‌ترین هدف این ابزار "تنها درگیری با اشیا و مدل شیء گرایی" کار را پیش از پیش آسان‌تر نمود.
در این بین به راحتی می‌توان چندین نمونه از این ORM‌ها را  نام برد مثل IBatis , Hibernate ,Nhibernate و EF که از معروفترین آن‌ها هستند.
من در حال حاضر قصد شروع یک پروژه اندرویدی را دارم و دوست دارم بجای استفاده‌ی از Sqlitehelper، از یک ORM مناسب بهره ببرم که چند سوال برای من پیش می‌آید. آیا ORM ای برای آن تهیه شده است؟ اگر آری چندتا و کدامیک از آن‌ها بهتر هستند؟ شاید در اولین مورد کتابخانه‌ی Hibernate جاوا را نام ببرید؛ ولی توجه به این نکته ضروری است که ما در مورد پلتفرم موبایل و محدودیت‌های آن صحبت می‌کنیم. یک کتابخانه همانند Hibernate مطمئنا برای یک برنامه اندروید چه از نظر حجم نهایی برنامه و چه از نظر حجم بزرگش در اجرا، مشکل زا خواهد بود و وجود وابستگی‌های متعدد و دارا بودن بسیاری از قابلیت‌هایی که اصلا در بانک‌های اطلاعاتی موبایل قابل اجرا نیست، باعث می‌شود این فریمورک انتخاب خوبی برای یک برنامه اندروید نباشد.

معیارهای انتخاب یک فریم ورک مناسب برای موبایل:
  • سبک بودن: مهمترین مورد سبک بودن آن است؛ چه از لحاظ اجرای برنامه و چه از لحاظ حجم نهایی برنامه
  • سریع بودن: مطمئنا ORM‌های طراحی شده‌ی موجود، از سرعت خیلی بدی برخوردار نخواهند بود؛ اگر سر زبان هم افتاده باشند. ولی باز هم انتخاب سریع بودن یک ORM، مورد علاقه‌ی بسیاری از ماهاست.
  • یادگیری آسان و کانفیگ راحت تر.

OrmLight

این فریمورک مختص اندروید طراحی نشده ولی سبک بودن آن موجب شده‌است که بسیاری از برنامه نویسان از آن در برنامه‌های اندرویدی استفاده کنند. این فریم ورک جهت اتصالات JDBC و Spring و اندروید طراحی شده است.

نحوه معرفی جداول در این فریمورک به صورت زیر است:
@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(id = true)
    private String username;
    @DatabaseField
    private String password;
 
    public User() {
        // ORMLite needs a no-arg constructor
    }
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    // Implementing getter and setter methods
    public String getUserame() {
        return this.username;
    }
    public void setName(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
با استفاده از کلمات کلیدی DatabaseTable@ در بالای کلاس و DatabaseField@ در بالای هر پراپرتی به معرفی جدول و فیلدهای جدول می‌پردازیم.
سورس این فریمورک را می‌توان در گیت هاب یافت و مستندات آن در این آدرس قرار دارند.


SugarORM
این فریمورک مختص اندروید طراحی شده است. یادگیری آن بسیار آسان است و به راحتی به یاد می‌ماند. همچنین جداول مورد نیاز را به طور خودکار خواهد ساخت. روابط یک به یک و یک به چند را پشتیبانی می‌کند و عملیات CURD را با سه متد Save,Delete و Find که البته FindById هم جزء آن است، پیاده سازی می‌کند.

برای استفاده از این فریمورک نیاز است ابتدا متادیتا‌های زیر را به فایل manifest اضافه کنید:
<meta-data android:name="DATABASE" android:value="my_database.db" />
<meta-data android:name="VERSION" android:value="1" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.my-domain" />
برای تبدیل یک کلاس به جدول هم از کلاس این فریم ورک ارث بری می‌کنیم:
public class User extends SugarRecord<User> {
    String username;
    String password;
    int age;
    @Ignore
    String bio; //this will be ignored by SugarORM
 
    public User() { }
 
    public User(String username, String password,int age){
        this.username = username;
        this.password = password;
        this.age = age;
    }
}
بر خلاف OrmLight که باید فیلد جدول را معرفی می‌کردید، اینجا تمام پراپرتی‌ها به اسم فیلد شناخته می‌شوند؛ مگر اینکه در بالای آن از عبارت Ignore@ استفاده کنید.

باقی عملیات آن از قبیل اضافه کردن یک رکورد جدید یا حذف رکورد(ها) به صورت زیر است:
User johndoe = new User(getContext(),"john.doe","secret",19);
johndoe.save(); //ذخیره کاربر جدید در دیتابیس


//حذف تمامی کاربرانی که سنشان 19 سال است
List<User> nineteens = User.find(User.class,"age = ?",new int[]{19});
foreach(user in nineteens) {
    user.delete();
}
برای اطلاعات بیشتر به مستندات آن رجوع کنید.


GreenDAO
موقعیکه بحث کارآیی و سرعت پیش می‌آید نام GreenDAO هست که می‌درخشد. طبق گفته‌ی سایت رسمی آن این فریمورک میتواند در ثانیه چند هزار موجودیت را اضافه و به روزرسانی و بارگیری نماید. این لیست حاوی برنامه‌هایی است که از این فریمورک استفاده می‌کنند. جدول زیر مقایسه‌ای است بین این کتابخانه و OrmLight که نشان میدهد 4.5 برابر سریعتر از OrmLight عمل می‌کند.

غیر از این‌ها در زمینه‌ی حجم هم حرف‌هایی برای گفتن دارد. حجم این کتابخانه کمتر از 100 کیلوبایت است که در اندازه‌ی APK اثر چندانی نخواهد داشت.

آموزش راه اندازی  آن در اندروید استادیو، سورس آن در گیت هاب و مستندات رسمی آن.


Active Android
این کتابخانه از دو طریق فایل JAR و به شیوه maven قابل استفاده است که می‌توانید روش استفاده‌ی از آن را در این لینک ببینید و سورس اصلی آن هم در این آدرس قرار دارد. بعد از اینکه کتابخانه را به پروژه اضافه کردید، دو متادیتای زیر را که به ترتیب نام دیتابیس و ورژن آن هستند، به manifest اضافه کنید:
<meta-data android:name="AA_DB_NAME" android:value="my_database.db" />
<meta-data android:name="AA_DB_VERSION" android:value="1" />
بعد از آن  عبارت ;()ActiveAndroid.Initialize را در اکتیویتی‌های مدنظر اعمال کنید:
public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActiveAndroid.initialize(this);
 
        //ادامه برنامه
    }
}
برای معرفی کلاس‌ها به جدول هم از دو اعلان Table و Column مانند کد زیر به ترتیب برای معرفی جدول و فیلد استفاده می‌کنیم.
@Table(name = "User")
public class User extends Model {
    @Column(name = "username")
    public String username;
 
    @Column(name = "password")
    public String password;
 
    public User() {
        super();
    }
 
    public User(String username,String password) {
        super();
        this.username = username;
        this.password = password;
    }
}
جهت اطلاعات بیشتر در مورد این کتابخانه به مستندات آن رجوع کنید.


ORMDroid
 از آن دست کتابخانه‌هایی است که سادگی و کم حجم بودن شعار آنان است و سعی دارند تا حد ممکن همه چیز را خودکار کرده و کمترین کانفیگ را نیاز داشته باشد. حجم فعلی آن حدود 20 کیلوبایت بوده و نمی‌خواهند از 30 کیلوبایت تجاوز کند.

برای استفاده‌ی از آن ابتدا دو خط زیر را جهت معرفی تنظیمات به manifest اضافه کنید:
<meta-data
  android:name="ormdroid.database.name"
  android:value="your_database_name" />

<meta-data
  android:name="ormdroid.database.visibility"
  android:value="PRIVATE||WORLD_READABLE||WORLD_WRITEABLE" />
برای آغاز کار این کتابخانه، عبارت زیر را در هرجایی که مایل هستید مانند کلاس ارث بری شده از Application یا در ابتدای هر اکتیویتی که مایل هستید بنویسید. طبق مستندات آن صدا زدن چندباره این متد هیچ اشکالی ندارد.
ORMDroidApplication.initialize(someContext);
معرفی مدل جدول بانک اطلاعاتی هم از طریق ارث بری از کلاس Entity می‌باشد .
public class Person extends Entity {
  public int id;
  public String name;
  public String telephone;
}

//====================

Person p = Entity.query(Person.class).where("id=1").execute();
p.telephone = "555-1234";
p.save();

// یا

Person person = Entity.query(Person.class).where(eql("id", id)).execute();
p.telephone = "555-1234";
p.save();
کد بالا دقیقا یادآوری به EF هست ولی حیف که از Linq پشتیبانی نمی‌شود.
سورس آن در گیت هاب

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


نکته نهایی اینکه خوب می‌شود دوستانی که از این ORM‌های مختص اندروید استفاده کرده اند؛ نظراتشان را در مورد آن‌ها بیان کنند و مزایا و معایب آن‌ها را بیان کنند.
نظرات مطالب
React 16x - قسمت 29 - احراز هویت و اعتبارسنجی کاربران - بخش 4 - محافظت از مسیرها
با سلام
const ProtectedRoute = ({ path, component: Component, render, ...rest }) => {

  const history = useHistory();
  const user = auth.getCurrentUser();

  useEffect(() => {

    const beforePath = history.location.pathname;
    if (user && beforePath.toLowerCase() !== '/activation') {
      if (user.status <= 0) {
        history.push({
          pathname: "/Activation"
        });
      }
    }
    // componentWillUnmount
    return () => {

    };

  });
من با استفاده از این بخش برای جلوگیری از ورود کاربر به صفحاتی که دسترسی ندارد رو انجام میدم (البته کد رو کامل نذاشتم چون مشابه با کد موجود در همین صفحه هستش)، مشکلی که وجود داره اینه که وقتی کاربر لینک صفحه ای که بهش دسترسی ندارد رو کلیک میکنه دفعه اول اتفاقی نمی‌افته ولی در دفعه دوم که کلیک میکنه رویداد componentDidMount صفحه مورد نظر صدا زده میشه! در صورتیکه به اون صفحه منتقل نمیشه!
ممنون میشم راهنمایی بفرمایید