مطالب
ایجاد «خواص الحاقی»
حتما با متدهای الحاقی یا Extension methods آشنایی دارید؛ می‌توان به یک شیء، که حتی منبع آن در دسترس ما نیست، متدی را اضافه کرد. سؤال: در مورد خواص چطور؟ آیا می‌شود به وهله‌ای از یک شیء موجود از پیش طراحی شده، یک خاصیت جدید را اضافه کرد؟
احتمالا شاید عنوان کنید که با اشیاء dynamic می‌توان چنین کاری را انجام داد. اما سؤال در مورد اشیاء غیر dynamic است.
یا نمونه‌ی دیگر آن Attached Properties در برنامه‌های مبتنی بر Xaml هستند. می‌توان به یک شیء از پیش موجود Xaml، خاصیتی را افزود که البته پیاده سازی آن منحصر است به همان نوع برنامه‌ها.


راه حل پیشنهادی

یک Dictionary را ایجاد کنیم تا ارجاعی از اشیاء، به عنوان کلید، در آن ذخیره شده و سپس key/valueهایی به عنوان value هر شیء، در آن ذخیره شوند. این key/valueها همان خواص و مقادیر آن‌ها خواهند بود. هر چند این راه حل به خوبی کار می‌کند اما ... مشکل نشتی حافظه دارد.
شیء Dictionary یک ارجاع قوی را از اشیاء، درون خودش نگه داری می‌کند و تا زمانیکه در حافظه باقی است، سیستم GC مجوز رهاسازی منابع آن‌ها را نخواهد یافت؛ چون عموما این نوع Dictionaryها باید استاتیک تعریف شوند تا طول عمر آن‌ها با طول عمر برنامه یکی گردد. بنابراین اساسا اشیایی که به این نحو قرار است پردازش شوند، هیچگاه dispose نخواهند شد. راه حلی برای این مساله در دات نت 4 به صورت توکار به دات نت فریم ورک اضافه شده‌است؛ به نام ساختار داده‌ای ConditionalWeakTable.


معرفی ConditionalWeakTable

ConditionalWeakTable جزو ساختارهای داده‌ای کمتر شناخته شده‌ی دات نت است. این ساختار داده، اشاره‌گرهایی را به ارجاعات اشیاء، درون خود ذخیره می‌کند. بنابراین چون ارجاعاتی قوی را به اشیاء ایجاد نمی‌کند، مانع عملکرد GC نیز نشده و برنامه در دراز مدت دچار مشکل نشتی حافظه نخواهد شد. هدف اصلی آن ایجاد ارتباطی بین CLR و DLR است. توسط آن می‌توان به اشیاء دلخواه، خواصی را افزود. به علاوه طراحی آن به نحوی است که thread safe است و مباحث قفل گذاری بر روی اطلاعات، به صورت توکار در آن پیاده سازی شده‌است. کار DLR فراهم آوردن امکان پیاده سازی زبان‌های پویایی مانند Ruby و Python برفراز CLR است. در این نوع زبان‌ها می‌توان به وهله‌هایی از اشیاء موجود، خاصیت‌های جدیدی را متصل کرد.
به صورت خلاصه کار ConditionalWeakTable ایجاد نگاشتی است بین وهله‌هایی از اشیاء CLR (اشیایی غیرپویا) و خواصی که به آن‌ها می‌توان به صورت پویا انتساب داد. در کار GC اخلال ایجاد نمی‌کند و همچنین می‌توان به صورت همزمان از طریق تردهای مختلف، بدون مشکل با آن کار کرد.


پیاده سازی خواص الحاقی به کمک ConditionalWeakTable

در اینجا نحوه‌ی استفاده از ConditionalWeakTable را جهت اتصال خواصی جدید به وهله‌های موجود اشیاء مشاهده می‌کنید:
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace ConditionalWeakTableSamples
{
    public static class AttachedProperties
    {
        public static ConditionalWeakTable<object,
            Dictionary<string, object>> ObjectCache = new ConditionalWeakTable<object,
                Dictionary<string, object>>();

        public static void SetValue<T>(this T obj, string name, object value) where T : class
        {
            var properties = ObjectCache.GetOrCreateValue(obj);

            if (properties.ContainsKey(name))
                properties[name] = value;
            else
                properties.Add(name, value);
        }

        public static T GetValue<T>(this object obj, string name)
        {
            Dictionary<string, object> properties;
            if (ObjectCache.TryGetValue(obj, out properties) && properties.ContainsKey(name))
                return (T)properties[name];
            return default(T);
        }

        public static object GetValue(this object obj, string name)
        {
            return obj.GetValue<object>(name);
        }
    }
}
ObjectCache تعریف شده از نوع استاتیک است؛ بنابراین در طول عمر برنامه زنده نگه داشته خواهد شد، اما اشیایی که به آن منتسب می‌شوند، خیر. هرچند به ظاهر در متد GetOrCreateValue، یک وهله از شیءایی موجود را دریافت می‌کند، اما در پشت صحنه صرفا IntPtr یا اشاره‌گری به این شیء را ذخیره سازی خواهد کرد. به این ترتیب در کار GC اخلالی صورت نخواهد گرفت و شیء مورد نظر، تا پایان کار برنامه به اجبار زنده نگه داشته نخواهد شد.


کاربرد اول

اگر با ASP.NET کار کرده باشید حتما با IPrincipal آشنایی دارید. خواصی مانند Identity یک کاربر در آن ذخیره می‌شوند.
سؤال: چگونه می‌توان یک خاصیت جدید به نام مثلا Disclaimer را به وهله‌ای از این شیء افزود:
    public static class ISecurityPrincipalExtension
    {
        public static bool Disclaimer(this IPrincipal principal)
        {
            return principal.GetValue<bool>("Disclaimer");
        }

        public static void SetDisclaimer(this IPrincipal principal, bool value)
        {
            principal.SetValue("Disclaimer", value);
        }
    }
در اینجا مثالی را از کاربرد کلاس AttachedProperties فوق مشاهده می‌کنید. توسط متد SetDisclaimer یک خاصیت جدید به نام Disclaimer به وهله‌ای از شیءایی از نوع  IPrincipal  قابل اتصال است. سپس توسط متد  Disclaimer قابل دستیابی خواهد بود.

اگر صرفا قرار است یک خاصیت به شیءایی متصل شود، روش ذیل نیز قابل استفاده می‌باشد (بجای استفاده از دیکشنری از یک کلاس جهت تعریف خاصیت اضافی جدید استفاده شده‌است):
using System.Runtime.CompilerServices;

namespace ConditionalWeakTableSamples
{
    public static class PropertyExtensions
    {
        private class ExtraPropertyHolder
        {
            public bool IsDirty { get; set; }
        }

        private static readonly ConditionalWeakTable<object, ExtraPropertyHolder> _isDirtyTable
                = new ConditionalWeakTable<object, ExtraPropertyHolder>();

        public static bool IsDirty(this object @this)
        {
            return _isDirtyTable.GetOrCreateValue(@this).IsDirty;
        }

        public static void SetIsDirty(this object @this, bool isDirty)
        {
            _isDirtyTable.GetOrCreateValue(@this).IsDirty = isDirty;
        }
    }
}


کاربرد دوم

ایجاد Id منحصربفرد برای اشیاء برنامه.
فرض کنید در حال نوشتن یک Entity framework profiler هستید. طراحی فعلی سیستم Interception آن به نحو زیر است:
public void Closed(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
{
}
سؤال: اینجا رویداد بسته شدن یک اتصال را دریافت می‌کنیم؛ اما ... دقیقا کدام اتصال؟ رویداد Opened را هم داریم اما چگونه این اشیاء را به هم مرتبط کنیم؟ شیء DbConnection دارای Id نیست. متد GetHashCode هم الزامی ندارد که اصلا پیاده سازی شده باشد یا حتی یک Id منحصربفرد را تولید کند. این متد با تغییر مقادیر خواص یک شیء می‌تواند مقادیر متفاوتی را ارائه دهد. در اینجا می‌خواهیم به ازای ارجاعی از یک شیء، یک Id منحصربفرد داشته باشیم تا بتوانیم تشخیص دهیم که این اتصال بسته شده، دقیقا کدام اتصال باز شده‌است؟
راه حل: خوب ... یک خاصیت Id را به اشیاء موجود متصل کنید!
using System;
using System.Runtime.CompilerServices;

namespace ConditionalWeakTableSamples
{
    public static class UniqueIdExtensions
    {
        static readonly ConditionalWeakTable<object, string> _idTable = 
                                    new ConditionalWeakTable<object, string>();

        public static string GetUniqueId(this object obj)
        {
            return _idTable.GetValue(obj, o => Guid.NewGuid().ToString());
        }

        public static string GetUniqueId(this object obj, string key)
        {
            return _idTable.GetValue(obj, o => key);
        }
    }
}
در اینجا مثالی دیگر از پیاده سازی و استفاده از ConditionalWeakTable را ملاحظه می‌کنید. اگر در کش آن ارجاعی به شیء مورد نظر وجود داشته باشد، مقدار Guid آن بازگشت داده می‌شود؛ اگر خیر، یک Guid به ارجاعی از شیء، انتساب داده شده و سپس بازگشت داده می‌شود. به عبارتی به صورت پویا یک خاصیت UniqueId به وهله‌هایی از اشیاء اضافه می‌شوند. به این ترتیب به سادگی می‌توان آن‌ها را ردیابی کرد و تشخیص داد که اگر این Guid پیشتر جایی به اتصال باز شده‌ای منتسب شده‌است، در چه زمانی و در کجا بسته شده است یا اصلا ... خیر. جایی بسته نشده‌است.


برای مطالعه بیشتر
The Conditional Weak Table: Enabling Dynamic Object Properties
How to create mixin using C# 4.0
Disclaimer Page using MVC
Extension Properties Revised
Easy Modeling
Providing unique ID on managed object using ConditionalWeakTable
اشتراک‌ها
سری آموزشی Blazor Hybrid

Blazor Hybrid for Beginners
Join James Montemagno as he takes you on a journey of building your first Hybrid applications across iOS, Android, Mac, Windows, and Web with ASP.NET Core, Blazor, Blazor Hybrid, and .NET MAUI!  You will learn how to use Blazor Hybrid to blend desktop and mobile native client frameworks with .NET and Blazor.
In a Blazor Hybrid app, Razor components run natively on the device. Components render to an embedded Web View control through a local interop channel. Components don't run in the browser, and WebAssembly isn't involved. Razor components load and execute code quickly, and components have full access to the native capabilities of the device through the .NET platform. Component styles rendered in a Web View are platform dependent and may require you to account for rendering differences across platforms using custom stylesheets.
Blazor Hybrid support is built into the .NET Multi-platform App UI (.NET MAUI) framework. .NET MAUI includes the BlazorWebView control that permits rendering Razor components into an embedded Web View. By using .NET MAUI and Blazor together, you can reuse one set of web UI components across mobile, desktop, and web.
 

سری آموزشی Blazor Hybrid
اشتراک‌ها
بررسی زبان Go برای توسعه دهندگان #C

A Tour of Go (golang) for the C# Developer

Learning other programming languages enhances our work in our primary language. From the perspective of a C# developer, the Go language (golang) has many interesting ideas. Go is opinionated on some things (such as where curly braces go and what items are capitalized). Declaring an unused variable causes a compile failure; the use of "blank identifiers" (or "discards" in C#) are common. Concurrency is baked right in to the language through goroutines and channels. Programming by exception is discouraged; it's actually called a "panic" in Go. Instead, errors are treated as states to be handled like any other data state. We'll explore these features (and others) by building an application that uses concurrent operations to get data from a service. These ideas make us think about the way we program and how we can improve our day-to-day work (in C# or elsewhere).

0:00 Welcome to Go
2:40 Step 1: Basics
12:20 Step 2: Calling a web service
23:35 Step 3: Parsing JSON
36:26 Step 4: "for" loops
41:00 Step 5: Interfaces and methods
50:05 Step 6: Time and Args
55:10 Step 7: Concurrency
1:07:10 Step 8: Errors
1:14:40 Step 9: Concurrency and errors
1:24:35 Where to go next 

بررسی زبان Go برای توسعه دهندگان #C
بازخوردهای دوره
رها سازی منابع IDisposable در StructureMap
بله. ADO.NET (فناوری اصلی پشت صحنه) از یک connection pool برای بالا بردن سرعت اتصال‌های جدید استفاده می‌کند (استفاده از کانکشن‌های موجود، بجای ایجاد یک کانکشن جدید که بسیار هزینه‌بر و کند است). Max Pool Size در اینجا 100 است. اگر اتصالات، بسته نشده و به این pool بازگشت داده نشوند، کاربران با پیام time out مواجه خواهند شد:
System.InvalidOperationException: Timeout expired. 
The timeout period elapsed prior to obtaining a connection from the pool.  
This may have occurred because all pooled connections were in use and max pool size was reached.
نظرات مطالب
کار با کوکی‌ها در ASP.NET Core

مورد مشابه دیگری که نیاز به تنظیم دارد، کوکی‌های anti-forgery-token هستند. این‌ها هم به صورت خودکار به SameSiteMode.Strict تنظیم شده‌اند و اگر از طریق ایمیل، به فرمی مرتبط در سایت هدایت شوید و ... فرم را ارسال کنید، فقط یک صفحه‌ی سفید رنگ را با پیام خطای 400، مشاهده خواهید کرد که قابل سفارشی سازی هم نیست (و مطلقا هم از تمام فیلترهای سراسری خطایابی شما، رد نمی‌شود). هدایت به سایت (یعنی یک redirect از سایت ثالث)، با کوکی‌های از نوع Strict هم‌خوانی ندارد و مرورگر، آن‌ها را به سمت سرور ارسال نمی‌کند. به همین جهت این درخواست‌های اعتبارسنجی anti-forgery-token هم برگشت خواهند خورد. برای تعدیل آن می‌توان به صورت زیر عمل کرد:

services.Configure<AntiforgeryOptions>(opts => { opts.Cookie.SameSite = SameSiteMode.Lax; });
نظرات مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
یک نکته: base href حساس به بزرگی و کوچکی حروف است!
بین تنظیم
<base href="/blazor/" />
و تنظیم زیر
<base href="/Blazor/" />
تفاوت وجود دارد. یعنی اگر اولی تنظیم شده باشد و کاربر در مرورگر http://localhost/Blazor را وارد کند، با پیام خطای زیر مواجه می‌شود:
System.ArgumentException: The URI is not contained by the base URI
فعلا برای رفع این مشکل می‌توان قطعه کد زیر را پیش از تگ js مربوط به blazor قرار داد تا base href را به صورت پویا تنظیم کند:
<script>
  var path = window.location.pathname.split('/');
  var baseTag = document.getElementsByTagName('base');
  baseTag[0].setAttribute('href', '/' + path[1] + '/');
</script>
اطلاعات بیشتر
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
- factory.Create به معنای ندیدگرفتن تنظیمات قبلی و ارائه یک وهله ساز جدید هست (یعنی قبلی از کار می‌افتد و فقط پروایدر جدید کار می‌کند).
- این مورد را الان آزمایش کردم و کار می‌کند:
return factory.Create(
     baseName: "SharedResource",
     location: "Core3xRtmTestResources.ExternalResources"  //نام اسمبلی ثالث
);
همان مثال نگارش 1x پیوست در نظرات هست که به نگارش 3x ارتقاء داده شده: Core3xRtmTestResources.zip
روی دکمه صفحه‌ی اول آن کلیک کنید (بعد از کلیک بر روی لینک فارسی سازی)، پیام خطا را از فایل shared می‌خواند.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API
من یک api  طراحی کردم که کنترلر آن رو مزین کردم به خصوصیت ApiController .  این api یک پارامتر از نوع  int داره که با خصوصیت Required و پیام خطای مناسب تنظیم شده ، مشکلی که دارم در صورت null بودن این پارامتر خطای اعتبار سنجی سفارشی برگشت داده نمی‌شود و خطای  زیر صادر میشه :
The JSON value could not be converted to System.Int32
آیا باید برای مدیریت این حالت‌ها ، مبدل json مثل این مطلب  نوشت؟
نظرات مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت ششم - کار با منابع محافظت شده‌ی سمت سرور
زمانیکه از متد http به جای  httpClient برای استفاده از داده‌های محافظت شده سمت سرور استفاده میکنم اجازه دسترسی به آنها را نمیدهد و در هدر ارسال شده این پیام رو نشان میدهد:   
Request URL: http://localhost:5000/api/office?page=1&pageSize=5
Request Method: GET
Status Code: 401 Unauthorized
Remote Address: [::1]:5000
Referrer Policy: no-referrer-when-downgrade
نظرات مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت سوم
سلام :)
نمونه برنامه‌ای که گذاشتید رو کلون کردم، برای اجرا نیاز به بسته runtime.win10-x86.microsoft.net.uwpcoreruntimesdk.2.1.2 داره، بدون VPN با VPN و حتی به صورت دستی (به این دلیل که باز نیاز به دانلود بخشهای دیگه‌ای داره) قابل نصب نیست. همچنین از اینترت خونه، همراه اول و رایتل هم امتحان کردم! پیام خطای Failed To Download میده. در نتیجه برنامه بیلد نمیشه.
آیا راه حلی مانده که امتحان کنم، یا تنها راه سعی و تلاش در ساعتهای مختلفه؟ آیا راه حلی وجود داره که این بسته رو به صورت کامل گرفت و دستی نصب کرد؟ 
ممنون :)