نظرات مطالب
وب‌پارت اسلاید متحرک در شیرپوینت 2010
نکته‌ای عمومی در مورد تمام محصولات مایکروسافت:
«اگر کپی مطابق اصل خطای مشاهده شده را در جستجوی گوگل paste کنید و بر روی دکمه جستجو کلیک نمائید، در همان صفحه اول به جواب خواهید رسید»
این خطاها و این جملات بارها در سایت‌های مختلف تکرار شدن. چون تغییری هم نخواهند کرد، به عنوان یک مرجع رفع اشکال مورد استفاده قرار می‌گیرند.
مثلا همین عبارت بالا را که جستجو کنید به این راه حل خواهید رسید.
مطالب
معماری لایه بندی نرم افزار #4

UI

در نهایت نوبت به طراحی و کدنویسی UI می‌رسد تا بتوانیم محصولات را به کاربر نمایش دهیم. اما قبل از شروع باید موضوعی را یادآوری کنم. اگر به یاد داشته باشید، در کلاس ProductService موجود در لایه‌ی Domain، از طریق یکی از روشهای الگوی Dependency Injection به نام Constructor Injection، فیلدی از نوع IProductRepository را مقداردهی نمودیم. حال زمانی که بخواهیم نمونه ای را از ProductService ایجاد نماییم، باید به عنوان پارامتر ورودی سازنده، شی ایجاد شده از جنس کلاس ProductRepository موجود در لایه Repository را به آن ارسال نماییم. اما از آنجایی که می‌خواهیم تفکیک پذیری لایه‌ها از بین نرود و UI بسته به نیاز خود، نمونه مورد نیاز را ایجاد نموده و به این کلاس ارسال کند، از ابزارهایی برای این منظور استفاده می‌کنیم. یکی از این ابزارها StructureMap می‌باشد که یک Inversion of Control Container یا به اختصار IoC Container نامیده می‌شود. با Inversion of Control در مباحث بعدی بیشتر آشنا خواهید شد. StructureMap ابزاری است که در زمان اجرا، پارامترهای ورودی سازنده‌ی کلاسهایی را که از الگوی Dependency Injection استفاده نموده اند و قطعا پارامتر ورودی آنها از جنس یک Interface می‌باشد را، با ایجاد شی مناسب مقداردهی می‌نماید.

به منظور استفاده از StructureMap در Visual Studio 2012 باید بر روی پروژه UI خود کلیک راست نموده و گزینه‌ی Manage NuGet Packages را انتخاب نمایید. در پنجره ظاهر شده و از سمت چپ گزینه‌ی Online و سپس در کادر جستجوی سمت راست و بالای پنجره واژه‌ی structuremap را جستجو کنید. توجه داشته باشید که باید به اینترنت متصل باشید تا بتوانید Package مورد نظر را نصب نمایید. پس از پایان عمل جستجو، در کادر میانی structuremap ظاهر می‌شود که می‌توانید با انتخاب آن و فشردن کلید Install آن را بر روی پروژه نصب نمایید.

جهت آشنایی بیشتر با NuGet و نصب آن در سایر نسخه‌های Visual Studio می‌توانید به لینک‌های زیر رجوع کنید:

1. آشنایی با  NuGetقسمت اول

2. آشنایی با  NuGetقسمت دوم

3. Installing NuGet

کلاسی با نام BootStrapper را با کد زیر به پروژه UI خود اضافه کنید:

using StructureMap;
using StructureMap.Configuration.DSL;
using SoCPatterns.Layered.Repository;
using SoCPatterns.Layered.Model;

namespace SoCPatterns.Layered.WebUI
{
    public class BootStrapper
    {
        public static void ConfigureStructureMap()
        {
            ObjectFactory.Initialize(x => x.AddRegistry<ProductRegistry>());
        }
    }
    public class ProductRegistry : Registry
    {
        public ProductRegistry()
        {
            For<IProductRepository>().Use<ProductRepository>();
        }
    }
}

ممکن است یک WinUI ایجاد کرده باشید که در این صورت به جای فضای نام SoCPatterns.Layered.WebUI از SoCPatterns.Layered.WinUI استفاده نمایید.

هدف کلاس BootStrapper این است که تمامی وابستگی‌ها را توسط StructureMap در سیستم Register نماید. زمانی که کدهای کلاینت می‌خواهند به یک کلاس از طریق StructureMap دسترسی داشته باشند، Structuremap وابستگی‌های آن کلاس را تشخیص داده و بصورت خودکار پیاده سازی واقعی (Concrete Implementation) آن کلاس را، براساس همان چیزی که ما برایش تعیین کردیم، به کلاس تزریق می‌نماید. متد ConfigureStructureMap باید در همان لحظه ای که Application آغاز به کار می‌کند فراخوانی و اجرا شود. با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

فایل Global.asax را به پروژه اضافه کنید و کد آن را بصورت زیر تغییر دهید:

namespace SoCPatterns.Layered.WebUI
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            BootStrapper.ConfigureStructureMap();
        }
    }
}

در WinUI:

در فایل Program.cs کد زیر را اضافه کنید:

namespace SoCPatterns.Layered.WinUI
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            BootStrapper.ConfigureStructureMap();
            Application.Run(new Form1());
        }
    }
}

سپس برای طراحی رابط کاربری، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

صفحه Default.aspx را باز نموده و کد زیر را به آن اضافه کنید:

<asp:DropDownList AutoPostBack="true" ID="ddlCustomerType" runat="server">
    <asp:ListItem Value="0">Standard</asp:ListItem>
    <asp:ListItem Value="1">Trade</asp:ListItem>
</asp:DropDownList>
<asp:Label ID="lblErrorMessage" runat="server" ></asp:Label>
<asp:Repeater ID="rptProducts" runat="server" >
    <HeaderTemplate>
        <table>
            <tr>
                <td>Name</td>
                <td>RRP</td>
                <td>Selling Price</td>
                <td>Discount</td>
                <td>Savings</td>
            </tr>
            <tr>
                <td colspan="5"><hr /></td>
            </tr>
    </HeaderTemplate>
    <ItemTemplate>
            <tr>
                <td><%# Eval("Name") %></td>
                <td><%# Eval("RRP")%></td>
                <td><%# Eval("SellingPrice") %></td>
                <td><%# Eval("Discount") %></td>
                <td><%# Eval("Savings") %></td>
            </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

در WinUI:

فایل Form1.Designer.cs را باز نموده و کد آن را بصورت زیر تغییر دهید:

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
    this.cmbCustomerType = new System.Windows.Forms.ComboBox();
    this.dgvProducts = new System.Windows.Forms.DataGridView();
    this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colRrp = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colSellingPrice = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colDiscount = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colSavings = new System.Windows.Forms.DataGridViewTextBoxColumn();
    ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).BeginInit();
    this.SuspendLayout();
    //
    // cmbCustomerType
    //
    this.cmbCustomerType.DropDownStyle =                                                                                                                                                                             System.Windows.Forms.ComboBoxStyle.DropDownList;
    this.cmbCustomerType.FormattingEnabled = true;
    this.cmbCustomerType.Items.AddRange(new object[] {
        "Standard",
        "Trade"});
    this.cmbCustomerType.Location = new System.Drawing.Point(12, 90);
    this.cmbCustomerType.Name = "cmbCustomerType";
    this.cmbCustomerType.Size = new System.Drawing.Size(121, 21);
    this.cmbCustomerType.TabIndex = 3;
    //
    // dgvProducts
    //
    this.dgvProducts.ColumnHeadersHeightSizeMode =     System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
    this.dgvProducts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
    this.colName,
    this.colRrp,
    this.colSellingPrice,
    this.colDiscount,
    this.colSavings});
    this.dgvProducts.Location = new System.Drawing.Point(12, 117);
    this.dgvProducts.Name = "dgvProducts";
    this.dgvProducts.Size = new System.Drawing.Size(561, 206);
    this.dgvProducts.TabIndex = 2;
    //
    // colName
    //
    this.colName.DataPropertyName = "Name";
    this.colName.HeaderText = "Product Name";
    this.colName.Name = "colName";
    this.colName.ReadOnly = true;
    //
    // colRrp
    //
    this.colRrp.DataPropertyName = "Rrp";
    this.colRrp.HeaderText = "RRP";
    this.colRrp.Name = "colRrp";
    this.colRrp.ReadOnly = true;
    //
    // colSellingPrice
    //
    this.colSellingPrice.DataPropertyName = "SellingPrice";
    this.colSellingPrice.HeaderText = "Selling Price";
    this.colSellingPrice.Name = "colSellingPrice";
    this.colSellingPrice.ReadOnly = true;
    //
    // colDiscount
    //
    this.colDiscount.DataPropertyName = "Discount";
    this.colDiscount.HeaderText = "Discount";
    this.colDiscount.Name = "colDiscount";
    //
    // colSavings
    //
    this.colSavings.DataPropertyName = "Savings";
    this.colSavings.HeaderText = "Savings";
    this.colSavings.Name = "colSavings";
    this.colSavings.ReadOnly = true;
    //
    // Form1
    //
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(589, 338);
    this.Controls.Add(this.cmbCustomerType);
    this.Controls.Add(this.dgvProducts);
    this.Name = "Form1";
    this.Text = "Form1";
    ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).EndInit();
    this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ComboBox cmbCustomerType;
private System.Windows.Forms.DataGridView dgvProducts;
private System.Windows.Forms.DataGridViewTextBoxColumn colName;
private System.Windows.Forms.DataGridViewTextBoxColumn colRrp;
private System.Windows.Forms.DataGridViewTextBoxColumn colSellingPrice;
private System.Windows.Forms.DataGridViewTextBoxColumn colDiscount;
private System.Windows.Forms.DataGridViewTextBoxColumn colSavings;

سپس در Code Behind، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

وارد کد نویسی صفحه Default.aspx شده و کد آن را بصورت زیر تغییر دهید:

using System;
using System.Collections.Generic;
using SoCPatterns.Layered.Model;
using SoCPatterns.Layered.Presentation;
using SoCPatterns.Layered.Service;
using StructureMap;

namespace SoCPatterns.Layered.WebUI
{
    public partial class Default : System.Web.UI.Page, IProductListView
    {
        private ProductListPresenter _productListPresenter;
        protected void Page_Init(object sender, EventArgs e)
        {
            _productListPresenter = new ProductListPresenter(this,ObjectFactory.GetInstance<Service.ProductService>());
            this.ddlCustomerType.SelectedIndexChanged +=
                delegate { _productListPresenter.Display(); };
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if(!Page.IsPostBack)
                _productListPresenter.Display();
        }
        public void Display(IList<ProductViewModel> products)
        {
            rptProducts.DataSource = products;
            rptProducts.DataBind();
        }
        public CustomerType CustomerType
        {
            get { return (CustomerType) int.Parse(ddlCustomerType.SelectedValue); }
        }
        public string ErrorMessage
        {
            set
            {
                lblErrorMessage.Text =
                    String.Format("<p><strong>Error:</strong><br/>{0}</p>", value);
            }
        }
    }
}

در WinUI:

وارد کدنویسی Form1 شوید و کد آن را بصورت زیر تغییر دهید:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SoCPatterns.Layered.Model;
using SoCPatterns.Layered.Presentation;
using SoCPatterns.Layered.Service;
using StructureMap;

namespace SoCPatterns.Layered.WinUI
{
    public partial class Form1 : Form, IProductListView
    {
        private ProductListPresenter _productListPresenter;
        public Form1()
        {
            InitializeComponent();
            _productListPresenter =
                new ProductListPresenter(this, ObjectFactory.GetInstance<Service.ProductService>());
            this.cmbCustomerType.SelectedIndexChanged +=
                delegate { _productListPresenter.Display(); };
            dgvProducts.AutoGenerateColumns = false;
            cmbCustomerType.SelectedIndex = 0;
        }
        public void Display(IList<ProductViewModel> products)
        {
            dgvProducts.DataSource = products;
        }
        public CustomerType CustomerType
        {
            get { return (CustomerType)cmbCustomerType.SelectedIndex; }
        }
        public string ErrorMessage
        {
            set
            {
                MessageBox.Show(
                    String.Format("Error:{0}{1}", Environment.NewLine, value));
            }
        }
    }
}

با توجه به کد فوق، نمونه ای را از کلاس ProductListPresenter، در لحظه‌ی نمونه سازی اولیه‌ی کلاس UI، ایجاد نمودیم. با استفاده از متد ObjectFactory.GetInstance مربوط به StructureMap، نمونه ای از کلاس ProductService ایجاد شده است و به سازنده‌ی کلاس ProductListPresenter ارسال گردیده است. در مورد Structuremap در مباحث بعدی با جزئیات بیشتری صحبت خواهم کرد. پیاده سازی معماری لایه بندی در اینجا به پایان رسید.

اما اصلا نگران نباشید، شما فقط پرواز کوتاه و مختصری را بر فراز کدهای معماری لایه بندی داشته اید که این فقط یک دید کلی را به شما در مورد این معماری داده است. این معماری هنوز جای زیادی برای کار دارد، اما در حال حاضر شما یک Applicaion با پیوند ضعیف (Loosely Coupled) بین لایه‌ها دارید که دارای قابلیت تست پذیری قوی، نگهداری و پشتیبانی آسان و تفکیک پذیری قدرتمند بین اجزای آن می‌باشد. شکل زیر تعامل بین لایه‌ها و وظایف هر یک از آنها را نمایش می‌دهد.

مطالب
ذخیره و بازیابی فایل در Mongodb (بخش سوم)
در قسمت‌های پیشین (^ ،^ ) در مورد عملیات CRUD در سطح دیتابیس و به طور کلی در مورد ایندکس گذاری صحبت کردیم. در این بخش قصد داریم یکی از موارد بسیار مهم، یعنی ذخیره‌ی فایل‌های باینری را در دیتابیس، مورد بررسی قرار دهیم. روش‌های مختلفی برای اینکار وجود دارند؛ ولی بعضی از این روش‌ها در حال حاضر منسوخ شده اعلام شده‌اند که در اینجا ما آخرین روش را که در حال حاضر هیچ ویژگی منسوخ شده‌ای ندارد، به کار می‌گیریم.
از آنجاکه نهایت اندازه‌ی یک سند BSON نمی‌تواند بیشتر از 16 مگابایت باشد، قابلیتی به نام GridFS ایجاد شده‌است تا بتوان فایل‌های باینری را در آن ذخیره کرد. GridFS شامل دو بخش مختلف برای ذخیره اطلاعات یک فایل باینری است:
- fs.chunks که برای ذخیره اطلاعات قطعه‌های یک فایل باینری به کار میرود.
- fs.files که برای ذخیره اطلاعات و متادیتاها به کار می‌رود.

قبل از هر چیزی باید بدانید که کتابخانه مربوط به GridFs در یک پکیج جداگانه عرضه شده است و باید آن از طریق nuget نصب کنیم:
install-package MongoDB.Driver.GridFS

برای آپلود یک فایل باینری به داخل سیستم از کد زیر استفاده میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
ابتدا یک شیء GridFsBucket را ایجاد میکنیم که از ما اطلاعات دیتابیس مورد نظر را برای ارسال فایل میخواهد و نتیجه‌ی آن یک کلاس از جنس اینترفیس IGridFSBucket می‌باشد. این باکت یا سطل در واقع همانند کالکشن رفتار میکند.
byte[] source=File.ReadAllBytes(@"D:\Untitled.png");
var options = new GridFSUploadOptions
            {
                ChunkSizeBytes = 64512, // 63KB
                Metadata = new BsonDocument
                {
                    { "CoverType", "Front" }, 
                    { "copyrighted", true }
                }
            };
در مرحله بعد فایل باینری را به صورت آرایه‌ای از بایت‌ها میخوانیم (البته حالت‌های مختلفی چون استریم را نیز پشتیبانی میکند). بعد از خواندن فایل، یک شیء از جنس کلاس GridFSUploadOptions را ایجاد و اطلاعات فایل آپلودی را مشخص می‌کنیم. به عنوان مثال اولین خصوصیتی که پر میکنیم خصوصیت تعیین سایز قطعات فایل باینری می‌باشد که به طور پیشفرض بر روی 64 مگابایت قرار گرفته است و عموما هم برای اکثر موارد، پاسخگوی نیاز‌ها است (در بخش بعدی مقالات، بیشتر این مورد را بررسی میکنیم).
مورد دومی که مقداردهی شده‌است، متادیتا‌ها هستند و این قابلیت را داریم که پرس و جوی خود را بر اساس آن‌ها نیز فیلتر کنیم. این خصوصیت مقدار دریافتی از جنس BsonDocument را دریافت میکند. ولی اگر شما برای فایل خود، کلاس اختصاصی برای متادیتاها در نظر گرفته‌اید میتوانید از یک Extension Method به نام ToBsonDocument استفاده کنید و شیء خود را به این نوع تبدیل کنید:
 var options = new GridFSUploadOptions
            {
                ChunkSizeBytes = 64512, // 63KB
                Metadata = metaData.ToBsonDocument()
            };


در نهایت آن را آپلود میکنیم:
 var id = bucket.UploadFromBytes("GoneWithTheWind", source, options);
پارامتر اول آن، نامی برای بسته آپلودی است. پارامتر دوم، خود فایل آپلودی میباشد و با پارمتر آخر هم تنظیماتی را که برای فایل مورد نظر توسط کلاس GridFSUploadOptions  تعیین کرده‌ایم، مشخص میکنیم. موقعیکه آپلود انجام شود، به ازای این کد، یک شناسه‌ی اختصاصی از جنس ObjectId را دریافت میکنیم که می‌توانیم آن را به یک خصوصیت در سند اصلی نسبت دهیم تا ارتباط بین سند و فایل هایش را داشته باشیم.

نکته: اگر در یک کلاس، چند فیلد از جنس ObjectId دارید، مونگو در بین تشخیص شناسه اصلی سند و شناسه تصاویر، با توجه به نام خصوصیت‌ها و غیره، تا حد زیادی هوشمند عمل میکند. ولی اگر خواستید صریحا شناسه اصلی را ذکر کنید و آن را متمایز از بقیه نشان دهید، می‌توانید از خصوصیت BsonId در بالای نام فیلد ID استفاده کنید:
[BsonId]
public ObjectId Id { get; set; }

جهت خواندن فایل آپلود شده، تنها کافی است از طریق شناسه‌ی دریافتی در مرحله‌ی آپلود، اقدام نماییم:
var bytes = bucket.DownloadAsBytes(id);

نکته: تمام متدهای آپلود و دانلود دیتا، هم به صورت آرایه ای از بایت‌ها و هم به صورت استریم میتوانند مورد استفاده قرار بگیرند و به ازای هر کدام، متدهای همزمان و غیرهمزمان نیز موجود هستند.


اگر قصد دارید بر اساس نام داده شده، فایل را دریافت کنید، ممکن است که چندین فایل، تحت یک نام ذخیره شده باشند که میتوانید در حالت‌های مختلفی این تصاویر را واکشی نمایید:
0 : فایل اصلی
1: اولین نسخه فایل
2: دومین نسخه فایل
و الی آخر...

1-: جدیدترین نسخه فایل (مقدار پیش فرض)
2-: نسخه ماقبل جدیدترین نسخه فایل
و الی آخر...


منظور از نسخه، فایل‌هایی با نامی موجود و از قبل ذخیره شده هستند که نسخه جدیدی از فایل قبلی بوده و فایل اول، فایل اصلی محسوب میشود.
برای درک بهتر مسئله، من تصاویر زیر را به ترتیب از سمت راست به سمت چپ، تحت یک نام، وارد سیستم میکنم:


اولین تصویر، تصویر اصلی محسوب می‌شود و بعد از آن، نسخه اول و نسخه دوم تصویر، وارد سیستم می‌شوند و تکه کد زیر از آنجاکه با مقدار پیش فرض پر شده‌است باید آخرین تصویر، تصویر سمت چپ را برای شما بر روی دیسک ذخیره کند:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var image=bucket.DownloadAsBytesByName("City of Glass-cover");
File.WriteAllBytes(@"D:\a.jpg",image);

برای مقداردهی خواص بالا به شکل زیر عمل میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var options = new GridFSDownloadByNameOptions
            {
                Revision = 0
            };
var image=bucket.DownloadAsBytesByName("City of Glass-cover",options);
File.WriteAllBytes(@"D:\a.jpg",image);
با پاس کردن مقدار 0 به مونگو، اولین تصویر وارد شده، یعنی تصویر اصلی را دریافت می‌کنیم که اولین تصویر از سمت راست می‌شود. اگر مقادیر 1 یا 1- را پاس دهیم، چون تنها سه تصویر بیشتر نیست، در هر دو حالت تصویر دوم بازگردانده می‌شود.

برای بازگردانی تصاویر از طریق مقادیر موجود در متادیتا، باید از کلاس ویژه‌ای به نام GridFSFileInfo استفاده کنیم. در اینجا هم همانند روزهای اول، از کلاس بیلدر جهت ایجاد شرط استفاده میکنیم:
var client = new MongoClient();
var db = client.GetDatabase("publisher");
IGridFSBucket bucket = new GridFSBucket(db);
var filter = Builders<GridFSFileInfo>.Filter.Gte(x => x.Length , 600);
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
var options =new GridFSFindOptions()
            {
                Limit = 3,
                Sort = sort
            };
var cursor = bucket.Find(filter, options);
var list = cursor.ToList();
در اینجا ابتدا مشخص کرده‌ایم که فایل مورد نظر باید حجمی بیشتر از 600 بایت داشته باشد و مرتب سازی آن به صورت نزولی، بر اساس زمان آپلود باشد. در عبارت لامبدا تعریف شده، می‌توانید خصوصیت‌های مختلف یک فایل از قبیل نام، حجم (سایز) ، زمان آپلود و ... را ببینید. سپس مرتب سازی و تعداد رکورد برگشتی از ابتدای جدول را مشخص میکنیم و از متد Find یا FindAsync جهت جست‌وجو استفاده میکنیم. با شکل گرفتن کوئری درخواست، لیستی از آن را تهیه میکنیم. مقدار بازگشتی این شیء، در واقع اسنادی از تصاویر هستند که میتوانید از طریق Id یا نام آن‌ها، فایل اصلی را واکشی نمایید.
برای یافتن تصاویر بر اساس متادیتاهای تعریف شده، از کد زیر استفاده میکنیم:
var filter = Builders<GridFSFileInfo>.Filter.Eq("metadata.CoverType","Front");

تغییر نام تصاویر
جهت ویرایش یک نام فایل از طریق متدهای زیر اقدام می‌نماییم:
bucket.Rename(id, newFilename);

//یا در حالت غیرهمزمان
await bucket.RenameAsync(id, newFilename);

حذف تصویر
bucket.Delete(id);

//یا
await bucket.DeleteAsync(id);

برای حذف کل bucket از طریق کد زیر اقدام می‌نماییم:
bucket.Drop();

//یا
await bucket.DropAsync();
 
در بخش بعدی Chunk را مورد بررسی قرار می‌دهیم.
مطالب
استفاده از Twitter Bootstrap در کارهای روزمره طراحی وب
پس از آشنایی مقدماتی با Twitter Bootstrap، در این قسمت قصد داریم تا با ویژگی‌هایی از آن آشنا شویم که در کارهای رومزه طراحی وب بسیار مورد استفاده هستند؛ مانند تایپوگرافی، جداول، فرم‌ها، دکمه‌ها، تصاویر و آیکون‌ها.

تایپوگرافی
هدف از تایپوگرافی، چیدمان متن به نحوی است که واضح، خوانا و مشخص باشد؛ همچنین مباحث زیبایی ارائه را نیز به آن اضافه کنید. برای مثال تنظیم فاصله بین حروف و کلمات، فاصله بین خطوط و یا رعایت یک سری نسبت‌های ویژه مانند نسبت طلایی جهت دعوت خواننده به مطالعه مطالب، بجای فراری دادن او، در مباحث تایپوگرافی رعایت می‌شوند. خوشبختانه Twitter Bootstrap به همراه یک سری تنظیمات تایپوگرافی پیش فرض است که در ادامه آن‌ها را مرور خواهیم کرد.
پیش فرض‌های ابتدایی آن‌را مانند قلم با اندازه 14px، قلم پیش فرض Helvetica، فاصله بین خطوط و رنگ متن را در فایل bootstrap.css می‌توانید مشاهده کنید:
body {
    margin: 0;"Helvetica Neue", Helvetica, Arial, sans-serif;
    color: #333333;
    background-color: #ffffff;
}
در اینجا اثر تنظیمات bootstrap را بر روی تگ‌های h1 تا h6 ملاحظه می‌کنید:
 

 
        <div class="row-fluid">
            <div class="span12">
                <h1>
                    سرتیتر 1
                </h1>
                <h2>
                    سرتیتر 2
                </h2>
                <h3>
                    سرتیتر 3
                </h3>
                <h4>
                    سرتیتر 4
                </h4>
                <h5>
                    سرتیتر 5
                </h5>
                <h6>
                    سرتیتر 6
                </h6>
            </div>
        </div>

همچنین اگر نیاز باشد تا نسبت به متنی جلب توجه خواننده را جلب کرد می‌توان از کلاس lead استفاده نمود. به علاوه bootstrap پیش فرض‌هایی را به المان‌های small، strong و em نیز اعمال می‌کند:
        <div class="row-fluid">
            <div class="span12">
                <p class="lead">
                    تیتر</p>
                <p>
                    متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن
                    متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن
                    متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن
                    متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن
                </p>
                <small>اندازه کوچک</small> <strong>متن ضخیم</strong> <em>متن ایتالیک</em>
            </div>
        </div>

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

 
 
        <div class="row-fluid">
            <div class="span12">
                <p class="muted">
                    متن غیرفعال</p>
                <p class="text-warning">
                    نمایش اخطار به کاربر</p>
                <p class="text-error">
                    نمایش خطا به کاربر</p>
                <p class="text-info">
                    نمایش اطلاعات به کاربر</p>
                <p class="text-success">
                    نمایش موفقیت آمیز بودن عملیات</p>
            </div>
        </div>

bootstrap تنظیماتی را به المان abbreviation موجود در HTML 5 نیز اعمال می‌کند. در این حالت کاربر با نزدیک ساختن اشاره‌گر ماوس به متن مشخص شده، یک tooltip توضیح دهنده نیز ظاهر خواهد شد:

 
 
        <div class="row-fluid">
            <div class="span12">
                <p>
                    My
                    <abbr title="منظور واحد پردازش مرکزی است">
                        CPU</abbr>
                    has N Cores.
                </p>
            </div>
        </div>

در bootstrap المان آدرس نیز شیوه نامه خاص خودش را داشته و به صورت یک قطعه خاص ظاهر می‌شود:
 

        <div class="row-fluid">
            <div class="span12">
                <address>
                    وحید نصیری
                    <br />
                    ایران، تهران
                    <br />
                    <abbr title="Phone">
                        P:</abbr>
                    12345678
                    <br />
                    <abbr title="Cell">
                        C:</abbr>
                    12345678
                </address>
            </div>
        </div>

 
در اینجا تاثیر bootstrap را بر روی المان blockquote ملاحظه می‌کنید؛ همچنین به همراه المان  citeبرای ذکر ماخذ نقل قول ذکر شده:

 
        <div class="row-fluid">
            <div class="span12">
                <blockquote>
                    <p>
                        جهت نمایش نقل قول
                    </p>
                    <small><cite title="کاربر شماره 2">از شخصی</cite> </small>
                </blockquote>
            </div>
        </div>

 
در bootstrap برای حذف بولت‌های کنار یک لیست مرتب، فقط کافی است از کلاس unstyled استفاده شود:
 

        <div class="row-fluid">
            <div class="span6">
                <ul class="unstyled">
                    <li>یک </li>
                    <li>دو </li>
                    <li>سه </li>
                </ul>
            </div>
            <div class="span6">
                <ol>
                    <li>یک </li>
                    <li>دو </li>
                    <li>سه </li>
                </ol>
            </div>
        </div>

 
به علاوه امکان تعریف دو نوع خاص از definition list نیست وجود دارد (المان dl در اینجا). در حالت عادی، ابتدا عنوان مشخص شده با dt یا definition term به صورت ضخیم ظاهر می‌شود؛ به همراه توضیحات ارائه شده در المان dd آن در سطر بعدی. اگر از کلاس dl-horizontal استفاده شود، همانند تصویر ذیل، عنوان در کنار توضیحات در یک سطر قرار خواهد گرفت:
 

        <div class="row-fluid">
            <div class="span12">
                <dl class="dl-horizontal">
                    <dt>عنوان</dt>
                    <dd>
                        توضیحات بلند در اینجا</dd>
                </dl>
            </div>
        </div>

 
در bootstrap امکان اعمال شیوه نامه ابتدایی به کدهای برنامه‌ها نیز وجود دارد. اگر از تگ code استفاده شود، فرض بر این است که قرار است اطلاعاتی را در یک سطر نمایش دهید. اگر اطلاعات کدها، بیشتر از یک سطر است، می‌توان از تگ pre استفاده کرد. در اینجا با اعمال کلاس pre-scrollable به تگ pre، به صورت خودکار یک اسکرول عمودی به قطعه کدها اعمال خواهد شد:
 

 
        <div class="row-fluid">
            <div class="span6">
                <code>inline code</code>
            </div>
            <div class="span6">
                <pre class="pre-scrollable">
                code
                code
                code
                </pre>
            </div>
        </div>



استفاده از جداول و تاثیر bootstrap بر آن‌ها

در ادامه کدهای یک جدول متداول را که مزین شده‌است به کلاس‌های bootstrap ملاحظه می‌کنید:
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span12">
                <table 
class="table table-striped table-hover table-bordered table-condensed">
                    <caption>
                        عنوانی خاص در اینجا</caption>
                    <thead>
                        <tr>
                            <th>
                                ستون یک
                            </th>
                            <th>
                                ستون دو
                            </th>
                            <th>
                                ستون سه
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr class="error">
                            <td>
                                1 متن یک
                            </td>
                            <td>
                                1 متن دو
                            </td>
                            <td>
                                1 متن سه
                            </td>
                        </tr>
                        <tr>
                            <td>
                                2 متن یک
                            </td>
                            <td>
                                2 متن دو
                            </td>
                            <td>
                                2 متن سه
                            </td>
                        </tr>
                        <tr>
                            <td>
                                3 متن یک
                            </td>
                            <td>
                                3 متن دو
                            </td>
                            <td>
                                3 متن سه
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

در اینجا ذکر caption اختیاری است. وجود thead و tbody به تشخیص هدر و ردیف‌ها جهت اعمال شیوه‌نامه‌های متناظر کمک می‌کنند. همچنین کلاس‌های ذیل نیز به جدول اعمال شده‌اند:
table: سبب می‌شود تا تنظیمات ابتدایی bootstrap به جدول طراحی شده، اعمال شوند.
 table-striped: رنگ زمینه سطرها را یک در میان تغییر می‌دهد.
 table-hover: سبب می‌شود تا با عبور اشاره‌گر ماوس از روی سطرها، رنگ زمینه آن‌ها تغییر کنند.
 table-bordered: حاشیه‌ای را به جدول و ردیف‌ها اعمال می‌کنند. همچنین سبب نمایش گوشه‌های گرد نیز می‌شود.
 table-condensed: اندکی padding اعمال شده به سلول‌های جداول را کاهش می‌دهد و جدول را فشرده‌تر می‌کند.

در جدول فوق، کلاس نمونه error به یک tr نیز اعمال شده‌است تا اثر آن‌را بر روی یک ردیف بهتر بتوان ملاحظه کرد.



طراحی فرم‌ها و تاثیر bootstrap بر آن‌ها

قرار دادن برچسب‌ها و عناصر صفحه، به نحوی کاربرپسند و دلپذیر، نیاز به رعایت یک سری اصول تایپوگرافی و طراحی ویژه دارد که این موارد نیز در bootstrap گنجانده شده‌اند.

1) فرم‌های عمودی
 

    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span12">
                <form action="/signup" method="post">
                <fieldset>
                    <legend>ثبت نام</legend>
                    <label>
                        ایمیل:</label>
                    <input name="email" type="text" />
                    <label>
                        نام:</label>
                    <input name="name" type="text" />
                </fieldset>
                </form>
            </div>
        </div>
    </div>

 
در اینجا یک فرم ساده را مشاهده می‌کنید. بدون bootstrap، تمام عناصر این فرم در یک سطر نمایش داده می‌شوند. اما با صرفا فعال سازی css مرتبط با آن، به شکل مدرن فوق خواهیم رسید.
همانطور که ملاحظه می‌کنید، کلیه عناصر را به صورت یک پشته عمودی در صفحه قرار داده‌است و نکته مهم اینجا است که هیچگونه شیوه نامه خاص و اضافه‌تری به فرم فوق از طرف ما اعمال نشده است.

نکته: اگر می‌خواهید همان حالت پیش فرض مرورگر، یعنی قرار دادن تمام عناصر در پشت سر هم را فعال کنید، تنها کافی است کلاس form-inline را به form تعریف شده فوق اضافه نمائید.

2) نکاتی در مورد checkboxes و radio buttons
در حالت پیش فرض، با تعریف یک label و checkbox، برچسب متناظر با آن اندکی بالاتر از checkbox قرار گرفته شده در صفحه ظاهر می‌شود. برای رفع این مشکل تنها کافی است کلاس checkbox به label اعمال شود (و برای radio button از کلاس radio استفاده خواهد شد):
<label class="checkbox">
       <input type="checkbox" name="isMale" />
 مذکر</label>
این مثال را یکبار با کلاس checkbox و بار دیگر بدون آن آزمایش کنید تا تفاوت را بهتر بتوان درک کرد.

نکته: برای قرار دادن چندین checkbox یا radio button در یک سطر (با توجه به حالت چیدمان عمودی پیش فرض فرم‌ها)، ابتدا  آن‌ها را داخل یک div قرار دهید. سپس به تمام checkboxها یا radio buttonها کلاس inline را نیز اضافه نمائید. برای مثال:

 
 
                  <div>
                        <label class="radio inline">
                            <input type="radio" name="isMale" />
                            مذکر</label>
                        <label class="radio inline">
                            <input type="radio" name="isFemale" />
                            مؤنث</label>
                    </div>

 4) تعیین اندازه فیلدها

با استفاده از کلاس‌هایی مانند input-mini، input-small، input-medium، input-large، input-xlarge و input-xxlarge می‌توان اندازه فیلدها را کوچک‌تر از اندازه معمول یا بزرگتر کرد. یا حتی می‌توان از همان کلاس‌های span مانند span12 (اختصاص کل عرض به یک فیلد) و امثال آن برای تعیین اندازه یک فیلد استفاده کرد.
اگر علاقمند هستید که یک textarea کل عرض صفحه را به خود اختصاص دهد، از کلاس input-block-level استفاده کنید.

5) فرم‌های جستجو

 
 
       <form class="search-form">
                <fieldset>
                    <legend>جستجو</legend>
                    <input type="search" class="search-query" />
                    <button type="submit" class="btn" >بیاب</button>
                </fieldset>
       </form>

چند نکته در فرم‌های جستجوی طراحی شده با bootstrap نسبت به بقیه فرم‌ها حائز اهمیت است:
- کلاس فرم بهتر است search-form تعیین شود
- نوع input بهتر است search وارد شود
- کلاس input جستجو نیز search-query انتخاب گردد

همانطور که ملاحظه می‌کنید، در این حالت گوشه‌های جعبه متنی جستجو، نسبت به حالت‌های معمولی آن‌ها گرد شده است. کلاس دکمه نیز btn درنظر گرفته شده است تا حالت ویژه دکمه‌های bootstrap را پیدا کند.

6) فرم‌های افقی

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

 
                <form class="form-horizontal" action="/signup" method="post">
                <fieldset>
                    <legend>ثبت نام</legend>
                    <div class="control-group">
                        <label class="control-label">
                            ایمیل:</label>
                        <div class="controls">
                            <input name="email" type="text" />
                        </div>
                    </div>
                    <div class="control-group">
                        <label class="control-label">
                            نام:</label>
                        <div class="controls">
                            <input name="name" type="text" />
                        </div>
                    </div>
                    <div class="control-group">
                        <div class="controls">
                            <label class="radio inline">
                                <input type="radio" name="isMale" />
                                مذکر</label>
                            <label class="radio inline">
                                <input type="radio" name="isFemale" />
                                مؤنث</label>
                        </div>
                    </div>
                </fieldset>
                </form>

مطابق مثال فوق، خلاصه طراحی فرم‌های افقی با Bootstrap به این نحو است:
- کلاس form-horizontal را به فرم جاری اضافه کنید.
- هر سطر مورد نظر را در div ایی با کلاس control-group محصور نمائید.
- به برچسب‌ها، کلاس control-label را انتساب دهید.
- کنترل‌های مدنظر را در div ایی با کلاس controls محصور کنید.

هر چند این روش نیاز به اندکی HTML نویسی دارد، اما بسیاری به این نوع فرم‌ها بیشتر علاقمند هستند تا فرم‌های عمودی ابتدای بحث.

7) جلب توجه کاربران به فیلدها برای نمایش خطاهای اعتبارسنجی
 

<div class="control-group error">
   <label class="control-label">
                            ایمیل:</label>
    <div class="controls">
           <input name="email" type="text" />
           <span class="help-block">لطفا ایمیل را با فرمت صحیحی وارد نمائید</span>
     </div>
</div>

 
برای تزریق خطاهای اعتبارسنجی ویژه، در اینجا می‌توان از کلاس‌های help-block و یا help-inline به همراه کلاس‌هایی مانند error، info، warning و success استفاده کرد.

8) توسعه فیلدهای استاندارد
 

                <div class="input-prepend input-append">
                    <span dir="ltr" class="add-on">.00</span>
                    <input dir="ltr" type="text" />
                    <span class="add-on">ریال</span>
                </div>

 
توسط ترکیب کلاس‌های input-prepend، input-append و spanهایی با کلاس add-on، می‌توان عناصری بصری خاصی را به عنوان جزئی از فیلد مورد نظر اضافه کرد.

9) HTML Helpers مخصوص ASP.NET MVC برای کار با bootstrap

نکاتی را که در اینجا مطرح شدند، اگر علاقمند بودید که به شکلی strongly typed در ASP.NET MVC اعمال کنید، می‌توان به پروژه‌هایی مانند TwitterBootstrapMvc مراجعه کرد. تعداد این نوع پروژه‌ها هم روز به روز بیشتر می‌شوند:
https://twitterbootstrapmvc.codeplex.com/
https://mvc4bootstaphelper.codeplex.com/
https://github.com/erichexter/twitter.bootstrap.mvc
http://bootstraphelpers.codeplex.com/



تنظیمات خاص دکمه‌ها در حین استفاده از Twitter Bootstrap

در مثال ذیل، کلاس‌های مرتبط با تزئین دکمه‌ها را توسط bootstrap، ملاحظه می‌کنید:
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span12">
                <table
 class="table table-striped table-hover table-bordered table-condensed">
                    <thead>
                        <tr>
                            <th>
                                دکمه
                            </th>
                            <th>
                                لینک
                            </th>
                            <th>
                                کلاس بکار گرفته شده
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>
                                <button class="btn">
                                    default</button>
                            </td>
                            <td>
                                <a href="#" class="btn">default</a>
                            </td>
                            <td>
                                <code>btn</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary">
                                    primary</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary">primary</a>
                            </td>
                            <td>
                                <code>btn btn-primary</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-info">
                                    info</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-info">info</a>
                            </td>
                            <td>
                                <code>btn btn-info</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-success">
                                    success</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-success">success</a>
                            </td>
                            <td>
                                <code>btn btn-success</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-warning">
                                    warning</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-warning">warning</a>
                            </td>
                            <td>
                                <code>btn btn-warning</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-danger">
                                    danger</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-danger">danger</a>
                            </td>
                            <td>
                                <code>btn btn-danger</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-inverse">
                                    inverse</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-inverse">inverse</a>
                            </td>
                            <td>
                                <code>btn btn-inverse</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-link">
                                    link</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-link">link</a>
                            </td>
                            <td>
                                <code>btn btn-link</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary btn-large">
                                    large</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary btn-large">large</a>
                            </td>
                            <td>
                                <code>btn btn-primary btn-large</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary btn-small">
                                    small</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary btn-small">small</a>
                            </td>
                            <td>
                                <code>btn btn-primary btn-small</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary btn-mini">
                                    mini</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary btn-mini">mini</a>
                            </td>
                            <td>
                                <code>btn btn-primary btn-mini</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary btn-block">
                                    block</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary btn-block">block</a>
                            </td>
                            <td>
                                <code>btn btn-primary btn-block</code>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <button class="btn btn-primary disabled">
                                    disabled</button>
                            </td>
                            <td>
                                <a href="#" class="btn btn-primary disabled">disabled</a>
                            </td>
                            <td>
                                <code>btn btn-primary disabled</code>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

همانطور که ملاحظه کردید، الزامی ندارد که این کلاس‌ها را حتما به دکمه‌ها اعمال کرد. برای نمونه می‌توان از یک span یا لینک نیز برای تعریف دکمه‌ها بهره جست. برای یکپارچه سازی چنین دکمه‌هایی (که در اصل دکمه نیستند) با ASP.NET MVC می‌توان به مطلب تکمیلی «استفاده از دکمه‌های CSS توئیتر در ASP.NET MVC» مراجعه نمود.

یک نکته: اگر علاقمند هستید که تعدادی دکمه را به شکل یک toolbar نمایش دهید، آن‌ها را در یک div محصور کرده و کلاس btn-group را به آن div اعمال نمائید.


کار با تصاویر و آیکون‌ها در Twitter Bootstrap

کلاس‌هایی مانند img-rounded، img-circle، img-polaroid با اعمال به یک تصویر، سبب گردن شدن گوشه‌های آن، نمایش دایره‌ای و یا نمایش به همراه حاشیه یک تصویر خواهند شد.
Twitter Bootstrap به همراه صدها آیکون ارائه شده است. این آیکون‌ها توسط glyphicons.com ایجاد شده‌اند. روشی که برای استفاده از آن‌ها توصیه شده است، استفاده از تگ i می‌باشد. برای مثال:
<i class="icon-music"></i>icon-music
البته بدیهی است که محدودیتی در اینجا وجود نداشته و می‌توان این کلاس‌ها را به یک span نیز اعمال کرد.
برای مشاهده لیست کلاس‌های قابل استفاده، کلمه icon-glass را در فایل bootstrap.css جستجو نمائید؛ تا شروع مدخل مرتبط با آیکون‌ها را بتوانید مشاهده نمائید.
رنگ پیش فرض این آیکون‌ها مشکی است. اگر علاقمند بودید که آن‌ها را برای مثال با رنگ سفید نمایش دهید فقط کافی است کلاس icon-white را پس از کلاس آیکون مدنظر،ذکر کرد:
<i class="icon-music icon-white"></i>icon-music
از این نمونه قلم‌های سازگار با Twitter Bootstrap در آدرس fontawesome.github.com نیز قابل دریافت هستند.
امکان اعمال این آیکون‌ها به دکمه‌ها نیز وجود دارد. برای مثال:
    <button class="btn">
        <i class="icon-music"></i>دکمه</button>
کاربرد دیگر این آیکون‌ها در بحث توسعه فیلدهای استاندارد است که پیشتر بحث شد. برای مثال نمایش آیکون نامه در کنار فیلد دریافت ایمیل:
 

 
    <div class="input-prepend">
        <span class="add-on"><i class="icon-envelope"></i></span>
        <input type="email" dir="ltr" />
    </div>


مطالب
آموزش WAF (بررسی ساختار همراه با پیاده سازی یک مثال)
در این پست با مفاهیم اولیه این کتابخانه آشنا شدید. برای بررسی و پیاده سازی مثال، ابتدا یک Blank Solution را ایجاد نمایید. فرض کنید قصد پیاده سازی یک پروژه بزرگ ماژولار را داریم. برای این کار لازم است مراحل زیر را برای طراحی ساختار مناسب پروژه دنبال نمایید.
نکته: آشنایی اولیه با مفاهیم MEF از ملزومات این بخش است.
»ابتدا یک Class Library به نام Views ایجاد نمایید و اینترفیس زیر را به صورت زیر در آن تعریف نمایید. این اینترفیس رابط بین کنترلر و View از طریق ViewModel خواهد بود.
 public interface IBookView : IView
    {
        void Show();
        void Close();
    }
اینترفیس IView در مسیر System.Waf.Applications قرار دارد. در نتیجه از طریق nuget اقدام به نصب Package  زیر نمایید:
Install-Package WAF
»حال در Solution ساخته شده  یک پروژه از نوع WPF Application به نام Shell ایجاد کنید. با استفاده از نیوگت، Waf Package را نصب نمایید؛ سپس ارجاعی از اسمبلی Views را به آن ایجاد کنید. output type اسمبلی Shell را به نوع ClassLibrary تغییر داده، همچنین فایل‌های موجود در آن را حذف نمایید. یک فایل Xaml جدید را به نام BookShell ایجاد نمایید و کد‌های زیر را در آن کپی نمایید:
<Window x:Class="Shell.BookShell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Book View" Height="350" Width="525">
    <Grid>
        <DataGrid ItemsSource="{Binding Books}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="400" Height="200">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Code" Binding="{Binding Code}" Width="100"></DataGridTextColumn>
                <DataGridTextColumn Header="Title" Binding="{Binding Title}" Width="300"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>

    </Grid>
</Window>
این فرم فقط شامل یک دیتاگرید برای نمایش اطلاعات کتاب‌هاست. دیتای آن از طریق ViewModel تامین خواهد شد، در نتیجه ItemsSource آن به خاصیتی به نام Books بایند شده است. حال ارجاعی به اسمبلی System.ComponentModel.Composition دهید. سپس در Code behind این فرم کد‌های زیر را کپی کنید:
[Export(typeof(IBookView))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public partial class BookShell : Window, IBookView
    {
        public BookShell()
        {
            InitializeComponent();
        }
    }
کاملا واضح است که این فرم اینترفیس IBookView را پیاده سازی کرده است. از آنجاکه کلاس Window به صورت پیش فرض دارای متد‌های Show و Close است در نتیجه نیازی به پیاده سازی مجدد متدهای IBookView نیست. دستور Export باعث می‌شود که این کلاس به عنوان وابستگی به Composition Container اضافه شود تا در جای مناسب بتوان از آن وهله سازی کرد. نکته‌ی مهم این است که به دلیل آنکه این کلاس، اینترفیس IBookView را پیاده سازی کرده است در نتیجه نوع Export این کلاس حتما باید به صورت صریح از نوع IBookView باشد.

»یک Class Library به نام Models بسازید و بعد از ایجاد آن، کلاس زیر را به عنوان مدل Book در آن کپی کنید:
 public class Book
    {
        public int Code { get; set; }

        public string Title { get; set; }
    }
»یک  Class Library دیگر به نام ViewModels ایجاد کنید و همانند مراحل قبلی، Package مربوط به WAF را نصب کنید. سپس کلاسی به نام BookViewModel ایجاد نمایید و کدهای زیر را در آن کپی کنید (ارجاع به اسمبلی‌های Views و Models را فراموش نکنید):
[Export]
    [Export(typeof(ViewModel<IBookView>))]
    public class BookViewModel : ViewModel<IBookView>
    {
        [ImportingConstructor]
        public BookViewModel(IBookView view)
            : base(view)
        {
        }
       
        public ObservableCollection<Book> Books { get; set; }
        
    }
ViewModel مورد نظر از کلاس ViewModel of T ارث برده است. نوع این کلاس معادل نوع View مورد نظر ماست که در اینجا مقصود IBookView است. این کلاس شامل خاصیتی به نام ViewCore است که امکان فراخوانی متد‌ها و خاصیت‌های View را فراهم می‌نماید. وظیفه اصلی کلاس پایه ViewModel، وهله سازی از View سپس ست کردن خاصیت DataContext در View مورد نظر به نمونه وهله سازی شده از ViewModel است. در نتیجه عملیات مقید سازی در Shell به درستی انجام خواهدشد.
به دلیل اینکه سازنده پیش فرض در  این کلاس وجود ندارد حتما باید از ImportingConstructor استفاده نماییم تا CompositionContainer در هنگام عملیات وهله سازی Exception صادر نکند.

»بخش بعدی ساخت یک Class Library دیگر به نام Controllers است. در این Library نیز بعد از ارجاع به اسمبلی‌های زیر کتابخانه WAF را نصب نمایید. 
  • Views
  • Models
  • ViewModels
  • System.ComponentModel.Composition
کلاسی به نام BookController بسازید و کد‌های زیر را در آن کپی نمایید:
[Export]
    public class BookController
    {
        [ImportingConstructor]
        public BookController(BookViewModel viewModel)
        {
            ViewModelCore = viewModel;
        }

        public BookViewModel ViewModelCore
        {
            get;
            private set;
        }

        public void Run()
        {
            var result = new List<Book>();
            result.Add(new Book { Code = 1, Title = "Book1" });
            result.Add(new Book { Code = 2, Title = "Book2" });
            result.Add(new Book { Code = 3, Title = "Book3" });

            ViewModelCore.Books = new ObservableCollection<Models.Book>(result);

            (ViewModelCore.View as IBookView).Show();
        }
    }
نکته مهم این کلاس این است که BookViewModel به عنوان وابستگی این کنترلر تعریف شده است. در نتیجه در هنگام وهله سازی از این کنترلر Container مورد نظر یک وهله از BookViewModel را در اختیار آن قرار خواهد داد. در متد Run نیز ابتدا مقدار Book که به ItemsSource دیتا گرید در BookShell مقید شده است مقدار خواهد گرفت. سپس با فراخوانی متد Show از اینترفیس IBookView، متد Show در BookShell فراخوانی خواهد شد که نتیجه آن نمایش فرم مورد نظر است.

طراحی Bootstrapper

در پروژه‌های ماژولار  Bootstrapper از ملزومات جدانشدنی این گونه پروژه هاست. برای این کار ابتدا یک WPF Application دیگر به نام Bootstrapper ایجاد نماید. سپس ارجاعی به اسمبلی‌های زیر را در آن قرار دهید:
»Controllers
»Views
»ViewModels
»Shell
»System.ComponentModel.Composition
»نصب بسته WAF با استفاده از nuget

حال یک کلاس به نام  AppBootstrapper ایجاد نمایید و کد‌های زیر را در آن کپی نمایید:
public class AppBootstrapper
    {
        public CompositionContainer Container
        {
            get;
            private set;
        }

        public AggregateCatalog Catalog
        {
            get;
            private set;
        }

        public void Run()
        {
            Catalog = new AggregateCatalog();
            Catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            
            Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "Shell.dll")));
            Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "ViewModels.dll")));
            Catalog.Catalogs.Add(new AssemblyCatalog(String.Format("{0}\\{1}", Environment.CurrentDirectory, "Controllers.dll")));

            Container = new CompositionContainer(Catalog);

            var batch = new CompositionBatch();
            batch.AddExportedValue(Container);
            Container.Compose(batch);

            var bookController = Container.GetExportedValue<BookController>();
            bookController.Run();


        }
    }
اگر با MEF آشنا باشید کد‌های بالا نیز برای شما مفهوم مشخصی دارند. در متد Run این کلاس ابتدا Catalog ساخته می‌شود. سپس با اسکن اسمبلی‌های مورد نظر تمام Export‌ها و Import‌های لازم واکشی شده و به Conrtainer مورد نظر رجیستر می‌شوند. در انتها نیز با وهله سازی از BookController و فراخوانی متد Run آن خروجی زیر نمایان خواهد شد.

نکته بخش Startup  را از فایل App.Xaml خذف نمایید و در متد Startup این فایل کد زیر را کپی کنید:

public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            new Bootstrapper.AppBootstrapper().Run();
        }
    }
در پایان، ساختار پروژه به صورت زیر خواهد شد:

نکته: می‌توان بخش اسکن اسمبلی‌ها را توسط یک DirecotryCatalog به صورت زیر خلاصه کرد:
Catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory));
در این صورت تمام اسمبلی‌های موجود در این مسیر اسکن خواهند شد.
نکته: می‌توان به جای جداسازی فیزیکی لایه‌ها آن‌ها را از طریق Directory‌ها به صورت منطقی در قالب یک اسمبلی نیز مدیریت کرد.
نکته: بهتر است به جای رفرنس مستقیم اسمبلی‌ها به Bootstrapper با استفاده از Pre post build در قسمت  Build Event، اسمبلی‌های مورد نظر را در یک مسیر Build کپی نمایید که روش آن به تفصیل در این پست و این پست شرح داده شده است.
دانلود سورس پروژه
مطالب
نمایش پیغامی به کاربر در هنگام استفاده از MS Ajax

مطلب "نمایش یک پیغام به کاربر در ASP.Net" را احتمالا به خاطر دارید. این روش در حالت postback کامل و یا در حالت page_load بدون مشکل کار می‌کند. اما اگر در حال استفاده از آپدیت پنل MS Ajax باشیم، دیگر کار نخواهد کرد چون تنها قسمتی از صفحه به روز رسانی می‌شود و یک postback کامل رخ نمی‌دهد تا از شیء صفحه جهت تزریق اسکریپت خود بتوانیم استفاده نمائیم. خوشبختانه طراحان MS Ajax به این مورد هم توجه داشته‌اند و متد RegisterStartupScript را جزئی از کلاس ScriptManager قرار داده‌اند. برای مثال اگر نیاز باشد در پایان عملیات یک متد که از آپدیت پنل کمک می‌گیرد، یک alert نمایش داده شود می‌توان از متد زیر استفاده کرد:

void jsCall(string alert)
{
string script = @"alert('" + alert + "');";
ScriptManager.RegisterStartupScript(this, this.GetType(), "jsCall", script, true);
}

با توجه به این نکته، مطلب "نمایش یک پیغام به کاربر در ASP.Net" را به صورت زیر نیز می‌توان با MS Ajax‌ سازگار کرد:

if (page != null)
{
ScriptManager sMgr = ScriptManager.GetCurrent(page);
if (sMgr != null)
{
ScriptManager.RegisterStartupScript(page, page.GetType(),
"script" + new Guid().ToString("N"),
scriptBlock, false);
}
else
//همان روش قبلی
...


مطالب
سیستم‌های توزیع شده در NET. - بخش هفتم- معرفی Apache Kafka
سرچشمه Kafka از LinkedIn آغاز و سپس در سال 2011 توسط Apache بصورت open source ارائه شد. هدف آن ارائه یک بستر جریان داده‌ای توزیع شده‌است که اساس آن، Publish-Subscribe می‌باشد . سادگی اضافه کردن قابلیت‌های مقیاس پذیری افقی، تحمل خطا و افزایش کارآیی توسط این بستر باعث شده‌است که هزاران شرکت از آن بعنوان بستر ارتباطی قسمتهای مختلف سیستمها و زیرسیستمهای خود استفاده کنند.
همانطور که گفته شد وظیفه و هدف اصلی Apache Kafka، ارائه یک بستر برای مدیریت و کنترل جریان‌های اطلاعاتی با کارآیی بسیار بالا، در سیستم‌ها و زیرسیستمهای مختلف است. یعنی شما می‌توانید با ایجاد کردن یک Pipeline برای جریان اطلاعات خود، وابستگی مستقیم سیستمها و زیرسیستمها را از بین ببرید؛ آن هم بصورتی که بروز مشکلی در هر قسمت، کمترین میزان تاثیر را در سایر قسمتها داشته باشد.
فرض کنید شما تعداد زیادی سیستم و زیرسیستم مختلف را داشته باشید که هر کدام از آنها نیازمند ارتباط با برخی از قسمتهای دیگر است. در این صورت شما دو راه دارید: اول اینکه در هر قسمت سرویس‌هایی را برای ارتباط با سایر قسمت‌ها پیاده سازی کنید و هر قسمت بصورت مستقیم با سایر قسمتها در ارتباط باشد.

مشخصا کنترل و مدیریت جریان اطلاعاتی در این پیاده سازی کار بسیار دشواری است. تغییر هر قسمت، تاثیر مستقیمی بر روی سایر قسمتها دارد و در صورتی که هریک از قسمتها با مشکلی روبرو شوند، سایر قسمتهای مرتبط نیز با مشکل روبرو می‌شوند. این مشکل زمانی بسیار نمایان می‌شود که در معماری‌هایی مانند میکروسرویس، بدلیل بالا رفتن تعداد زیرسیستم‌ها و ارتباطات آنها، مدیریت این ارتباطات کار بسیار دشوار، پرهزینه و پیچیده‌ای می‌شود.
روش Apache Kafka برای رفع مشکل فوق به این صورت است که Kafka با بر عهده گرفتن مدیریت ارتباطات و جریان داده‌ای قسمتهای مختلف، به شما کمک می‌کند تا تیم پیاده سازی، تنها تمرکزشان را بر روی Businessی که می‌خواهند پیاده سازی کنند، قرار دهند. با این روش می‌توانیم به راحتی سیستمهایی را پیاده سازی کنیم که از نظر ارتباطی در حالت معمول، پیچیده یا بسیار پیچیده‌اند.

همانطور که می‌بینید دیگر نیازی نیست تا قسمتهای مختلف بصورت مستقیم با یکدیگر در ارتباط باشند؛ تمامی ارتباطات از طریق Kafka انجام می‌شود. تغییر یک قسمت، تاثیر زیادی بر روی سایر قسمتها ندارد. از دسترس خارج شدن یا بروز هر گونه مشکلی در یک قسمت، بر روی کل سیستم تاثیر زیادی ندارد. پیامهای مربوط به یک قسمت تا زمانی که پردازش نشده‌اند از بین نمی‌روند. پس سیستمها می‌توانند در حالت Offline نیز به کار خود ادامه دهند. شما می‌توانید  در این روش تمامی قسمتهای  سیستم را بصورت یک Cluster پیاده سازی کنید. بنابراین احتمال از دسترس خارج شدن هر قسمت به کمترین میزان می‌رسد. اما حتی درصورتی که یک قسمت بصورت موقت از دسترس خارج شود، پیامهای مرتبط با آن قسمت تا زمانی که دوباره به جریان پردازش بازگردد، از بین نمی‌روند. پس از اضافه شدن قسمت از دسترس خارج شده، بلافاصله تمامی پیامهای مرتبط با آن قسمت برایش ارسال می‌شوند. برای بالا رفتن میزان کارآیی و تحمل خطا، به راحتی می‌توانید خود Kafka را نیز بصورت یک Cluster پیاده سازی کنید و با بالا رفتن تعداد درخواست، در صورت نیاز می‌توانید عملیات مقیاس پذیری افقی را به راحت‌ترین روش ممکن انجام دهید.

نمایی از معماری کلی Apache Kafka: 


برای شروع به آموزش Apache Kafka بهتر است ابتدا با مفاهیم و اصطلاحات آن آشنا شویم:

Producer:
  ارسال کننده پیام. Application، سیستم یا زیرسیستمی که عملیات Publish پیام را برای Topic خاص از Kafka Server انجام می‌دهد.

Consumer:
دریافت کننده پیام. Application، سیستم یا زیرسیستمی که بر روی یک یا چند Topic خاص، Subscribe کرده‌است (همچنین هر Consumer می‌تواند روی یک یا چند Partition از یک Topic خاص نیز Subscribe کند).

Consumer Group:
 گروهی از Consumer‌ها می‌باشند که با یک group.id، مشخص شده‌اند. عموما این گروه شامل یک Replicate از یک Application است؛ مانند گروه ارسال کننده ایمیل (یک زیر سیستم ارسال کننده ایمیل که چندین بار در سرور‌های مختلف اجرا شده است). Kafka این ضمانت را به ما می‌دهد که هر پیام ذخیره شده در یک Topic، برای تمامی Consumer Group‌های مرتبط ارسال شود؛ اما در هر Consumer Group، تنها یک دریافت کننده داشته باشد. یعنی هر پیام در هر Consumer Group، تنها توسط یک Consumer دریافت می‌شود.

Broker :
 قسمتی که تمامی پیامها را  از Producer دریافت می‌کند، سپس آن‌ها را در Log مربوط به Topic مشخص شده ذخیره می‌کند و پس از آن، پیام ذخیره شده را برای تمامی Consumerهای مرتبط ارسال می‌کند.

Topic: 
یک دسته بندی برای ذخیره کردن پیامهای Publish شده می‌باشد. Topicها همانند مفهوم Tableها در SQL Server می‌باشند. همانطور که می‌دانید هر Table از قبل تعریف شده‌است. یک کاربر با ارسال یک درخواست ثبت، داده‌ها را در آن ذخیره می‌کند و سپس گروهی از کاربران از داده‌های ثبت شده استفاده می‌کنند. در مفهموم Topic نیز ابتدا ما Topic مورد نظر را با خصوصیاتی که باید داشته باشد تعریف می‌کنیم (البته می‌توان بصورت Dynamic نیز آن را تعریف کرد؛ اما این روش توصیه نمی‌شود). سپس Producer پیام مربوطه را به همراه نام Topic برای Broker ارسال می‌کند. Broker پیام را در Partition مربوطه از Topic ذخیره می‌کند و سپس پیام برای تمامی Consumer‌های مربوطه ارسال می‌شود.

Partition:
یکی از تفاوتهای بسیار مهم Kafka با سایر Message broker‌ها مانند RabitMQ که باعث بالارفتن کارآیی آن نیز شده‌است، قابلیت Partition در Topic‌ها می‌باشد. در واقع هر Topic از یک یا چندین Partition برای ذخیره داده‌ها استفاده می‌کند. تعریف درست تعداد Partition‌ها در یک Topic، تاثیر مستقیمی بر درجه همزمانی و کارآیی در آن Topic و کل سیستم دارد. در Kafka تمامی پیامها به همان ترتیبی که وارد شده‌اند، در Partition‌های یک Topic ذخیره می‌شوند و به همان ترتیب نیز برای Consumer‌ها ارسال می‌شوند.
بطور مثال فرض کنید تعداد Partition‌های یک Topic با نام DepartmentMessage یک می‌باشد (از این Topic برای ذخیره پیامهای واحدهای مختلف یک سازمان استفاده می‌شود). در این صورت تمامی پیامهای دریافتی تنها در یک Partition ذخیره می‌شوند.

هر خانه از یک Partition، توسط یک شناسه از نوع int و با نام offset در دسترس است. تمامی پیامهای جدید ارسالی توسط Producer با offset ی بزرگتر از offset موجود در این Partition ذخیره می‌شوند؛ یعنی در انتهای آن قرار می‌گیرند. در مثال فوق در صورت دریافت پیام جدید، offset آن با عدد 10 مقداردهی می‌شود. همچنین عملیات خواندن نیز از کوچکترین offsetی که هنوز  مقدار آن توسط Consumer‌ها خوانده نشده‌است، انجام می‌شود. همانطور که مشخص است، بدلیل اینکه تعداد Partitionهای این مثال عدد یک می‌باشد، تمامی درخواست‌های Producer‌ها در یک Partition قرار می‌گیرند و تمامی Consumer‌ها نیز از طریق یک Partition به پیامها دسترسی دارند؛ یعنی در صورت بالا بردن تعداد Producer‌ها یا Consumer‌ها، کارآیی بالا نمی‌رود. البته با اینکه کنترل مقدار اولیه offset برای شروع یک Consumer به دست خود Consumer و Zookeeper است، اما در اکثر موارد تمامی Consumer‌های یک Topic باید از یک نقطه، شروع به خواندن داده‌ها کنند. در این حالت تا زمانیکه پیام با offset 1، توسط Consumerی خوانده نشود، هیچ Consumerی نمی‌تواند پیام شماره 2 را بخواند. استفاده کردن از یک Partition بیشتر زمانی کاربرد دارد که بخواهید تمامی پیامهایتان، واقعا در یک صف قرار بگیرند.
حال فرض کنید در سازمان شما سه واحد اداری، مالی و آموزش وجود دارد. در این صورت بدلیل اینکه تمامی پیامها در یک Partition ذخیره می‌شوند، تا زمانی که یک واحد تمامی پیامهای مرتبط با خود را از ابتدای Partition نخوانده‌است، دیگر واحدها نمی‌توانند به پیامهای مرتبط با خود دسترسی داشته باشند. پس در این صورت ما می‌توانیم تعداد Partition‌های این Topic را عدد 3 درنظر بگیریم؛ بصورتی که پیامهای مرتبط با هر واحد در یک Partition جدا قرار بگیرد.

در این روش هر Producer زمانیکه پیامی را برای این Topic ارسال می‌کند، یک Key نیز برای آن مشخص می‌کند و این Key نشان دهنده این است که پیام جدید باید در کدام Partition ذخیره شود. یعنی بصورت همزمان می‌توانید در هر سه Partition، پیامهایتان را ذخیره کنید؛ بصورتی که بطور مثال تمامی پیامهای مربوط به واحد اداری، در Partition 0  و تمامی پیامهای مربوط به واحد مالی، در Partition 1 و واحد آموزش، در Partition 2 ذخیره شوند و همچنین عملیات خواندن از این Topic نیز می‌تواند بصورت همزمان در واحدهای مختلف انجام شود.
باید در تعریف تعداد Partition‌های یک Topic این نکته را در نظر بگیرید که این تعداد کاملا به نیازمندی شما و کارآیی که شما مد نظر دارید، بستگی دارد. تعداد این Partition‌ها حتی می‌تواند به تعداد User‌های یک سیستم نیز تعریف شود. علاوه بر آن باید بدانید که هر Partition در هر زمان تنها توسط یک Primary Broker می‌تواند در دسترس سایر قسمتها قرار بگیرد و تمامی عملیات خواندن و نوشتن در Partition توسط این Kafka Server انجام می‌شود و در صورتیکه به هر دلیلی این سرور از دسترس خارج شود، مدیریت این Partition به سرور‌های دیگر داده می‌شود.

Cluster:
مجموعه ای از Brokerها می‌باشد که بصورت یک Cluster اجرا شده‌اند. این کار باعث بالا رفتن کارآیی و تحمل خطا می‌شود.

Primary Broker:
یک Kafka Server که مسئول خواندن و نوشتن در یک Partition است. در یک Cluster هر Partition در یک زمان تنها یک Primary Broker دارد. این Primary Broker همزمان می‌تواند برای Partition‌های دیگر نقش Replicas Broker را بازی کند. انتخاب یک Primary Broker برای یک Partition توسط ZooKeeper انجام می‌شود.

Replicas Brokers :
Kafka Serverهایی که شامل یک کپی از Partition می‌باشند. عملیات خواندن و نوشتن در Partition توسط Primary انجام می‌شود. در صورتیکه Primary از دسترس خارج شود، ZooKeeper یکی از Replicas Broker‌ها را بعنوان Primary در نظر می‌گیرد. همچنین این نکته را باید در نظر بگیرید که هر Replicate همزمان می‌تواند Primary پارتیشن‌های دیگر باشد.

Replication Factor :
این خصوصیت احتمال از دست دادن داده‌های یک Topic را به حداقل می‌رساند؛ به این صورت که هر پیام از یک Topic، در چندین سرور مختلف که تعداد آنها توسط این خصوصیت مشخص می‌شود، نگهداری می‌شود.

Apache ZooKeeper :
Kafka هیچ Stateی را نگه نمی‌دارد (اصطلاحا stateless می‌باشد). برای ذخیره کردن و مدیریت تمامی Stateها از جمله اینکه در حال حاضر Primary Broker برای یک Partition چه سروری است، یا اینکه پیامهای یک Partition تا کدام offset توسط Consumer‌ها خوانده شده‌اند یا اینکه کدام Consumer در حال حاضر در یک Consumer Group مسئول یک Partition می‌باشد، توسط Apache Zookeeper انجام می‌شود.

ضمانت‌هایی که Kafka می‌دهد:
  1. تمامی پیامهای دریافتی در یک Partition از یک Topic، به همان ترتیبی که دریافت می‌شوند ذخیره می‌شوند.
  2. Consumer‌ها تمامی پیامها را در یک Partition به همان ترتیبی که ذخیره شده‌اند، دریافت می‌کنند.
  3. در یک Topic با Replication Factorی با مقدار N، درجه تحمل خطا N - 1 می‌باشد.

تا اینجا با اهداف، مفاهیم و اصطلاحات Apache Kafka آشنا شدیم. در بخش بعد به راه اندازی قسمتهای مختلف آن در Ubuntu می‌پردازیم و می‌بینیم که به چه صورت می‌توان به راحتی یک Cluster از سرورهای Kafka را ایجاد کرد.
اشتراک‌ها
معرفی DevDocs
DevDocs مستندات API‌های متعددی را در یک اینترفیس سریع، منظم و قابل جستجو تلفیق می‌کند. چیزهایی که باید پیش از شروع بدانید:

  1. برای انتخاب مستندات خود Select documentation را در گوشه سمت چپ کلیک کنید
  2. لازم نیست حتما از ماوس خود استفاده کنید. به لیست کلیدهای میانبر مراجعه شود
  3. جستجوی سایت از fuzzy searching پشتیبانی می‌کند. مثلا جستجوی عبارت "bgcp" مطالب "background-clip" را جستجو می‌کند
  4. برای اینکه در مستندات خاصی جستجو کنید، نام یا مخفف آن را وارد کرده سپس Tab را فشار دهید
  5. برای جستجو از مرورگر خود هم می‌توانید استفاده کنید. پروتکل Open Search پشتیبانی می‌شود
  6. از طریق موبایل هم می‌توانید به این سایت دسترسی داشته باشید. افزونه ای هم برای کروم وجود دارد
  7. برای دریافت خبرنامه‌ها یا عضو شوید یا @DevDocs را در توییتر دنبال کنید
  8. DevDocs رایگان و متن باز است
برخی از مستندات موجود در DevDocs:
  • CSS
  • DOM
  • DOM Events
  • HTML
  • HTTP
  • JavaScript
همین! از برنامه نویسی لذت ببرید :)
معرفی DevDocs
مطالب
انقیاد داده‌ها در WPF (بخش اول)
در این مقاله مفاهیم مختلفی را در ارتباط با DataBinding بررسی خواهیم کرد:
• One Way Binding بخش اول
• INPC  بخش اول
• Tow Way Binding بخش اول
• List Binding  بخش دوم
• Element Binding بخش دوم
• Data Conversion بخش دوم
در ابتدا مفهوم انقیاد داده‌ها یا همان DataBinding  را مرور می‌کنیم. به فرآیند مرتبط سازی منابع اطلاعاتی به کنترل‌ها در برنامه‌ها یا به بیان امروزی‌تر، به View‌ها و نمایش اطلاعات در آنها، انقیاد (Databinding) گویند.

One Way Data Binding (انقیاد یک طرفه)
در این حالت اطلاعات را صرفا در یک View و یا یک کنترل نمایش می‌دهیم و تغییر اطلاعات در View، تاثیری بر روی منبع اطلاعاتی نخواهد داشت.
مثال: یک پروژه‌ی WPF ساده را ایجاد و سپس کلاس Employee را با خصوصیات زیر، تعریف می‌کنیم:
public class Employee
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public static Employee GetEmployee()
        {
            var emp = new Employee
            {
                Name = "Mani",
                Title = "CEO"
            };
            return emp;
        }
    }
در بخش markup فایل MainWindow.xaml کد‌های زیر را ایجاد می‌کنیم:
<Grid>
        <StackPanel Name="Display">
            <StackPanel Orientation="Horizontal">
                <TextBlock>First Name :</TextBlock>
                <TextBlock Margin="5,0,0,0" Text="{Binding Name}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock>Title :</TextBlock>
                <TextBlock Margin="5,0,0,0" Text="{Binding Title}"></TextBlock>
            </StackPanel>
        </StackPanel>
    </Grid>
در markup  فوق برای بایند کردن اطلاعات شیء Employee به خصوصیت Text در Textblock، از روش خاصی استفاده می‌کنیم. ابتدا یک {} ایجاد می‌کنیم و درون آن عبارت Binding و خصوصیت مورد نظر جهت عملیات انقیاد داده‌ها را می‌نویسیم.
برای تکمیل و انجام عملیات Binding کافی است خصوصیت DataContext را با استفاده از متد استاتیک تعریف شده‌ی در کلاس Employee پر کنیم.
 public MainWindow()
        {
            InitializeComponent();        
            DataContext = Employee.GetEmployee();
        }
در ادامه‌ی بحث لازم است کمی مفهوم DataContext را بررسی کنیم. منبع داده پیش فرض در WPF شیء DataContext است؛ مگر اینکه منبع داده را خودمان تغییر دهیم. DataContext یک خصوصیت از کلاس FrameworkElement است که بیشتر کنترل‌های بخش UI در WPF از این کلاس مشتق می‌شوند.
 
INPC یا INotifyPropertyChanged Interface 
پس از بایند کردن اطلاعات به View  مورد نظر، ممکن است منبع داده در طول زمان استفاده تغییر کند. از این رو لازم است که این تغییرات، به View اعمال شوند. بدین منظور می‌بایست ابتدا Interface  ، INotifyPropertyChanged    را برای کلاس Employee پیاده سازی کنیم:
   public class Employee :INotifyPropertyChanged
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public static Employee GetEmployee()
        {
            var emp = new Employee
            {
                Name = "Mani",
                Title = "CEO"
            };
            return emp;
        }
        public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged
            ([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
پس از پیاده سازی Interface، خصوصیات کلاس Employee  نبایستی بصورت  AutoProperty  باشند. پس پیاده سازی خصوصیات را با تعریف فیلد‌های مورد نیاز تغییر می‌دهیم.
علت تغییر پیاده سازی، لزوم فراخوانی رویداد dOnPropertyChange در بلاک Set خصوصیات کلاس Employee  می‌باشد:
 private string _name;
        private string _title;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                OnPropertyChanged();
            }
        }

در ادامه ، در بخش CodeBehind در سازنده کلاس کد‌های زیر را جایگزین کد‌های قبلی  می کنیم :
        private Employee emp;
        public MainWindow()
        {
            InitializeComponent();
            emp = new Employee()
            {
                Name = "Mani",
                Title = "CEO"
            };
            DataContext = emp;
        }
سپس در بخش Markup، یک یک دکمه را ایجاد و رویداد کلیک آن را تعیین می‌کنیم:
     <StackPanel Orientation="Horizontal">
                <Button Click="btnClick" Width="70" Height="30" Content="Change"/>
     </StackPanel>
در بخش CodeBehind، رویداد Click را به شکل زیر پیاده سازی می‌کنیم:
private void btnClick(object sender, RoutedEventArgs e)
        {
            emp.Name = "Amir";
            emp.Title = "Manager";
        }
در برنامه‌ی فوق، در ابتدا View با اطلاعات ارسالی در بخش سازنده، پر می‌شود و با کلیک بر روی دکمه‌، منبع داده به‌روز شده (در اینجا شیء emp) و به‌صورت اتوماتیک View ما به‌روز خواهد شد.

Tow Way Data binding  (انقیاد دو طرفه)
در این حالت از Data binding، با تغییر View، تغییرات بر روی منبع داده نیز اعمال می‌شوند.
ابتدا بخش markup مثال فوق را با اضافه کردن ویژگی Mode در کنار ویژگی Binding به شکل زیر تغییر می‌دهیم:
<Grid>
        <StackPanel Name="Display" >
            <StackPanel Orientation="Horizontal">
                <TextBlock  Margin="5,0,0,0">Name :</TextBlock>
                <TextBox Margin="5,0,0,0" Text="{Binding Name, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="5,0,0,0">Title :</TextBlock>
                <TextBox Margin="5,0,0,0" Text="{Binding Title,Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock  Margin="5,0,0,0">Name :</TextBlock>
                <TextBlock Margin="5,0,0,0" Text="{Binding Name}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="5,0,0,0">Title :</TextBlock>
                <TextBlock Margin="5,0,0,0" Text="{Binding Title}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
اگر ویژگی Mode نوشته نشود بصورت پیش فرض به‌صورت OneWay تعبیر می‌شود. حالت قبل. همچنین در کد بالا دو Textbox در صفحه قرار داده شده‌اند تا با تغییر محتوای آن بتوانیم تاثیر عملیات دوطرفه‌ی انقیاد را بر روی Textblockهای بعدی مشاهده کنیم.
پس از اجرای برنامه (بخش CodeBehind نیازی به اصلاح ندارد) مقداری جدید در Textbox موجود در صفحه تایپ کنید. کافی است Focus از روی کنترلی که محتوای آن را تغییر داده‌اید، عوض شود، بلافاصله Textblock متناظر با آن، با محتوای جدیدی که در منبع داده اعمال شده است به‌روز می‌شود.