مطالب دوره‌ها
استفاده از AOP Interceptors برای حذف کدهای تکراری INotifyPropertyChanged در WPF
هرکسی که با WPF کار کرده باشد با دردی به نام اینترفیس INotifyPropertyChanged و پیاده سازی‌های تکراری مرتبط با آن آشنا است:
public class MyClass : INotifyPropertyChanged
{
    private string _myValue;
    public event PropertyChangedEventHandler PropertyChanged;
    public string MyValue
    {
        get
        {
            return _myValue;
        }
        set
        {
            _myValue = value;
            RaisePropertyChanged("MyValue");
        }
    }
    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
چندین راه‌حل هم برای ساده سازی و یا بهبود آن وجود دارد از Strongly typed کردن آن تا روش‌های اخیر دات نت 4 و نیم در مورد استفاده از ویژگی‌های متدهای فراخوان. اما ... با استفاده از AOP Interceptors می‌توان در وهله سازی‌ها و فراخوانی‌ها دخالت کرد و کدهای مورد نظر را در مکان‌های مناسبی تزریق نمود. بنابراین در مطلب جاری قصد داریم ارائه متفاوتی را از پیاده سازی خودکار INotifyPropertyChanged ارائه دهیم. به عبارتی چقدر خوب می‌شد فقط می‌نوشتیم :
public class MyDreamClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public string MyValue { get; set; }
}
و ... همه چیز مثل سابق کار می‌کرد. برای رسیدن به این هدف، باید فراخوانی‌های set خواص را تحت نظر قرار داد (یا همان Interception در اینجا). ابتدا باید اجازه دهیم تا set صورت گیرد، پس از آن کدهای معروف RaisePropertyChanged را به صورت خودکار فراخوانی کنیم.


پیشنیازها

ابتدا یک برنامه جدید WPF را آغاز کنید. تنظیمات آن‌را از حالت Client profile به Full تغییر دهید.
سپس همانند قسمت قبل، ارجاعات لازم را به StructureMap و Castle.Core نیز اضافه نمائید:
 PM> Install-Package structuremap
PM> Install-Package Castle.Core


ساختار برنامه

برنامه ما از یک اینترفیس و کلاس سرویس تشکیل شده است:
namespace AOP01.Services
{
    public interface ITestService
    {
        int GetCount();
    }
}

namespace AOP01.Services
{
    public class TestService: ITestService
    {     
        public int GetCount()
        {
            return 10; //این فقط یک مثال است برای بررسی تزریق وابستگی‌ها
        }
    }
}
همچنین دارای یک ViewModel به شکل زیر می‌باشد:
using AOP01.Services;
using AOP01.Core;

namespace AOP01.ViewModels
{
    public class TestViewModel  : BaseViewModel
    {
        private readonly ITestService _testService;
        //تزریق وابستگی‌ها در سازنده کلاس
        public TestViewModel(ITestService testService)
        {
            _testService = testService;
        }

        // Note: it's a virtual property.
        public virtual string Text { get; set; }
    }
}
سه نکته در این ViewModel حائز اهمیت هستند:
الف) استفاده از کلاس پایه BaseViewModel برای کاهش کدهای تکراری مرتبط با INotifyPropertyChanged که به صورت زیر تعریف شده است:
using System.ComponentModel;

namespace AOP01.Core
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;

            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
ب) کلاس سرویس، در حالت تزریق وابستگی‌ها در سازنده کلاس در اینجا مورد استفاده قرار گرفته است. وهله سازی خودکار آن توسط کلاس‌های پروکسی و DI صورت خواهند گرفت.
ج) خاصیتی که در اینجا تعریف شده از نوع virtual است؛ بدون پیاده سازی مفصل قسمت set آن و فراخوانی مستقیم RaisePropertyChanged کلاس پایه به صورت متداول. علت virtual تعریف کردن آن به امکان دخل و تصرف در نواحی get و set این خاصیت توسط Interceptor ایی که در ادامه تعریف خواهیم کرد بر می‌گردد.


پیاده سازی NotifyPropertyInterceptor

using System;
using Castle.DynamicProxy;

namespace AOP01.Core
{
    public class NotifyPropertyInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            // متد ست، ابتدا فراخوانی می‌شود و سپس کار اطلاع رسانی را انجام خواهیم داد
            invocation.Proceed();

            if (invocation.Method.Name.StartsWith("set_"))
            {
                var propertyName = invocation.Method.Name.Substring(4);
                raisePropertyChangedEvent(invocation, propertyName, invocation.TargetType);
            }
        }

        void raisePropertyChangedEvent(IInvocation invocation, string propertyName, Type type)
        {
            var methodInfo = type.GetMethod("RaisePropertyChanged");
            if (methodInfo == null)
            {
                if (type.BaseType != null)
                    raisePropertyChangedEvent(invocation, propertyName, type.BaseType);
            }
            else
            {
                methodInfo.Invoke(invocation.InvocationTarget, new object[] { propertyName });
            }
        }
    }
}
با اینترفیس IInterceptor در قسمت قبل آشنا شدیم.
در اینجا ابتدا اجازه خواهیم داد تا کار set به صورت معمول انجام شود. دو حالت get و set ممکن است رخ دهند. بنابراین در ادامه بررسی خواهیم کرد که اگر حالت set بود، آنگاه متد RaisePropertyChanged کلاس پایه BaseViewModel را یافته و به صورت پویا با propertyName صحیحی فراخوانی می‌کنیم.
به این ترتیب دیگر نیازی نخواهد بود تا به ازای تمام خواص مورد نیاز، کار فراخوانی دستی RaisePropertyChanged صورت گیرد.


اتصال Interceptor به سیستم

خوب! تا اینجای کار صرفا تعاریف اولیه تدارک دیده شده‌اند. در ادامه نیاز است تا DI و DynamicProxy را از وجود آن‌ها مطلع کنیم.
برای این منظور فایل App.xaml.cs را گشوده و در نقطه آغاز برنامه تنظیمات ذیل را اعمال نمائید:
using System.Linq;
using System.Windows;
using AOP01.Core;
using AOP01.Services;
using Castle.DynamicProxy;
using StructureMap;

namespace AOP01
{
    public partial class App
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            ObjectFactory.Initialize(x =>
            {
                x.For<ITestService>().Use<TestService>();

                var dynamicProxy = new ProxyGenerator();
                x.For<BaseViewModel>().EnrichAllWith(vm =>
                {
                    var constructorArgs = vm.GetType()
                            .GetConstructors()
                            .FirstOrDefault()
                            .GetParameters()
                            .Select(p => ObjectFactory.GetInstance(p.ParameterType))
                            .ToArray();

                    return dynamicProxy.CreateClassProxy(
                                classToProxy: vm.GetType(),
                                constructorArguments: constructorArgs,
                                interceptors: new[] { new NotifyPropertyInterceptor() });
                });
            });
        }
    }
}
مطابق این تنظیمات، هرجایی که نیاز به نوعی از ITestService بود، از کلاس TestService استفاده خواهد شد.
همچنین در ادامه به DI مورد استفاده اعلام می‌کنیم که ViewModelهای ما دارای کلاس پایه BaseViewModel هستند. بنابراین هر زمانی که این نوع موارد وهله سازی شدند، آن‌ها را یافته و با پروکسی حاوی NotifyPropertyInterceptor مزین کن.
مثالی که در اینجا انتخاب شده، تقریبا مشکل‌ترین حالت ممکن است؛ چون به همراه تزریق خودکار وابستگی‌ها در سازنده کلاس ViewModel نیز می‌باشد. اگر ViewModelهای شما سازنده‌ای به این شکل ندارند، قسمت تشکیل constructorArgs را حذف کنید.


استفاده از ViewModel مزین شده با پروکسی در یک View

<Window x:Class="AOP01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</Window>
اگر فرض کنیم که پنجره اصلی برنامه مصرف کننده ViewModel فوق است، در code behind آن خواهیم داشت:
using AOP01.ViewModels;
using StructureMap;

namespace AOP01
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            //علاوه بر تشکیل پروکسی
            //کار وهله سازی و تزریق وابستگی‌ها در سازنده را هم به صورت خودکار انجام می‌دهد
            var vm = ObjectFactory.GetInstance<TestViewModel>(); 
            this.DataContext = vm;
        }
    }
}
به این ترتیب یک ViewModel محصور شده توسط DynamicProxy مزین با NotifyPropertyInterceptor به DataContext  ارسال می‌گردد.

اکنون اگر برنامه را اجرا کنیم، مشاهده خواهیم کرد که با وارد کردن مقداری در TextBox برنامه، NotifyPropertyInterceptor مورد استفاده قرار می‌گیرد:



دریافت مثال کامل این قسمت
AOP01.zip
نظرات مطالب
کار با کوکی‌ها در ASP.NET Core

یک نکته‌ی تکمیلی: در تنظیمات کوکی‌های سایت جدید، SameSite به Strict تنظیم شده بود:

options.Cookie.SameSite = SameSiteMode.Strict;

این موضوع سبب شده بود که اگر کاربری در سایت لاگین کرده بود و ایمیلی را دریافت می‌کرد، پس از کلیک بر روی ایمیل دریافتی و ورود به سایت، اعتبارسنجی نشده تلقی می‌شد و برای مثال امکان ارسال پاسخی را نداشت؛ چون حالت Strict به این معنا است که کوکی‌های سایت (منجمله کوکی‌ اعتبارسنجی کاربر)، فقط به درخواست‌های رسیده‌ی از طریق خود سایت، به صورت خودکار توسط مرورگرها ارسال می‌شوند و نه در حالتیکه یک redirect از سایت دیگری وجود داشته باشد. برای تعدیل این تنظیم می‌توان از حالت Lax استفاده کرد:

options.Cookie.SameSite = SameSiteMode.Lax;

در اصل، حالت Lax با حالت Strict، تفاوت آنچنانی ندارد و هنوز هم کوکی‌ها را فقط به درخواست‌های رسیده‌ی از طریق دومین جاری ارسال می‌کند. اما در این حالت اگر redirect ای نیز به دومین برنامه، از نوع GET وجود داشته باشد، آن‌را امن حساب کرده و کوکی‌های برنامه را در اختیار آن درخواست قرار می‌دهد.

پ.ن.

برای دریافت کوکی‌های جدید از نوع Lax، باید یکبار از برنامه خارج شوید و مجددا به آن وارد شوید تا کوکی‌های جدید را دریافت کنید.

نظرات مطالب
رمزنگاری Connection String از طریق خط فرمان
حاصل این رمزنگاری بر اساس کلیدهای موجود در کامپیوترهای مختلف متفاوت خواهد بود. بنابراین روی سرور باید رمزنگاری شود. اگر دسترسی مدیریتی دارید، روش خط فرمان مناسب است؛ اگر خیر، این روش با برنامه نویسی هم قابل اجرا است.
مطالب
WebStorage: قسمت دوم
در این مقاله قصد داریم نحوه‌ی کدنویسی webstorage را با کتابخانه‌هایی که در مقاله قبل معرفی کردیم بررسی کنیم.
ابتدا روش ذخیره سازی و بازیابی متداول آن را بررسی میکنیم که تنها توسط دو تابع صورت می‌گیرد. مطلب زیر برگرفته از w3Schools است:
دسترسی به شیء webstorage به صورت زیر امکان پذیر است:
window.localStorage
window.sessionStorage
ولی بهتر است قبل از ذخیره و بازیابی، از پشتیبانی مرورگر از webstorage اطمینان حاصل نمایید:
if(typeof(Storage) !== "undefined") {
    // Code for localStorage/sessionStorage.
} else {
    // Sorry! No Web Storage support..
}
برای ذخیره سازی و سپس خواندن به شکل زیر عمل می‌کنیم:
localStorage.setItem("lastname", "Smith");

//========================
localStorage.getItem("lastname");
خواندن می‌تواند حتی به شکل زیر هم صورت بگیرد:
var a=localStorage.lastname;
استفاده از store.js برای مرورگرهایی که از webstorage پشتیبانی نمی‌کنند به شکل زیر است:
//ذخیره مقدار
store.set('username', 'marcus')

//بازیابی مقدار
store.get('username')

//حذف مقدار
store.remove('username')

//حذف تمامی مقادیر ذخیره شده
store.clear()

//ذخیره ساختار
store.set('user', { name: 'marcus', likes: 'javascript' })

//بازیابی ساختار به شکل قبلی
var user = store.get('user')
alert(user.name + ' likes ' + user.likes)

//تغییر مستقیم مقدار قبلی
store.getAll().user.name == 'marcus'

//بازخوانی تمام مقادیر ذخیر شده توسط یک حلقه
store.forEach(function(key, val) {
    console.log(key, '==', val)
})
همچنین بهتر هست از یک فلگ برای بررسی فعال بودن storage استفاده نمایید. به این دلیل که گاهی کاربرها از پنجره‌های private استفاده می‌کنند که ردگیری اطلاعات آن ممکن نیست و موجب خطا می‌شود.
<script src="store.min.js"></script>
<script>
    init()
    function init() {
        if (!store.enabled) {
            alert('Local storage is not supported by your browser. Please disable "Private Mode", or upgrade to a modern browser.')
            return
        }
        var user = store.get('user')
        // ... and so on ...
    }
</script>
در صورتیکه بخشی از داده‌ها را توسط localstorage ذخیره نمایید و بخواهید از طریق storage به آن دسترسی داشته باشید، خروجی string خواهد بود؛ صرف نظر از اینکه شما عدد، شیء یا آرایه‌ای را ذخیره کرده‌اید.
در صورتیکه ساختار JSON را ذخیره کرده باشید، می‌توانید رشته برگردانده شده را با json.stringify و json.parse بازیابی و به روز رسانی کنید.
در حالت cross browser تهیه‌ی یک sessionStorage امکان پذیر نیست. ولی می‌توان به روش ذیل و تعیین یک زمان انقضاء آن را محدود کرد:
var storeWithExpiration = {
// دریافت کلید و مقدار و زمان انقضا به میلی ثانیه
    set: function(key, val, exp) {
//ایجاد زمان فعلی جهت ثبت تاریخ ایجاد
        store.set(key, { val:val, exp:exp, time:new Date().getTime() })
    },
    get: function(key) {
        var info = store.get(key)
//در صورتی که کلید داده شده مقداری نداشته باشد نال را بر میگردانیم
        if (!info) { return null }
//تاریخ فعلی را منهای تاریخ ثبت شده کرده و در صورتی که
//از مقدار میلی ثاینه بیشتر باشد یعنی منقضی شده و نال بر میگرداند
        if (new Date().getTime() - info.time > info.exp) { return null }
        return info.val
    }
}

// استفاده عملی از کد بالا
// استفاده از تایمر جهت نمایش واکشی داده‌ها قبل از نقضا و بعد از انقضا
storeWithExpiration.set('foo', 'bar', 1000)
setTimeout(function() { console.log(storeWithExpiration.get('foo')) }, 500) // -> "bar"
setTimeout(function() { console.log(storeWithExpiration.get('foo')) }, 1500) // -> null

مورد بعدی استفاده از سورس  cross-storage  است. اگر به یاد داشته باشید گفتیم یکی از احتمالاتی که برای ما ایجاد مشکل می‌کند، ساب دومین هاست که ممکن است دسترسی ما به یک webstorage را از ساب دومین دیگر از ما بگیرد.
این کتابخانه به دو جز تقسیم شده است یکی هاب Hub و دیگری Client .
ابتدا نیاز است که هاب را آماده سازی و با ارائه یک الگو از آدرس وب، مجوز عملیات را دریافت کنیم. در صورتیکه این مرحله به فراموشی سپرده شود، انجام هر نوع عمل روی دیتاها در نظر گرفته نخواهد شد.
CrossStorageHub.init([
  {origin: /\.example.com$/,            allow: ['get']},
  {origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);
حرف $ در انتهای عبارت باعث مشود که دامنه‌ها با دقت بیشتری در Regex بررسی شوند و دامنه زیر را معتبر اعلام کند:
valid.example.com
ولی دامنه زیر را نامعتبر اعلام می‌کند:
invalid.example.com.malicious.com
همچنین می‌توانید تنظیماتی را جهت هدر‌های CSP و CORS، نیز اعمال نمایید:
{
  'Access-Control-Allow-Origin':  '*',
  'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
  'Access-Control-Allow-Headers': 'X-Requested-With',
  'Content-Security-Policy':      "default-src 'unsafe-inline' *",
  'X-Content-Security-Policy':    "default-src 'unsafe-inline' *",
  'X-WebKit-CSP':                 "default-src 'unsafe-inline' *",
}
پس کار را بدین صورت آغاز می‌کنیم، یک فایل به نام hub.htm درست کنید و هاب را آماده سازید:
hub.htm
<script type="text/javascript" src="~/Scripts/cross-storage/hub.js"></script>
<script>
 CrossStorageHub.init([
      {origin: /.*localhost:300\d$/, allow: ['get', 'set', 'del']}
    ]);
  </script>
کد بالا فقط درخواست‌های هاست لوکال را از پورتی که ابتدای آن با 300 آغاز می‌شود، پاسخ می‌دهد و مابقی درخواست‌ها را رد می‌کند. متدهای ایجاد، ویرایش و حذف برای این آدرس معتبر اعلام شده است. 
در فایل دیگر که کلاینت شناخته می‌شود باید فایل hub معرفی شود تا تنظیمات هاب خوانده شود:
var storage = new CrossStorageClient('http://localhost:3000/example/hub.html');

var setKeys = function () {
      
return storage.set('key1', 'foo').then(function() {
return storage.set('key2', 'bar');
      });
};
در خط اول، فایل هاب معرفی شده و تنظیمات روی این صفحه اعمال می‌شود. سپس در خطوط بعدی داده‌ها ذخیره می‌شوند. از آنجا که با هر یکبار ذخیره، return صورت می‌گیرد و تنها اجازه‌ی ورود یک داده را داریم، برای حل این مشکل متد then پیاده سازی شده است. متغیر setKeys شامل یک آرایه از کلیدها خواهد بود.
نحوه‌ی ذخیره سازی بدین شکل هم طبق مستندات صحیح است:
storage.onConnect().then(function() {

  return storage.set('key', {foo: 'bar'});

}).then(function() {

  return storage.set('expiringKey', 'foobar', 10000);

});
در کد بالا ابتدا یک داده دائم ذخیره شده است و در کد بعد یک داده موقت که بعد از 10 ثانیه اعتبار خود را از دست می‌دهد.
برای خواندن داده‌های ذخیره شده به نحوه زیر عمل می‌کنیم:
storage.onConnect().then(function() {
  return storage.get('key1');
}).then(function(res) {
  return storage.get('key1', 'key2', 'key3');
}).then(function(res) {
  // ...
});
کد بالا نحوه‌ی خواندن مقادیر را به شکل‌های مختلفی نشان میدهد و مقدار بازگشتی آن‌ها یک آرایه از مقادیر است؛ مگر اینکه تنها یک مقدار برگشت داده شود. مقدار بازگشتی در تابع بعدی به عنوان یک آرگومان در دسترس است. در صورتی که خطایی رخ دهد، قابلیت هندل آن نیز وجود دارد:
storage.onConnect()
    .then(function() {
      return storage.get('key1', 'key2');
    })

.then(function(res) {
      console.log(res); // ['foo', 'bar']
    })['catch'](function(err) {
      console.log(err);
    });
برای باقی مسائل چون به دست آوردن لیست کلیدهای ذخیره شده، حذف کلید‌های مشخص شده، پاکسازی کامل داده‌ها و ... به مستندات رجوع کنید.
در اینجا جهت سازگاری با مرورگرهای قدیمی خط زیر را به صفحه اضافه کنید:
<script src="https://s3.amazonaws.com/es6-promises/promise-1.0.0.min.js"></script>
ذخیره‌ی اطلاعات به شکل یونیکد، فضایی دو برابر کدهای اسکی میبرد و با توجه به محدود بودن حجم webstorage به 5 مگابایت ممکن است با کمبود فضا مواجه شوید. در صورتیکه قصد فشرده سازی اطلاعات را دارید می‌توانید از کتابخانه lz-string استفاده کنید. ولی توجه به این نکته ضروری است که در صورت نیاز، عمل فشرده سازی را انجام دهید و همینطوری از آن استفاده نکنید.

IndexedDB API
آخرین موردی که بررسی می‌شود استفاده از IndexedDB API است که با استفاده از آن میتوان با webstorage همانند یک دیتابیس رفتار کرد و به سمت آن کوئری ارسال کرد. 
var request = indexedDB.open("library");

request.onupgradeneeded = function() {
  // The database did not previously exist, so create object stores and indexes.
  var db = request.result;
  var store = db.createObjectStore("books", {keyPath: "isbn"});
  var titleIndex = store.createIndex("by_title", "title", {unique: true});
  var authorIndex = store.createIndex("by_author", "author");

  // Populate with initial data.
  store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
  store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
  store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
};

request.onsuccess = function() {
  db = request.result;
};
کد بالا ابتدا به دیتابیس library متصل می‌شود و اگر وجود نداشته باشد، آن را می‌سازد. رویداد onupgradeneeded برای اولین بار اجرا شده و در آن می‌توانید به ایجاد جداول و اضافه کردن داده‌های اولیه بپردازید؛ یا اینکه از آن جهت به ارتقاء ورژن دیتابیس استفاده کنید. خصوصیت result، دیتابیس باز شده یا ایجاد شده را باز می‌گرداند. در خط بعدی جدولی با کلید کد ISBN کتاب تعریف شده است. در ادامه هم دو ستون اندیس شده برای عنوان کتاب و نویسنده معرفی شده است که عنوان کتاب را یکتا و بدون تکرار در نظر گرفته است. سپس در جدولی که متغیر store به آن اشاره می‌کند، با استفاده از متد put، رکوردها داخل آن درج می‌شوند. در صورتیکه کار با موفقیت انجام شود رویداد onSuccess فراخوانی می‌گردد.
برای انجام عملیات خواندن و نوشتن باید از تراکنش‌ها استفاده کرد:
var tx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});

tx.oncomplete = function() {
  // All requests have succeeded and the transaction has committed.
};
در خط  اول ابتدا یک خط تراکنشی بین ما و جدول books با مجوز خواندن و نوشتن باز می‌شود و در خط بعدی جدول books را در اختیار می‌گیریم و همانند کد قبلی به درج داده‌ها می‌پردازیم. در صورتیکه عملیات با موفقیت به اتمام برسد، متغیر تراکنش رویدادی به نام oncomplete فراخوانی می‌گردد. در صورتیکه قصد دارید تنها مجوز خواندن داشته باشید، عبارت readonly را به کار ببرید.
var tx = db.transaction("books", "readonly");
var store = tx.objectStore("books");
var index = store.index("by_author");

var request = index.openCursor(IDBKeyRange.only("Fred"));
request.onsuccess = function() {
  var cursor = request.result;
  if (cursor) {
    // Called for each matching record.
    report(cursor.value.isbn, cursor.value.title, cursor.value.author);
    cursor.continue();
  } else {
    // No more matching records.
    report(null);
  }
};
در دو خط اول مثل قبل، تراکنش را دریافت می‌کنیم و از آنجا که می‌خواهیم داده را بر اساس نام نویسنده واکشی کنیم، ستون اندیس شده نام نویسنده را دریافت کرده و با استفاده از متد opencursor درخواست خود را مبنی بر واکشی رکوردهایی که نام نویسنده fred است، ارسال میداریم. در صورتیکه عملیات با موفقیت انجام گردد و خطایی دریافت نکنیم رویداد onsuccess فراخوانی می‌گردد. در این رویداد با دو حالت برخورد خواهیم داشت؛ یا داده‌ها یافت می‌شوند و رکوردها برگشت داده می‌شوند یا هیچ رکوردی یافت نشده و مقدار نال برگشت خواهد خورد. با استفاده از cursor.continue می‌توان داده‌ها را به ترتیب واکشی کرده و مقادیر رکورد را با استفاده خصوصیت value  به سمت تابع report ارسال کرد.
کدهای بالا همه در مستندات معرفی شده وجود دارند و ما پیشتر توضیح ابتدایی در مورد آن دادیم و برای کسب اطلاعات بیشتر می‌توانید به همان مستندات معرفی شده رجوع کنید. برای idexedDB هم می‌توانید از این منابع +  +  +  استفاده کنید که خود w3c منبع فوق العاده‌تری است.

مطالب دوره‌ها
آشنایی با AOP Interceptors
در حین استفاده از Interceptors، کار مداخله و تحت نظر قرار دادن قسمت‌های مختلف کدها، توسط کامپوننت‌های خارجی صورت خواهد گرفت. این کامپوننت‌های خارجی، به صورت پویا، تزئین کننده‌هایی را جهت محصور سازی قسمت‌های مختلف کدهای شما تولید می‌کنند. این‌ها، بسته به توانایی‌هایی که دارند، در زمان اجرا و یا حتی در زمان کامپایل نیز قابل تنظیم می‌باشند.


ابزارهایی جهت تولید AOP Interceptors

متداول‌ترین کامپوننت‌های خارجی که جهت تولید AOP Interceptors مورد استفاده قرار می‌گیرند، همان IOC Containers معروف هستند مانند StructureMap، Ninject، MS Unity و غیره.
سایر ابزارهای تولید AOP Interceptors، از روش تولید Dynamic proxies بهره می‌گیرند. به این ترتیب مزین کننده‌هایی پویا، در زمان اجرا، کدهای شما را محصور خواهند کرد. (نمونه‌ای از آن‌را شاید در حین کار با ORMهای مختلف دیده باشید).


نگاهی به فرآیند Interception

زمانیکه از یک IOC Container در کدهای خود استفاده می‌کنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست می‌کند. عموما اینکار با درخواست یک اینترفیس صورت می‌گیرد؛ هرچند محدودیتی نیز وجود نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
ج) سپس، کد فراخوان، وهله دریافتی را مورد پردازش قرار داده و شروع به استفاده از متدها و خواص آن خواهد نمود.

اکنون با اضافه کردن Interception به این پروسه، چند مرحله دیگر نیز در این بین به آن اضافه خواهند شد:
الف) در اینجا نیز در ابتدا کد فراخوان، درخواست وهله‌ای را بر اساس اینترفیسی خاص به IOC Container ارائه می‌دهد.
ب) IOC Container نیز سعی در وهله سازی درخواست رسیده بر اساس تنظیمات اولیه خود می‌کند.
ج) اما در این حالت IOC Container تشخیص می‌دهد، نوعی که باید بازگشت دهد، علاوه بر وهله سازی، نیاز به مزین سازی توسط  Aspects و پیاده سازی Interceptors را نیز دارد. بنابراین نوع مورد انتظار را در صورت وجود، به یک Dynamic Proxy، بجای بازگشت مستقیم به فراخوان ارائه می‌دهد.
د) در  ادامه Dynamic Proxy، نوع مورد انتظار را توسط Interceptors محصور کرده و به فراخوان بازگشت می‌دهد.
ه) اکنون فراخوان، در حین استفاده از امکانات شیء وهله سازی شده، به صورت خودکار مراحل مختلف اجرای یک Aspect را که در قسمت قبل بررسی شدند، سبب خواهد شد.


نحوه ایجاد Interceptors

برای ایجاد یک Interceptor دو مرحله باید انجام شود:
الف) پیاده سازی یک اینترفیس
ب) اتصال آن به کدهای اصلی برنامه

در ادامه قصد داریم از یک IOC Container معروف به نام StructureMap در یک برنامه کنسول استفاده کنیم. برای دریافت آن نیاز است دستور پاورشل ذیل را در کنسول نوگت ویژوال استودیو فراخوانی کنید:
 PM> Install-Package structuremap
پس از آن یک برنامه کنسول جدید را ایجاد کنید. (هدف از استفاده از این نوع پروژه خاص، توضیح جزئیات یک فناوری، بدون درگیر شدن با لایه UI است)
البته باید دقت داشت که برای استفاده از StructureMap نیاز است به خواص پروژه مراجعه و سپس حالت Client profile را به Full profile تغییر داد تا برنامه قابل کامپایل باشد.
using System;
using StructureMap;

namespace AOP00
{
    public interface IMyType
    {
        void DoSomething(string data, int i);
    }

    public class MyType : IMyType
    {
        public void DoSomething(string data, int i)
        {
            Console.WriteLine("DoSomething({0}, {1});", data, i);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ObjectFactory.Initialize(x =>
            {
                x.For<IMyType>().Use<MyType>();
            });

            var myType = ObjectFactory.GetInstance<IMyType>();
            myType.DoSomething("Test", 1);
        }
    }
}
اکنون کدهای این برنامه را به نحو فوق تغییر دهید.
در اینجا یک اینترفیس نمونه و پیاده سازی آن‌را ملاحظه می‌کنید. همچنین نحوه آغاز تنظیمات StructureMap و نحوه دریافت یک وهله متناظر با IMyType نیز بیان شده‌اند.
نکته‌ی مهمی که در اینجا باید به آن دقت داشت، وضعیت شیء myType حین فراخوانی متد myType.DoSomething است. شیء myType در اینجا، دقیقا یک وهله‌ی متداول از کلاس myType است و هیچگونه دخل و تصرفی در نحوه اجرای آن صورت نگرفته است.
خوب! تا اینجای کار را احتمالا پیشتر نیز دیده بودید. در ادامه قصد داریم یک Interceptor را طراحی و مراحل چهارگانه اجرای یک Aspect را در اینجا بررسی کنیم.

در ادامه نیاز خواهیم داشت تا یک Dynamic proxy را نیز مورد استفاده قرار دهیم؛ از این جهت که StructureMap تنها دارای Interceptorهای وهله سازی اطلاعات است و نه Method Interceptor. برای دسترسی به Method Interceptors نیاز به یک Dynamic proxy نیز می‌باشد. در اینجا از Castle.Core استفاده خواهیم کرد:
 PM> Install-Package Castle.Core
برای دریافت آن تنها کافی است دستور پاور شل فوق را در خط فرمان کنسول پاورشل نوگت در VS.NET اجرا کنید.
سپس کلاس ذیل را به پروژه جاری اضافه کنید:
using System;
using Castle.DynamicProxy;

namespace AOP00
{
    public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                Console.WriteLine("Logging On Start.");

                invocation.Proceed(); //فراخوانی متد اصلی در اینجا صورت می‌گیرد

                Console.WriteLine("Logging On Success.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Logging On Error.");
                throw;
            }
            finally
            {
                Console.WriteLine("Logging On Exit.");
            }
        }
    }
}
در کلاس فوق کار Method Interception توسط امکانات Castle.Core انجام شده است. این کلاس باید اینترفیس IInterceptor را پیاده سازی کند. در این متد سطر invocation.Proceed دقیقا معادل فراخوانی متد مورد نظر است. مراحل چهارگانه شروع، پایان، خطا و موفقیت نیز توسط try/catch/finally پیاده سازی شده‌اند.

اکنون برای معرفی این کلاس به برنامه کافی است سطرهای ذیل را اندکی ویرایش کنیم:
        static void Main(string[] args)
        {
            ObjectFactory.Initialize(x =>
            {
                var dynamicProxy = new ProxyGenerator();
                x.For<IMyType>().Use<MyType>();
                x.For<IMyType>().EnrichAllWith(myTypeInterface => dynamicProxy.CreateInterfaceProxyWithTarget(myTypeInterface, new LoggingInterceptor()));
            });

            var myType = ObjectFactory.GetInstance<IMyType>();
            myType.DoSomething("Test", 1);
        }
در اینجا تنها سطر EnrichAllWith آن جدید است. ابتدا یک پروکسی پویا تولید شده است. سپس این پروکسی پویا کار دخالت و تحت نظر قرار دادن اجرای متدهای اینترفیس IMyType را عهده دار خواهد شد.
برای مثال اکنون با فراخوانی متد myType.DoSomething، ابتدا کنترل برنامه به پروکسی پویای تشکیل شده توسط Castle.Core منتقل می‌شود. در اینجا هنوز هم متد DoSomething فراخوانی نشده است. ابتدا وارد بدنه متد public void Intercept خواهیم شد. سپس سطر invocation.Proceed، فراخوانی واقعی متد DoSomething اصلی را انجام می‌دهد. در ادامه باز هم فرصت داریم تا مراحل موفقیت، خطا یا خروج را لاگ کنیم.
تنها زمانیکه کار متد public void Intercept به پایان می‌رسد، سطر پس از فراخوانی متد  myType.DoSomething اجرا خواهد شد.
در این حالت اگر برنامه را اجرا کنیم، چنین خروجی را نمایش می‌دهد:
 Logging On Start.
DoSomething(Test, 1);
Logging On Success.
Logging On Exit.
بنابراین در اینجا نحوه دخالت و تحت نظر قرار دادن اجرای متدهای یک کلاس عمومی خاص را ملاحظه می‌کنید. برای اینکه کنترل کامل را در دست بگیریم، کلاس پروکسی پویا وارد عمل شده و اینجا است که این کلاس پروکسی تصمیم می‌گیرد چه زمانی باید فراخوانی واقعی متد مورد نظر انجام شود.
برای اینکه فراخوانی قسمت On Error را نیز ملاحظه کنید، یک استثنای عمدی را در متد DoSomething قرار داده و مجددا برنامه را اجرا کنید.
مطالب
نمایش خروجی RSS سایت‌های دیگر به کمک jQuery
شاید خیلی از دوستان (مثل گذشته نه چندان دور خودم ) خیلی بیش از اندازه به برنامه نویسی‌های سمت سرور اهمیت  می دهند که این کار باعث از دست دادن ، سرعت و سادگی برنامه نویسی  سمت کلاینت می‌شود.
معمولا ما برای کار با خروجی‌های XML از کد‌های سمت سرور استفاده می‌کنیم ، بدون اینکه از قدرت جی کوئری در این زمینه  اطلاعی داشته باشیم. البته در این مقاله خیلی به پردازش XML  توسط جی کوئری نمی‌پردازیم و کار اصلی را گوگل برای ما انجام می‌دهد.
برای انجام این کار ما ابتدا توسط یکی از کتابخانه‌های گوگل ، خروجی RSS یک وب سایت رامی خوانیم و بعد به کمک jQuery  آن‌ها را نمایش می‌دهیم.
    <script src="jquery-1.8.0.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">

        google.load("feeds", "1");

        function initializeda() {
            var feed = new google.feeds.Feed("http://www.drupaleasy.ir/rss.xml");
            feed.setNumEntries(5);
            feed.setResultFormat(google.feeds.Feed.JSON_FORMAT);
            feed.load(function (result) {
                if (!result.error) {
                    for (var i = 0; i < result.feed.entries.length; i++) {
                        var entry = result.feed.entries[i];
                        $('#drupaleasy ul').append('<li><a href="' + entry.link + '">' + entry.title + '</a></li>');
                    }
                }
            });
        }

google.setOnLoadCallback(initializeda);
    </script>
توضیحات کد :
ابتدا نیاز داریم کتابخانه گوگل را به صفحه اضافه کنیم. شما می‌توانید مستندات کامل این کتابخانه در این لینک  بخوانید.
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
در ابتدا معرفی می‌کنیم که به کدام امکان این کتابخانه نیاز داریم ، در این جا ما از ورژن "1" تابع feed  استفاده می‌کنیم.بد نیست برای آشنایی بیشتر سری هم به این لینک  بزنید.
google.load("feeds", "1");
ابتدا لینک مورد نظر که باید خروجی از آن گرفته شود را معرفی می‌کنیم و آن را در متغییری قرار می‌دهیم.
var feed = new google.feeds.Feed("http://www.drupaleasy.ir/rss.xml");
تنظیمات دلخواه خودمان را نظیر تعداد پست واکشی شده و نوع خروجی مشخص می‌کنیم. تجربه (شخصی )نشان داده که بهترین نوع خروجی JSON   است.
feed.setNumEntries(5);
feed.setResultFormat(google.feeds.Feed.JSON_FORMAT);
و بعد هم به کمک jQuery  ساختار مورد نظر خودمان را تنظیم می‌کنیم. شما می‌توانید به کمک مستندات ، و سلیقه شخصی خودتان این کار را انجام بدهید ، و در آخر هم تنظیم می‌کنید ، اجرای تابع شما در چه زمانی اتفاق بیافتید.
            feed.load(function (result) {
                if (!result.error) {
                    for (var i = 0; i < result.feed.entries.length; i++) {
                        var entry = result.feed.entries[i];
                        $('#drupaleasy ul').append('<li><a href="' + entry.link + '">' + entry.title + '</a></li>');
                    }
                }
            });
در این جا من خروجی فید را به صورت یک لیست (ul)  تنظیم کردم ، به این صورت که به ازای هر سطر یک li  همراه با تگ a  تولید کرده ام و به ul  مورد نظر append  کردم. 
فایل HTML :
            <div id="drupaleasy" class="feeds">
            <span>DrupalEasy.ir</span>
                <ul>
                </ul>
                <a href="http://drupaleasy.ir">more</a>
            </div>
همچنین خیلی راحت می‌توانید به کمک CSS  استایل‌های دلخواه خودتون رو اعمال کنید.
        .feeds
        {
            float: right;
            background-color: rgba(234, 242, 243, 0.73);
            margin: 5px;
            border-radius: 20px;
            padding: 8px;
            width: 293px;
            height: 217px;
            border: 1px solid #293883;
        }

        #drupaleasy ul
        {
            list-style-image: url("img/drupal.png");
        }
این هم شد خروجی کار  من 

اشتراک‌ها
معرفی Script#؛ اسکریپت نویسی با جاوا‌اسکریپت در دات‌نت

#Script is a simple, fast, highly versatile and embeddable scripting language for .NET Core and .NET Apps that utilizes a familiar JavaScript inspired expressive Syntax to enable dynamic scripting of .NET Apps via controlled access to pluggable methods and arguments within a sandbox environment - ensuring scripts are encapsulated and encourages the use of reusable and testable components where live environments can be easily re-created and simulated.  

معرفی Script#؛ اسکریپت نویسی با جاوا‌اسکریپت در دات‌نت
اشتراک‌ها
دوره 3 ساعت و نیمه Asp.Net Core SignalR

Asp.Net Core WebSockets Vs SignalR. Which should you use? (Full Course)

In this video we build 2 separate chat applications, one using Asp.Net Core WebSockets and the other using SignalR, allowing you to compare approaches and decide on which one works best for you. In both cases we build them with C#, .NET Core and JavaScript. You’ll also learn about:

- .NET Core Request Pipeline
- Request Delegates
- Asynchronous Programming in .NET (Async / Await)
- Introduction to Dependency Injection  

دوره 3 ساعت و نیمه Asp.Net Core SignalR
اشتراک‌ها
پیشنهاد اضافه شدن type به JavaScript

Today we’re excited to announce our support and collaboration on a new Stage 0 proposal to bring optional and erasable type syntax to JavaScript. Because this new syntax wouldn’t change how surrounding code runs, it would effectively act as comments. We think this has the potential to make TypeScript easier and faster to use for development at every scale. We’d like to talk about why we’re pursuing this, and how this proposal works at a high level.

 

پیشنهاد اضافه شدن type به JavaScript