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

اعتبارسنجی فایل‌های تصویر که کاربر به سایت ارسال می‌کند، لازم است تا در سمت سرور انجام شود. ولی برای افزایش کارایی و بهبود تجربه کاربری، می‌تواند بخشی از اعتبارسنجی را به صورت موازی در سمت مشتری یا فرانت‌اند نیز به انجام رسانید.
اعتبارسنجی فایل‌های تصویر قبل از آپلود با جاوااسکریپت
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 16 - کار با Sessions
سشن‌ها نیز همانند تمام قسمت‌های دیگر یک برنامه‌ی ASP.NET Core، به صورت پیش فرض غیرفعال هستند و نیاز به مراحل خاصی است تا امکان استفاده‌ی از آن‌ها فراهم شود. همچنین روش کار کردن با آن‌ها نیز متفاوت است با نگارش‌های قبلی ASP.NET (تمام نگارش‌ها).


سشن چیست؟

شیء سشن، مجموعه‌ای از اشیاء serialized مرتبط با جلسه‌ی کاری جاری یک کاربر است. این اشیاء عموما در حافظه‌ی محلی سرور ذخیره می‌شوند؛ اما امکان ذخیره سازی توزیع شده‌ی آن‌ها در بانک‌های اطلاعاتی نیز پیش بینی شده‌است.
عموما استفاده‌ی از اشیاء سشن توصیه نمی‌شوند. از این جهت که این نوع اشیاء بسیار شبیه هستند به متغیرهای سراسری و وجود این نوع متغیرها اساسا ضعف طراحی شیء‌گرا به حساب می‌آیند. اما با توجه به ماهیت stateless بودن برنامه‌های وب، به این معنا که با پایان رندر یک صفحه، تمام اشیاء مرتبط با آن‌ها نیز در سمت سرور تخریب می‌شوند، نیاز است برای یک سری از داده‌های عمومی کاربر، راه حلی را پیدا کرد تا بتوان از اطلاعات آن‌ها استفاده‌ی مجدد کرد. برای مثال نگهداری رشته‌ی اتصالی بانک اطلاعاتی که کاربر در حین لاگین به سیستم آن‌را انتخاب کرده‌است (اگر برنامه به ازای هر سال از یک بانک اطلاعاتی مجزا استفاده می‌کند) و یا زمانیکه کاربری captcha را پر می‌کند و مقدار آن‌را به سمت سرور ارسال می‌کند، نیاز است مقدار ارسالی او را با مقدار ابتدایی captcha مقایسه کرد. یک چنین اطلاعاتی نباید با پایان رندر صفحه تخریب شوند و نیاز است تا زمانیکه جلسه‌ی کاری کاربر به پایان نرسیده‌است، در دسترس باشند. به همین جهت است که مفهومی را به نام «اشیاء سشن» طراحی کرده‌اند.
درکل خارج از این موارد بهتر است از سشن استفاده نکنید و در جای جای برنامه‌ی خود ردپای آن‌را باقی نگذارید و به خاطر داشته باشید:
متغیر سشن = متغیر سراسری = ضعف طراحی شی‌‌ءگرا


توصیه‌ی به استفاده‌ی از روش‌های سبک وزن‌تر

سشن‌ها تنها روش به اشتراک گذاری اطلاعات نیستند. اگر می‌خواهید اطلاعاتی را در بین میان افزارهای برنامه در طی یک درخواست به اشتراک بگذارید، شاید سشن هم یک راه حل باشد؛ اما راه حلی سنگین وزن. راه حل بهتر برای این موارد، استفاده‌ی از HttpContext.Items است. HttpContext.Items نیز همانند سشن، یک key/value store است؛ اما طول عمر آن محدود است به طول عمر درخواست جاری و در تمام میان افزارهای برنامه در دسترس است.
برای مثال در یک میان افزار آن‌را تنظیم می‌کنید:
app.Use(async (context, next) =>
{
   context.Items["isVerified"] = true;
   await next.Invoke();
});
و سپس در میان افزاری دیگر از آن استفاده خواهید کرد:
app.Run(async (context) =>
{
   await context.Response.WriteAsync("Verified request? " + context.Items["isVerified"]);
});


فعال سازی سشن‌ها در ASP.NET Core

ASP.NET Core یک choose-what-you-need framework است. به این معنا که تا زمانیکه قابلیتی را به صورت صریح فعال سازی نکرده باشید، در دسترس نخواهد بود. همین مساله در نهایت به کاهش مصرف منابع این نوع برنامه‌ها و همچنین طراحی ماژولار سیستم ختم می‌شوند. برای مثال در نگارش‌های قبلی ASP.NET (تمام نگارش‌ها)، سشن‌ها به صورت پیش فرض فعال هستند، مگر آنکه HTTP Module آن‌را در فایل web.config حذف کنید؛ اما در اینجا برعکس است.
اگر تنها در موارد خاصی که ذکر شد، نیاز به استفاده‌ی از متغیرهای سشن را داشتید، روش فعال سازی آن به صورت ذیل است:
الف) نصب بسته‌ی نیوگت Microsoft.AspNetCore.Session
برای این منظور وابستگی ذیل را به فایل project.json اضافه کنید:
{
    "dependencies": {
      //same as before
      "Microsoft.AspNetCore.Session": "1.0.0"
    }
}
ب) انجام تنظیمات آغازین برنامه
برای این کار به کلاس آغازین برنامه مراجعه کرده و ابتدا سرویس سشن‌ها را فعال کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
و سپس در متد Configure، استفاده‌ی از سشن‌ها را نیز باید ذکر کنید:
public void Configure(IApplicationBuilder app)
{
    app.UseSession();
در اینجا امکان سفارشی سازی مقادیر پیش فرض مدیریت سشن‌ها نیز وجود دارند. برای مثال زمان پیش فرض انقضای سشن کاربر، پس از 20 دقیقه عدم فعالیت او است که قابل تغییر می‌باشد:
app.UseSession(options: new SessionOptions
{
    IdleTimeout = TimeSpan.FromMinutes(30),
    CookieName = ".MyApplication"
});


روش استفاده‌ی از سشن‌ها

در مثال ذیل نحوه‌ی ذخیره سازی اطلاعات را در شیء سشن جلسه‌ی جاری یک کاربر، ملاحظه می‌کنید:
public ActionResult TestSession()
{
 
   this.HttpContext.Session.Set("key-1", BitConverter.GetBytes(DateTime.Now.Ticks));
   this.HttpContext.Session.SetInt32("key-2", 1);
   this.HttpContext.Session.SetString("key-3", "DNT");
 
   return Content("OK!");
}
به همراه نحوه‌ی بازیابی این اطلاعات در متدهای دیگر برنامه:
 public IActionResult Index()
{
   byte[] key1 = this.HttpContext.Session.Get("key-1");
   long key1Value = BitConverter.ToInt64(key1, 0);
 
   int? key2Value = this.HttpContext.Session.GetInt32("key-2");
   string key3Value = this.HttpContext.Session.GetString("key-3");
 
   return Content("OK!");
}
همانطور که ملاحظه می‌کنید، سه متد Set، SetInt32 و SetString در اینجا برای تنظیم key/valueهای سشن، از پیش موجود هستند.
حالت Set آن آرایه‌ای از بایت‌ها را دریافت می‌کند و می‌توان برای حالت serialization اشیاء، مفید باشد.
دقیقا معادل همین سه متد، متدهای Get، GetInt32 و GetString برای بازیابی مقادیر سشن طراحی شده‌اند و باید دقت داشت که خروجی‌های این‌ها می‌توانند نال نیز باشند. به همین جهت خروجی GetInt32 آن نال پذیر است.


توسعه‌ی متدهای پیش فرض کار با سشن‌ها

سه متد یاد شده‌ی کار با سشن‌ها در ASP.NET Core هرچند ضروری هستند، اما کافی نیستند. برای توسعه‌ی آن‌ها می‌توان متدهای الحاقی را تدارک دید که نمونه‌ای از آن‌ها را ذیل مشاهده می‌کنید:
using System;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
 
namespace Core1RtmEmptyTest.StartupCustomizations
{
    public static class SessionExts
    {
        public static void SetDateTime(this ISession collection, string key, DateTime value)
        {
            collection.Set(key, BitConverter.GetBytes(value.Ticks));
        }
 
        public static DateTime? GetDateTime(this ISession collection, string key)
        {
            var data = collection.Get(key);
            if (data == null)
            {
                return null;
            }
            var dateInt = BitConverter.ToInt64(data, 0);
            return new DateTime(dateInt);
        }
 
        public static void SetObject(this ISession session, string key, object value)
        {
            var stringValue = JsonConvert.SerializeObject(value);
            session.SetString(key, stringValue);
        }
 
        public static T GetObject<T>(this ISession session, string key)
        {
            var stringValue = session.GetString(key);
            return JsonConvert.DeserializeObject<T>(stringValue);
        }
    }
}
در اینجا با استفاده از کلاس BitConverter و امکان سریالایز مقادیر توسط آن به آرایه‌ای از بایت‌ها، امکان کار با متد‌های عمومی Set و Get را یافته‌ایم.
و یا جهت کار با اشیاء پیچیده‌تر می‌توان از کتابخانه‌ی JSON.NET استفاده کرد. به عبارتی در این نگارش از ASP.NET، کار سریالایز و دی‌سریالایز اشیاء، به برنامه نویس واگذار شده‌است و اینکه در پشت صحنه از چه کتابخانه‌ای می‌خواهید استفاده کنید، در اختیار خودتان است.
البته باید دقت داشت که در اینجا وابستگی JSON.NET به صورت خودکار در دسترس است. از این جهت که بسیاری از وابستگی‌های ASP.NET Core مانند مورد ذیل، به JSON.NET وابسته‌اند و نصب آن‌ها به معنای نصب خودکار JSON.NET نیز هست:
{
    "dependencies": {
     //same as before
     "Microsoft.Extensions.Configuration.Json": "1.0.0"
   }
}
اگر لیست بسته‌های وابسته‌ی به JSON.NET را می‌خواهید مشاهده کنید، فایل project.lock.json را گشوده و در آن Newtonsoft.Json را جستجو کنید.


یک مطلب تکمیلی

در اینجا نیز امکان ذخیره سازی سشن‌ها در بانک اطلاعاتی بجای حافظه‌ی فرار سرور درنظر گرفته شده‌است و برای این حالت، بانک‌های اطلاعاتی NoSQL ویژه‌ای به نام key/value stores مانند بانک اطلاعاتی فوق سریع Redis پیشنهاد می‌شود؛ هرچند امکان کار با SQL Server نیز در اینجا وجود دارد، اما برای کش سرورهای مبتنی بر key/value ها، بانک اطلاعاتی Redis، انتخاب اول است.
Managing Application State
نظرات مطالب
متدی برای بررسی صحت کد ملی وارد شده
سلام
من از یک مبدل آنلاین استفاده کردم به این نتیجه رسیدم.
Namespace ConsoleApplicationTest
Class Program
Private Shared Sub Main(args As String())
Console.WriteLine("0172942284 => {0}", "0172942284".IsValidNationalCode())
Console.WriteLine("1000000001 => {0}", "1000000001".IsValidNationalCode())
End Sub
End Class

Public NotInheritable Class Helpers
Private Sub New()
End Sub
<System.Runtime.CompilerServices.Extension> _
Public Shared Function IsValidNationalCode(nationalCode As [String]) As [Boolean]
If [String].IsNullOrEmpty(nationalCode) Then
Throw New Exception("لطفا کد ملی را صحیح وارد نمایید")
End If

If nationalCode.Length <> 10 Then
Throw New Exception("طول کد ملی باید ده کاراکتر باشد")
End If

Dim regex__1 = New Regex("[^0-9]<span> </span>")
If Not regex__1.IsMatch(nationalCode) Then
Throw New Exception("کد ملی تشکیل شده از ده رقم عددی می‌باشد؛ لطفا کد ملی را صحیح وارد نمایید")
End If

If Not Regex.IsMatch(nationalCode, "^(?!(\d)\1{9})\d{10}$") Then
Return False
End If

Dim check = Convert.ToInt32(nationalCode.Substring(9, 1))
Dim result = Enumerable.Range(0, 9).[Select](Function(x) Convert.ToInt32(nationalCode.Substring(x, 1)) * (10 - x)).Sum() Mod 11

Dim remainder As Integer = result Mod 11
Return check = (If(remainder < 2, remainder, 11 - remainder))

End Function
End Class
End Namespace
نظرات اشتراک‌ها
FastReport سورس باز شد
اتفاقا امروز درگیر این گزارشگر بودم ، با توجه به این پست گفتم بد نباشه این مشکل را در اینجا هم به اشتراک بگذارم تا شاید دوستان بتوانند از آن استفاده کنند.
احتمالا اطلاع دارید یکی از ویژگی‌های این گزارشگر پشتیبانی از تگ‌های html میباشد. یکی از مشکلاتی که امروز مشاهده شد این بود که ما خروجی گزارش را خیلی راحت در مرورگر می‌دیدیم ولی خروجی اکسلی که جدیدا اضافه کردیم قابل دانلود نبود و استثنا صادر میشد، بعد از بررسی متوجه شدیم که حداقل در خروجی اکسل (باقی خروجی‌ها را تست نکردیم) تگ font پشتیبانی نمیگردید، در صورتی که در مرورگر مشکلی وجود نداشت. با حذف این تگ مشکل در خروجی اکسل حل شد. 
نظرات مطالب
حذف اعراب از حروف و کلمات
اگر می‌خواهید در حین جستجو، فرقی بین حروف اعراب‌دار و معادل معمولی و بدون اعراب آن‌ها نباشد، در حین تشکیل ایندکس لوسین متد RemoveDiacritics معرفی شده در متن را جهت پاکسازی اعراب، پیش از ذخیره سازی آن‌ها در ایندکس‌ها و اسناد لوسین بکار بگیرد. همچنین در حین جستجو هم ورودی کاربر را با همین متد پاکسازی کنید. برای نمونه معمول است که در حین ایندکس کردن اسناد HTML، ابتدا تمام تگ‌های آن‌ها حذف شده و سپس صرفا متن موجود در صفحه به ایندکس‌ها معرفی می‌شوند.
مطالب
مروری بر کاربردهای Action و Func - قسمت چهارم
طراحی API برنامه توسط Actionها

روش مرسوم طراحی Fluent interfaces، جهت ارائه روش ساخت اشیاء مسطح به کاربران بسیار مناسب هستند. اما اگر سعی در تهیه API عمومی برای کار با اشیاء چند سطحی مانند معرفی فایل‌های XML توسط کلاس‌های سی شارپ کنیم، اینبار Fluent interfaces آنچنان قابل استفاده نخواهند بود و نمی‌توان این نوع اشیاء را به شکل روانی با کنار هم قرار دادن زنجیر وار متدها تولید کرد. برای حل این مشکل روش طراحی خاصی در نگارش‌های اخیر NHibernate معرفی شده است به نام loquacious interface که این روزها در بسیاری از APIهای جدید شاهد استفاده از آن هستیم و در ادامه با پشت صحنه و طرز تفکری که در حین ساخت این نوع API وجود دارد آشنا خواهیم شد.

در ابتدا کلاس‌های مدل زیر را در نظر بگیرید که قرار است توسط آن‌ها ساختار یک جدول از کاربر دریافت شود:
using System;
using System.Collections.Generic;

namespace Test
{
    public class Table
    {
        public Header Header { set; get; }
        public IList<Cell> Cells { set; get; }
        public float Width { set; get; }
    }

    public class Header
    {
        public string Title { set; get; }
        public DateTime Date { set; get; }
        public IList<Cell> Cells { set; get; }
    }

    public class Cell
    {
        public string Caption { set; get; }
        public float Width { set; get; }
    }
}
در روش طراحی loquacious interface به ازای هر کلاس مدل، یک کلاس سازنده ایجاد خواهد شد. اگر در کلاس جاری، خاصیتی از نوع کلاس یا لیست باشد، برای آن نیز کلاس سازنده خاصی درنظر گرفته می‌شود و این روند ادامه پیدا می‌کند تا به خواصی از انواع ابتدایی مانند int و string برسیم:
using System;
using System.Collections.Generic;

namespace Test
{
    public class TableApi
    {
        public Table CreateTable(Action<TableCreator> action)
        {
            var creator = new TableCreator();
            action(creator);
            return creator.TheTable;
        }
    }

    public class TableCreator
    {
        readonly Table _theTable = new Table();
        internal Table TheTable
        {
            get { return _theTable; }
        }

        public void Width(float value)
        {
            _theTable.Width = value;
        }

        public void AddHeader(Action<HeaderCreator> action)
        {
            _theTable.Header = ...
        }

        public void AddCells(Action<CellsCreator> action)
        {
            _theTable.Cells = ...
        }        
    }
}
نقطه آغازین API ایی که در اختیار استفاده کنندگان قرار می‌گیرد با متد CreateTable ایی شروع می‌شود که ساخت شیء جدول را به ظاهر توسط یک Action به استفاده کننده واگذار کرده است، اما توسط کلاس TableCreator او را مقید و راهنمایی می‌کند که چگونه باید اینکار را انجام دهد.
همچنین در بدنه متد CreateTable، نکته نحوه دریافت خروجی از Action ایی که به ظاهر خروجی خاصی را بر نمی‌گرداند نیز قابل مشاهده است.
همانطور که عنوان شد کلاس‌های xyzCreator تا رسیدن به خواص معمولی و ابتدایی پیش می‌روند. برای مثال در سطح اول چون خاصیت عرض از نوع float است، صرفا با یک متد معمولی دریافت می‌شود. دو خاصیت دیگر نیاز به Creator دارند تا در سطحی دیگر برای آن‌ها سازنده‌های ساده‌تری را طراحی کنیم.
همچنین باید دقت داشت که در این طراحی تمام متدها از نوع void هستند. اگر قرار است خاصیتی را بین خود رد و بدل کنند، این خاصیت به صورت internal تعریف می‌شود تا در خارج از کتابخانه قابل دسترسی نباشد و در intellisense ظاهر نشود.
مرحله بعد، ایجاد دو کلاس HeaderCreator و CellsCreator است تا کلاس TableCreator تکمیل گردد:
using System;
using System.Collections.Generic;

namespace Test
{
    public class CellsCreator
    {
        readonly IList<Cell> _cells = new List<Cell>();
        internal IList<Cell> Cells
        {
            get { return _cells; }
        }

        public void AddCell(string caption, float width)
        {
            _cells.Add(new Cell { Caption = caption, Width = width });
        }
    }

    public class HeaderCreator
    {
        readonly Header _header = new Header();
        internal Header Header
        {
            get { return _header; }
        }

        public void Title(string title)
        {
            _header.Title = title;
        }

        public void Date(DateTime value)
        {
            _header.Date = value;
        }

        public void AddCells(Action<CellsCreator> action)
        {
            var creator = new CellsCreator();
            action(creator);
            _header.Cells = creator.Cells;
        }
    }
}
نحوه ایجاد کلاس‌های Builder و یا Creator این روش بسیار ساده و مشخص است:
مقدار هر خاصیت معمولی توسط یک متد ساده void دریافت خواهد شد.
هر خاصیتی که اندکی پیچیدگی داشته باشد، نیاز به یک Creator جدید خواهد داشت.
کار هر Creator بازگشت دادن مقدار یک شیء است یا نهایتا ساخت یک لیست از یک شیء. این مقدار از طریق یک خاصیت internal بازگشت داده می‌شود.

البته عموما بجای معرفی مستقیم کلاس‌های Creator از یک اینترفیس معادل آن‌ها استفاده می‌شود. سپس کلاس Creator را internal تعریف می‌کنند تا خارج از کتابخانه قابل دسترسی نباشد و استفاده کننده نهایی فقط با توجه به متدهای void تعریف شده در interface کار تعریف اشیاء را انجام خواهد داد.

در نهایت، مثال تکمیل شده ما به شکل زیر خواهد بود:
using System;
using System.Collections.Generic;

namespace Test
{
    public class TableCreator
    {
        readonly Table _theTable = new Table();
        internal Table TheTable
        {
            get { return _theTable; }
        }

        public void Width(float value)
        {
            _theTable.Width = value;
        }

        public void AddHeader(Action<HeaderCreator> action)
        {
            var creator = new HeaderCreator();
            action(creator);
            _theTable.Header = creator.Header;
        }

        public void AddCells(Action<CellsCreator> action)
        {
            var creator = new CellsCreator();
            action(creator);
            _theTable.Cells = creator.Cells;
        }
    }

    public class CellsCreator
    {
        readonly IList<Cell> _cells = new List<Cell>();
        internal IList<Cell> Cells
        {
            get { return _cells; }
        }

        public void AddCell(string caption, float width)
        {
            _cells.Add(new Cell { Caption = caption, Width = width });
        }
    }

    public class HeaderCreator
    {
        readonly Header _header = new Header();
        internal Header Header
        {
            get { return _header; }
        }

        public void Title(string title)
        {
            _header.Title = title;
        }

        public void Date(DateTime value)
        {
            _header.Date = value;
        }

        public void AddCells(Action<CellsCreator> action)
        {
            var creator = new CellsCreator();
            action(creator);
            _header.Cells = creator.Cells;
        }
    }
}
نحوه استفاده از این طراحی نیز جالب توجه است:
var data = new TableApi().CreateTable(table =>
            {
                table.Width(1);
                table.AddHeader(header=>
                {
                    header.Title("new rpt");
                    header.Date(DateTime.Now);
                    header.AddCells(cells=>
                    {
                        cells.AddCell("cell 1", 1);
                        cells.AddCell("cell 2", 2);
                    });
                });
                table.AddCells(tableCells=>
                {
                    tableCells.AddCell("c 1", 1);
                    tableCells.AddCell("c 2", 2);
                });
            });

این نوع طراحی مزیت‌های زیادی را به همراه دارد:
الف) ساده سازی طراحی اشیاء چند سطحی و تو در تو
ب) امکان درنظر گرفتن مقادیر پیش فرض برای خواص
ج) ساده‌تر سازی تعاریف لیست‌ها
د) استفاده کنندگان در حین استفاده نهایی و تعریف اشیاء به سادگی می‌توانند کدنویسی کنند (مثلا سلول‌ها را با یک حلقه اضافه کنند).
ه) امکان بهتر استفاده از امکانات Intellisense. برای مثال فرض کنید یکی از خاصیت‌هایی که قرار است برای آن Creator درست کنید یک interface را می‌پذیرد. همچنین در برنامه خود چندین پیاده سازی کمکی از آن نیز وجود دارد. یک روش این است که مستندات قابل توجهی را تهیه کنید تا این امکانات توکار را گوشزد کند؛ روش دیگر استفاده از طراحی فوق است. در اینجا در کلاس Creator ایجاد شده چون امکان معرفی متد وجود دارد، می‌توان امکانات توکار را توسط این متدها نیز معرفی کرد و به این ترتیب Intellisense تبدیل به راهنمای اصلی کتابخانه شما خواهد شد.
نظرات مطالب
نوشتن TagHelperهای سفارشی برای ASP.NET Core
یک نکته: برای تبدیل محتوای یک TagBuilder به رشته می‌توان از متد زیر استفاده کرد:
using System.IO;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;

namespace Sample
{
    /// <summary>
    /// Html Helper Extensions
    /// </summary>
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Convert IHtmlContent/TagBuilder to string
        /// </summary>
        public static string GetString(this IHtmlContent content)
        {
            using (var writer = new StringWriter())
            {
                content.WriteTo(writer, HtmlEncoder.Default);
                return writer.ToString();
            }
        }
    }
}
مطالب
استفاده از AvalonEdit در WPF
AvalonEdit یکی از زیرساخت‌های برنامه‌ی SharpDevelop است که ویرایشگر متنی به همراه syntax highlighting زبان‌های مختلف را در آن پشتیبانی می‌کند. کیفیت بالایی داشته و بسیاری از برنامه‌های دیگر نیز از آن جهت ارائه ویرایشگر و یا syntax highlighting متون ارائه شده، استفاده می‌کنند. در ادامه نحوه‌ی استفاده از این ویرایشگر را در برنامه‌های WPF خصوصا با دید MVVM بررسی خواهیم کرد.



دریافت و نصب AvalonEdit

برای نصب AvalonEdit می‌توان دستور ذیل را در کنسول پاورشل نیوگت صادر کرد:
 PM> install-package AvalonEdit


استفاده‌ی مقدماتی از AvalonEdit

برای استفاده از این ویرایشگر ابتدا نیاز است فضای نام xmlns:avalonEdit تعریف شود. سپس کنترل avalonEdit:TextEditor در دسترس خواهد بود:
<Window x:Class="SyntaxHighlighter.MainWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
   Title="MainWindow" Height="401" Width="617">
  <Grid>
<avalonEdit:TextEditor  
    Name="txtCode"
    SyntaxHighlighting="C#"
  FontFamily="Consolas"
  FontSize="10pt"/>   
  </Grid>
</Window>
توسط خاصیت SyntaxHighlighting آن می‌توان زبان مشخصی را تعریف کرد. لیست زبان‌های توکار پشتیبانی شده


استفاده از AvalonEdit در برنامه‌های MVVM

خاصیت Text این ویرایشگر به صورت معمولی تعریف شده (DependencyProperty نیست) و امکان binding دو طرفه به آن وجود ندارد. به همین جهت نیاز است یک چنین DependencyProperty را به آن اضافه کرد:
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Windows;
using System.Xml;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;

namespace AvalonEditWpfTest.Controls
{
    public class BindableAvalonTextEditor : TextEditor
    {
        public static readonly DependencyProperty BoundTextProperty =
            DependencyProperty.Register("BoundText",
                typeof(string),
                typeof(BindableAvalonTextEditor),
                new FrameworkPropertyMetadata(default(string), propertyChangedCallback));
 
        public static string GetBoundText(DependencyObject obj)
        {
            return (string)obj.GetValue(BoundTextProperty);
        }

        public static void SetBoundText(DependencyObject obj, string value)
        {
            obj.SetValue(BoundTextProperty, value);
        }

        protected override void OnTextChanged(EventArgs e)
        {
            SetCurrentValue(BoundTextProperty, Text);
            base.OnTextChanged(e);
        }

        private static void propertyChangedCallback(DependencyObject obj,
                                    DependencyPropertyChangedEventArgs args)
        {
            var target = (BindableAvalonTextEditor)obj;
            var value = args.NewValue;
            if (value == null)
                return;

            if (string.IsNullOrWhiteSpace(target.Text) ||
                !target.Text.Equals(args.NewValue.ToString()))
            {
                target.Text = args.NewValue.ToString();
            }
        }
    }
}
کار با ارث بری از TextEditor (ویرایشگر AvalonEdit) شروع می‌شود. سپس یک DependencyProperty به نام BoundText در اینجا اضافه شده‌است. هر زمان که متن داخل آن تغییر کرد، آن‌را به خاصیت متنی Text این ویرایشگر نسبت می‌دهد. به این ترتیب binding یک طرفه (از کدها به کنترل) کار می‌کند. فعال سازی binding دو طرفه با پشتیبانی از انتقال تغییرات از ویرایشگر به خواص ViewModel در متد بازنویسی شده‌ی OnTextChanged انجام می‌شود.

اکنون برای استفاده از این کنترل جدید که BindableAvalonTextEditor نام دارد، می‌توان به نحو ذیل عمل کرد:
<Window x:Class="AvalonEditWpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModels="clr-namespace:AvalonEditTests.ViewModels"
        xmlns:controls="clr-namespace:AvalonEditWpfTest.Controls"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <viewModels:MainWindowViewModel x:Key="MainWindowViewModel"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource MainWindowViewModel}}">
        <controls:BindableAvalonTextEditor
                BoundText="{Binding SourceCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                        WordWrap="True"
                        ShowLineNumbers="True"
                        LineNumbersForeground="MediumSlateBlue"
                        FontFamily="Consolas"
                        VerticalScrollBarVisibility="Auto"
                        Margin="3"          
                        HorizontalScrollBarVisibility="Auto"
                        FontSize="10pt"/>
    </Grid>
</Window>
ابتدا فضای نام جدید کنترل BindableAvalonTextEditor مشخص می‌شود و سپس به controls:BindableAvalonTextEditor دسترسی خواهیم داشت. در اینجا نحوه‌ی استفاده از خاصیت جدید BoundText را نیز مشاهده می‌کنید.


افزودن syntax highlighting زبان‌هایی که به صورت رسمی پشتیبانی نمی‌شوند

به خاصیت SyntaxHighlighting این کنترل صرفا مقادیری را می‌توان نسبت داد که به صورت توکار پشتیبانی می‌شوند. برای مثال#XML، C و امثال آن.
فرض کنید نیاز است SyntaxHighlighting زبان SQL را فعال کنیم. برای اینکار نیاز به فایل‌های ویژه‌ای است، با پسوند xshd. برای نمونه فایل sql-ce.xshd را در اینجا می‌توانید مطالعه کنید. در آن یک سری واژه‌های کلیدی و حروفی که باید با رنگی متفاوت نمایش داده شوند، مشخص می‌گردند.
برای استفاده از فایل sql-ce.xshd باید به نحو ذیل عمل کرد:
الف) فایل sql-ce.xshd را به پروژه اضافه کرده و سپس در برگه‌ی خواص آن در VS.NET، مقدار build action آن‌را به embedded resource تغییر دهید.



ب) با استفاده از متد ذیل، این فایل مدفون شده در اسمبلی را گشوده و به متد HighlightingLoader.Load ارسال می‌کنیم:
        private static IHighlightingDefinition getHighlightingDefinition(string resourceName)
        {
            if (string.IsNullOrWhiteSpace(resourceName))
                throw new NullReferenceException("Please specify SyntaxHighlightingResourceName.");

            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                if (stream == null)
                    throw new NullReferenceException(string.Format("{0} resource is null.", resourceName));

                using (var reader = new XmlTextReader(stream))
                {
                    return HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }
نحوه استفاده از آن نیز به صورت ذیل است:
 txtCode.SyntaxHighlighting = getHighlightingDefinition(resourceName);
به این ترتیب می‌توان یک فایل xhsd را به صورت پویا بارگذاری و به خاصیت SyntaxHighlighting کنترل انتساب داد.

برای سهولت استفاده از این قابلیت شاید بهتر باشد یک DependencyProperty دیگر به نام SyntaxHighlightingResourceName را به کنترل جدید BindableAvalonTextEditor اضافه کنیم:
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Windows;
using System.Xml;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;

namespace AvalonEditWpfTest.Controls
{
    public class BindableAvalonTextEditor : TextEditor
    {
         public static readonly DependencyProperty SyntaxHighlightingResourceNameProperty =
           DependencyProperty.Register("SyntaxHighlightingResourceName",
               typeof(string),
               typeof(BindableAvalonTextEditor),
               new FrameworkPropertyMetadata(default(string), resourceNamePropertyChangedCallback));
 
        public static string GetSyntaxHighlightingResourceName(DependencyObject obj)
        {
            return (string)obj.GetValue(SyntaxHighlightingResourceNameProperty);
        }

        public static void SetSyntaxHighlightingResourceName(DependencyObject obj, string value)
        {
            obj.SetValue(SyntaxHighlightingResourceNameProperty, value);
        }

        private static void loadHighlighter(TextEditor @this, string resourceName)
        {
            if (@this.SyntaxHighlighting != null)
                return;

            @this.SyntaxHighlighting = getHighlightingDefinition(resourceName);
        }

        private static void resourceNamePropertyChangedCallback(DependencyObject obj,
                                            DependencyPropertyChangedEventArgs args)
        {
            var target = (BindableAvalonTextEditor)obj;
            var value = args.NewValue;
            if (value == null)
                return;

            loadHighlighter(target, value.ToString());
        }
    }
}
کاری که در اینجا انجام شده، افزودن یک خاصیت جدید به نام SyntaxHighlightingResourceName به کنترل BindableAvalonTextEditor است. هر زمانیکه مقدار آن تغییر کند، متد getHighlightingDefinition بحث شده، فراخوانی گردیده و به صورت پویا مقدار خاصیت SyntaxHighlighting این کنترل، مقدار دهی می‌شود.
استفاده از آن نیز به شکل زیر است:
   <controls:BindableAvalonTextEditor
SyntaxHighlightingResourceName = "AvalonEditWpfTest.Controls.sql-ce.xshd"
/>

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
AvalonEditWpfTest.zip 
مطالب
تبدیل شدن زبان C# 9.0 به یک زبان اسکریپتی با معرفی ویژگی Top Level Programs
اگر به قالب ابتدایی یک برنامه‌ی کنسول #C دقت کنیم، همواره به ساختار استاندارد زیر می‌رسیم:
using System;

namespace CS9Features
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
در اینجا یک سری import، به همراه تعریف فضای نام، تعریف کلاس و تعریف متد Main وجود دارند ... تا بتوان یک سطر Hello World را در کنسول نمایش داد. در این حالت اگر تازه شروع به یادگیری زبان #C کرده باشید، مفاهیم زیادی را باید در جهت درک آن فرا بگیرید؛ برای مثال static چیست؟ args چیست؟ کاربرد فضای نام چیست و غیره. کاری که در C# 9.0 انجام شده، امکان حذف تمام این عوامل در جهت نمایش تک سطر Hello World است که به آن top level programs و یا top level statements گفته می‌شود.


تبدیل قالب پیش‌فرض برنامه‌های کنسول به یک Top level program

در C# 9.0 می‌توان تمام سطرهای فوق را به دو سطر زیر تقلیل داد و خلاصه کرد:
using System;

Console.WriteLine("Hello World!");
این قطعه کد بدون هیچگونه مشکلی در C# 9.0 کامپایل می‌شود و به این ترتیب زبان #C را تبدیل و یا شبیه به یک «زبان اسکریپتی» ساده می‌کند.


روش استفاده از متدهای async در Top level programs

زمانیکه نقطه‌ی آغازین برنامه را تبدیل به یک top level program کردیم، دیگر دسترسی مستقیمی را به متد Main نداریم تا آن‌را async Task دار معرفی کنیم و پس از آن بتوانیم به سادگی با متدهای async کار کنیم. برای رفع این مشکل، کامپایلر فقط کافی است یک await را در قطعه کد شما پیدا کند. خودش به صورت خودکار متد Main غیرهمزمانی را جهت اجرای کدها، تشکیل می‌دهد. به همین جهت برای کار با کدهای async در اینجا، نیاز به تنظیم خاصی نیست و قطعه کد زیر که در آن متد MyMethodAsync را اجرا می‌کند، بدون مشکل کامپایل و اجرا خواهد شد:
using System;
using System.Threading.Tasks;

await MyMethodAsync();
Console.WriteLine("Hello World!");

static async Task MyMethodAsync()
{
   await Task.Yield();
}


روش دسترسی به args در Top level programs

همانطور که در قطعه کد ابتدایی این مطلب مشخص است، متد Main به همراه پارامتر string[] args نیز هست. اما اکنون در Top level programs که فاقد متد Main هستند، چگونه می‌توان به این آرگومان‌های ارسالی توسط کاربر دسترسی یافت؟
پاسخ: پارامتر args نیز هنوز در اینجا قابل دسترسی است؛ فقط به ظاهر مخفی است:
using System;

Console.WriteLine(args[0]);


ارائه‌ی return codes به فراخون در Top level programs

بعضی از برنامه‌های کنسول در انتهای متد Main خود برای مثال return 0 و یا return 1 را دارند؛ که اولی به معنای موفقیت عملیات و دومی به معنای شکست عملیات است. در top level programs نیز می‌توان این return‌ها را در انتهای کار قید کرد:
using System;
Console.WriteLine($"Hello world!");
return 1;
که یک چنین خروجی نهایی را توسط کامپایلر تولید می‌کند:
// <Program>$
using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal static class <Program>$
{
   private static int <Main>$(string[] args)
   {
     Console.WriteLine("Hello world!");
     return 1;
   }
}


امکان تعریف کلاس‌ها و متدها در Top level programs

در تک فایل program.cs برنامه، در حین کار با Top level programs محدودیتی از لحاظ تعریف متدها، کلاس‌ها و غیره نیست؛ یک مثال:
using System;

var greeter = new Greeter();

var helloTeacher = greeter.Greet("teacher");
var helloStudents = SayHello("students");

Console.WriteLine(helloTeacher);
Console.WriteLine(helloStudents);

static string SayHello(string name)
{
    return "Hello, " + name;
}

public class Greeter
{
    public string Greet(string name)
    {
        return "Hello, " + name;
    }
}
همانطور که مشاهده می‌کنید، در حالت کار اسکریپتی با زبان #C، امکان استفاده‌ی از کلاس‌ها و یا متدها نیز وجود دارد؛ اما با یک شرط: این تعاریف باید پس از Top-level statements قرار گیرند. یعنی اگر متد و کلاس تعریف شده را به بالای فایل انتقال دهید، به خطای کامپایلر زیر خواهید رسید:
Top-level statements must precede namespace and type declarations. [CS9Features]csharp(CS8803)


سطوح دسترسی به کلاس‌ها و متدهای تعریف شده‌ی در Top level programs

اگر قطعه کد مثال قبل را کامپایل کنیم، نمونه‌ی دی‌کامپایل شده‌ی آن به صورت زیر است:
using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal static class <Program>$
{
  private static void <Main>$(string[] args)
  {
   Greeter greeter = new Greeter();
   string helloTeacher = greeter.Greet("teacher");
   string helloStudents = SayHello("students");
   Console.WriteLine(helloTeacher);
   Console.WriteLine(helloStudents);

   static string SayHello(string name)
   {
    return "Hello, " + name;
   }
  }
}
همانطور که مشاهده می‌کنید، کامپایلر نه فقط نام متدها را تغییر داده‌است، بلکه سطوح دسترسی به آن‌ها را یا private و یا internal تعریف کرده‌است. به این معنا که کلاس‌ها و متدهای تعریف شده‌ی در Top level programs در سایر کتابخانه‌ها و یا برنامه‌ها، قابل استفاده و دسترسی نیستند. البته کلاس public class Greeter به همان صورت public باقی می‌ماند و سطح دسترسی آن تغییری نمی‌کند.


نوع متدهای تعریف شده‌ی در Top level programs

مثال زیر را که یک top level program است، درنظر بگیرید:
using System;

Foo();

var x = 3;

int result = AddToX(4);
Console.WriteLine(result);

static void Foo()
{
    Console.WriteLine("Foo");
}

int AddToX(int y)
{
    return x + y;
}
متد AddToX که static نیست، امکان دسترسی به متغیر x را یافته‌است. با توجه به اینکه متد Main هم static است، چطور چنین چیزی ممکن شده‌است؟
پاسخ: متدهایی که در top level programs تعریف می‌شوند در حقیقت از نوع local functions هستند که در ابتدا در C# 7.0 معرفی شدند و سپس در C# 8.0 امکان تعریف نمونه‌های static آن‌ها نیز میسر شد.
قطعه کد فوق در اصل به صورت زیر کامپایل می‌شود که متدهای AddToX و Foo در آن داخل متد Main تشکیل شده، به صورت local function تعریف شده‌اند:
// <Program>$
using System;
using System.Runtime.CompilerServices;

[CompilerGenerated]
internal static class <Program>$
{
   private static void <Main>$(string[] args)
   {
     Foo();
     int x = 3;
     int result = AddToX(4);
     Console.WriteLine(result);

     int AddToX(int y)
     {
       return x + y;
     }

     static void Foo()
     {
       Console.WriteLine("Foo");
     }
   }
}
فقط یک local function از نوع static، دسترسی به متغیرهای تعریف شده‌ی در متد Main را ندارد.
مطالب
دریافت اطلاعات از پایگاه داده بواسطه Stored Procedure در EF Core 2.0
همواره در تکنولوژی  EF CodeFirst، چه در ASP.NET MVC و چه در ASP.NET Core، استفاده از امکانات بومی پایگاه‌های داده با محدودیت‌هایی مواجه بوده‌است. یکی از این اشکالات، عدم توانایی این تکنولوژی در گرفتن لیستی از اطلاعات که منطبق بر بیشتر از یک مدل می‌باشد، هست. در این مقاله تمرکز بر روی رفع این اشکال، بدون نیاز به اضافه کردن مدخل جدیدی به پروژه می‌باشد. بنابراین پیشنیاز ضروری این مبحث، مطالعه «شروع به کار با EF Core 1.0» ، مخصوصا «استفاده از امکانات بومی بانک‌های اطلاعاتی» است.

Stored Procedure چیست ؟

Stored Procedure  یا  SP  یا به زبان فارسی «رویه‌های ذخیره شده» اشیایی اجرا پذیر در بانک اطلاعاتی SQL Server هستند که شامل یک یا چندین دستور SQL می‌شوند. این رویه‌ها می‌توانند پارامتر‌های ورودی و خروجی داشته باشند؛ همچنین می‌توانند لیستی از موجودیت‌ها را نیز برگردانند و یا می‌توان داخل این رویه‌ها به زبان T-SQL برنامه نویسی کرد.
مهم‌ترین کاربر این رویه‌ها، ذخیره کردن دستورات Select , Insert , Update , Delete هست و یا ترکیبی از این‌ها .


اشکال راه حل‌های پیش فرض مبتنی بر Context

برای استفاده از راه حل‌های پیش فرض  مبتنی بر Context، همانطور که در مقاله «استفاده از امکانات بومی بانک‌های اطلاعاتی» به آن پرداخته شده، سه روش کلی برای استفاده از Stored Procedure  پیشنهاد شده‌است:
- روش اول استفاده از متد fromsql است. اشکال این متد، محدودیت استفاده برای یک موجودیت برنامه  است و به زبان ساده نمی‌توان در کوئری پایگاه داده از join  استفاده کرد.
- روش دوم استفاده از متد ExecuteSqlCommand موجود در context برنامه است . اشکال این متد void بودن آن است که باعث می‌شود بازگشتی از پایگاه داده حاصل نشود.
- روش سوم استفاده از متد ExecuteScalar  موجود در Context برنامه است. اشکالی که به این متد گرفته می‌شود، Scalar  بودن مقدار بازگشتی از آن است که باعث می‌شود نتوانیم لیستی از موجودیت‌ها را به ViewModel مورد نظر نگاشت کنیم.

راه حل این مشکل

برای حل این مشکلات که بسیار هم مهم هستند، اول باید قطعه کد زیر را به Context برنامه اضافه نمود:
public void OpenConnection()
{
   Database.OpenConnection();
}

public DbCommand Command()
{
   DbCommand cmd = Database.GetDbConnection().CreateCommand();
   return cmd;
}
سپس در اینترفیس IUnitOfWork  که در مطلب «لایه بندی و تزریق وابستگی‌ها» در مورد آن بحث شده، متد OpenConnection و Command را اضافه می‌کنیم:
void OpenConnection();
DbCommand Command();
حال کلاس و اینترفیس جدیدی را برای پیاده سازی سرویس اتصال به Stored Procedure ایجاد کرده و  در کلاس آغازین برنامه، به‌صورت AddScopped این سرویس را برای استفاده از تزریق وابستگی توکار ASP.NET Core  معرفی می‌کنیم:
public void ConfigureServices(IServiceCollection services)
{
     services.AddScoped<IUnitOfWork, ApplicationDbContext>();
     services.AddScoped<ISpReader, SpReader>();
}
سپس در سازنده کلاس این سرویس، اینترفیس IUnitOfWork را تزریق کرده تا بتوانیم از متد‌های نوشته شده در Context استفاده کنیم. حال اقدام به پیاده سازی متد GetFromSp بصورت زیر می‌کنیم :
public List<ViewModel> GetFromSp <ViewModel>(string[,] Parametr, string NameSp) where ViewModel  : new()
        {
            _uow.OpenConnection();
            DbCommand cmd = _uow.Command();
            cmd.CommandText = NameSp;
            cmd.CommandType = CommandType.StoredProcedure;
            var countParametr = Parametr.GetLength(0);

            for (int i = 0; i < countParame tr; i++)
            {
                cmd.Parameters.Add(new SqlParameter { ParameterName = Parametr[i, 0], Value = Parametr[i, 1] });
            }

            List<ViewModel> list = new List<ViewModel >();
            using (var reader = cmd.ExecuteReader())
            {
                if (reader != null && reader.HasRows)
                {
                    var entity = typeof(ViewModel);
                    var propDict = new Dictionary<string, PropertyInfo>();
                    var props = entity.GetProperties
           (BindingFlags.Instance | BindingFlags.Public);
                    propDict = props.ToDictionary(p => p.Name.ToUpper(), p => p);
                    while (reader.Read())
                    {
                        ViewModel  newobject = new ViewModel ();

                        for (int index = 0; index < reader.FieldCount; index++)
                        {
                            if (propDict.ContainsKey(reader.GetName(index).ToUpper()))
                            {
                                var info = propDict[reader.GetName(index).ToUpper()];
                                if ((info != null) && info.CanWrite)
                                {
                                    var val = reader.GetValue(index);
                                    info.SetValue(newobject, (val == DBNull.Value) ? null : val, null);
                                }
                            }
                        }
                        list.Add(newobject);
                    }

                }
                return list;
            }
در این متد، اول با استفاده از OpenConnection، اتصالی را به پایگاه داده، باز کرده سپس شیئ از DbCommand را می‌سازیم و نام Stored Procedure و نوع کوئری ارسالی را معین می‌کنیم. حال با استفاده از  حلقه for، نام و مقدار پارامتر‌های ارسال شده به متد را به شیئ cmd اضافه می‌کنیم. در مرحله بعد، لیستی را از کلاس مدلی که باید مقادیر بازگشتی به آن نگاشت شوند و بعنوان کلاس جنریک به متد ارسال شده است، می‌سازیم. با متد ExecuteReader که در شیئ ساخته شده از Command موجود می‌باشد، اقدام به خواندن اطلاعات از Stored Procedure کرده و در شیئ Reader نگه داری می‌کنیم و سپس اطلاعات خوانده شده را با استفاده از Dictionary و متد Add به لیست ساخته شده اضافه می‌کنیم. در آخر لیست ساخته شده در حلقه While را بعنوان نتیجه متد باز می‌گردانیم.

همچنین می‌توان برای استفاده این متد برای رویه‌های بدون پارامتر ورودی، از OverLoad این متد، با حذف قطعات کد زیر:
var countParametr = Parametr.GetLength(0);
for (int i = 0; i < countParametr; i++)
{
     cmd.Parameters.Add(new SqlParameter { ParameterName = Parametr[i, 0], Value = Parametr[i, 1] });
}
و حذف آرایه string[,] Parameter  از ورودی متد، استفاده نمود .

روش استفاده از این متد

برای استفاده از این متد، لازم است چند نکته رعایت شوند:
1- خروجی Stored Procedure دقیقا منطبق بر ViewModel ارسالی به متد جهت تشکیل لیست باشد.
2- لیست پارامتر‌ها باید بصورت آرایه دوبعدی باشد که اندازه بعد اول، تعداد پارامتر‌ها و اندازه بعد دوم 2 باشد.
3- در ماتریسی که از این پارامتر‌ها ساخته می‌شود، ستون اول نام پارامتر و ستون دوم مقدار پارامتر ست می‌شود.

بطور مثال Stored Procedure  زیر حاوی سه پارامتر است :
CREATE PROCEDURE [dbo].[isRelation](
@TableName as varchar(50),
@FieldOfRelation as varchar(70),
@ValueOfField as int)
برای دسترسی به این رویه ابتدا در سرویس استفاده کننده، ISpReader را تزریق می‌کنیم و سپس بصورت زیر مقدمات استفاده از این سرویس را فراهم می‌کنیم:

public class EntityServices : IEntityService
    {
        private ISpreader _Reader;
        public EntityServices( ISpreader reader)
        {
            _Reader = reader;
        }

        public List<StoreProcedureResultViewModel>  IsRelation(string tableName , int keyValue, string keyFieldName)
        {
            List<StoreProcedureResultViewModel> IsContact;
            try
            {
                string[,] Parametr = new string[3, 2];
                Parametr[0, 0] = "@TableName";
                Parametr[0, 1] = tableName ;
                Parametr[1, 0] = "@ValueOfField";
                Parametr[1, 1] = keyValue.ToString().Trim();
                Parametr[2, 0] = "@FieldOfRelation";
                Parametr[2, 1] = keyFieldName.Trim();
                IsContact = _Reader.GetSp<StoreProcedureResultViewModel>(Parametr, "IsRelation");
                return IsContact;
            }
            catch (Exception ex)
            {
            }
        }
    }
بدین ترتیب با استفاده از این متد توانستیم لیستی از ViewModel منطبق بر خروجی Stored Procedure  را بدست آوریم.