نظرات اشتراک‌ها
چرا از آنگولار به ری اکت + ری داکس سوئیچ کردم!
  • تایپ اسکریپت برای برنامه نویسان سی شارپ و کلا خانواده مایکروسافت ایده ال می‌باشد در حالی که این یک گروه خاص و نه اکثریت رو تشکیل میدهند. مسلما برنامه نویس‌های حرفه ای جاوا اسکریپت، خلوص، سادگی و انعطاف پذیری زبان اصلی رو با مزیت‌های جانبی که ترانس پایلرهای گونان ارائه میدهند، عوض نمی‌کنند (برای کار با مرورگر بهتر است). (( بنده به شخصه جاوا اسکریپت رو ترجیح میدهم )). در ضمن انگولار را با جاوااسکریپت هم میتوان استفاده کرد.
  • تزریق وابستگی  به هیچ زبان خاصی وابسته نیست و بطور گسترده در کتابخانه‌ها و فریم ورک‌های جاوا اسکریپتی استفاده میشود. یکی از بهترین و ساده‌ترین پیاده سازی این الگو در زبان جاوا اسکریپت صورت میگیرد.
  • یکی از لدلایل محبوبیت و استفاده از ری اکت نسبت به انگولار کامپوننت‌های ساده و با قابلیت استفاده مجدد می‌باشد که از توابع جاوااسکریتی خالص تولید میشوند. (هر کامپوننت معادل یک تابع است، تست پذیری ساده و سرعت اجرای بالا)^
  • ری اکت یک کتابخانه است و نه یک فریم ورک. این شما هستید که تک تک اجزای سیستم رو با دستی باز انتخاب می‌کنید. این امر برنامه نویس رو به سمت فول استک شدن هدایت می‌کند.

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

الگوی Composite در عمل یک Collection Pattern (الگوی مجموعه ای) است. که می‌توان در درون آن ترکیبی از زیر مجموعه‌های مختلف را قرار داد و سپس هر زیر مجموعه را به نوبه خود فراخوانی نمود.به بیان دیگر الگوی Composite به ما کمک می‌کند که در یک ساختار درختی بتوانیم مجموعه ای (Collection ی)،از بخشی از آبجکتهای سلسله مراتبی را نمایش دهیم. این الگو به Client اجازه می‌دهد، که رفتار یکسانی نسبت به یک Collection ی از آبجکتها یا یک آبجکت تنها داشته باشد.

مثالهای متعددی می‌توان از الگوی Composite زد، که در ذیل به چند نمونه از آنها می‌پردازیم:

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

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

نمونه سوم: یک File System را در نظر بگیرید،که ساختارش از File و Folder تشکیل شده است. و می‌تواند یک ساختار سلسله مراتبی داشته باشد.بطوریکه درون هر Folder می‌تواند یک یا چند File یا Folder قرار گیرد. و در درون Folder‌های زیر مجموعه می‌توان چندین File یا Folder دیگر قرار داد.اگر بخواهیم به عنوان نمونه شکل ساختار درختی File و فولدر را نمایش دهیم بصورت زیر خواهد بود:

در ساختار درختی به Folder شاخه یا Branch گویند، چون می‌تواند زیر شاخه‌های دیگری نیز در خود داشته باشد. و به File برگ یا Leaf گویند.برگ نمی‌تواند زیر مجموعه ای داشته باشد. در واقع برگ (Leaf) بیانگر انتهای یک شاخه می‌باشد.

نمونه آخر:می توان به ساختار منوها در برنامه‌ها اشاره نمود.هر منو می‌تواند شامل چندین زیر منو باشد. و همان زیر منوها می‌توانند از چندین زیر منوی دیگر تشکیل شوند. این ساختار نیز یک ساختار سلسله مراتبی می‌باشد، و برای نگهداری آبجکتهای یک مجموعه می‌توان از الگوی Composite استفاده نمود.

الگوی Composite از سه Component اصلی تشکیل شده است،که یکایک آنها را بررسی می‌کنیم:

  • Component: کلاس پایه ای است که در آن متدها یا Functionality‌های مشترک تعریف می‌گردد. Component می‌تواند یک Abstract Class یا Interface باشد.
  • Leaf : به آبجکتهای گفته می‌شود که هیچ Child ی ندارند. و فقط یک آبجکت مستقل تنها می‌باشد. کلاس Leaf متدهای مشترک تعریف شده در Component  را پیاده سازی می‌کند.اگر مثال File و Folder را بخاطر آورید،File یک آبجکت از نوع Leaf است چون نمی‌تواند هیچ فرزندی داشته باشد و یک آبجکت تنها می‌باشد.
  • Composite: کلاس فوق Collection ی از آبجکتها را در خود نگهداری می‌کند، به عبارتی در Composite می‌توان بخشی از ساختار درختی را قرار داد، که این ساختار می‌تواند ترکیبی از آبجکتهای Leaf و Composite باشد. در مثال File و Folder، یک Folder را می‌توان به عنوان Composite در نظر گرفت،زیرا که یک Folder می‌تواند چندین File یا Folder را در خود جای دهد. در کلاس Composite معمولا متدهایی همچون Add (افزودن Remove،( Child (حذف یک Child) و غیرو... وجود دارد. 
کلاس Leaf و کلاس Composite از کلاس Component ارث بری (Inherit) می‌شوند.
شکل زیر بیانگر الگوی Composite می‌باشد:


توصیف شکل: طبق تعاریف گفته شده، دو کلاس Leaf و Composite از Inherit ،Component شده اند. و Client نیز فقط متد‌های مشترک تعریف شده در Component را مشاهده می‌کند، به عبارتی Client رفتار یکسانی نسبت به Leaf و Composite خواهد داشت.
برای درک بیشتر الگوی Composite مثالی را بررسی می‌کنیم، فرض کنید در کلاس Component متدی به نام Display را تعریف می‌کنیم،بطوریکه نام آبجکت را نمایش دهد.بنابراین خواهیم داشت:
اینترفیسی را برای Component در نظر می‌گیریم، و متد Display را در آن تعریف می‌کنیم:
public interface Icomponent
    {
        void Display(int depth);
    }
در کلاس Leaf، اینترفیس IComponent را پیاده سازی می‌نماییم:
 public class Leaf:Icomponent
    {
        private String name = string.Empty;
        public Leaf(string name)
        {
            this.name = name;
        }

        public void Display(int depth)
        {

            Console.WriteLine(new String('-', depth) + ' ' + name);

        }
    }
در کلاس Composite نیز اینترفیس IComponent را پیاده سازی می‌نماییم، با این تفاوت که متد‌های Add و Remove را نیز در کلاس Composite اضافه می‌کنیم، چون قبلا هم گفته بودیم، Composite در حکم یک Collection می‌باشد، بنابراین می‌بایست قابلیت حذف و اضافه نمود آبجکت در خود را داشته باشد. پیاده سازی متد Display در آن بصورت Recursive (بازگشتی) میباشد. و علتش این است که بتوانیم ساختار سلسله مراتبی را بازیابی نماییم.
 public class Composite:Icomponent
    {
        private List<Icomponent> _children = new List<Icomponent>();
        private String name = String.Empty;

        public Composite(String sname)
        {
            this.name = sname;
        }

        public void Add(Icomponent component)
        {

            _children.Add(component);

        }


        public void Remove(Icomponent component)
        {

            _children.Remove(component);

        }


        public void Display(int depth)
        {

            Console.WriteLine(new String('-', depth) + ' ' + name);



            // Recursively display child nodes

            foreach (Icomponent component in _children)
            {

                component.Display(depth + 2);

            }

        }
    }
در ادامه بوسیله چندین آبجکت Leaf و Composite یک ساختار درختی را ایجاد می‌کنیم.
class Program
    {
        static void Main(string[] args)
        {
            // Create a tree structure

            Composite root = new Composite("root");

            root.Add(new Leaf("Leaf A"));

            root.Add(new Leaf("Leaf B"));



            Composite comp = new Composite("Composite X");

            comp.Add(new Leaf("Leaf XA"));

            comp.Add(new Leaf("Leaf XB"));



            root.Add(comp);

            root.Add(new Leaf("Leaf C"));



            // Add and remove a leaf

            Leaf leaf = new Leaf("Leaf D");

            root.Add(leaf);

            root.Remove(leaf);



            // Recursively display tree

            root.Display(1);

            Console.ReadKey();
        }
    }
در ابتدا یک آبجکت Composite ایجاد می‌کنیم و آن را به عنوان ریشه در نظر گرفته و نام آن را Root قرار می‌دهیم. سپس دو آبجکت LeafA و LeafB را به آن می‌افزاییم، در ادامه آبجکت Composite دیگری به نام Comp ایجاد می‌کنیم، که خود دارای دو فرزند به نامهای LeafXA و LeafXB  می باشد. و سر آخر هم یک آبجکت LeafC ایجاد می‌کنیم.
آبجکت LeafD صرفا جهت نمایش افزودن و حذف کردن آن در یک آبجکت Composite نوشته شده است. برای این که بتوانیم ساختار سلسله مراتبی کد بالا را مشاهده نماییم، متد Root.Display را اجرا می‌کنیم و خروجی آن بصورت زیر خواهد بود:

اگر بخواهیم،شکل درختی آن را تصور کنیم بصورت زیر خواهد بود:


درپایان باید بگویم،که نمونه کد بالا را می‌توان به ساختار File و Folder نیز تعمیم داد، بطوریکه متدهای مشترک بین File و Folder را در اینترفیس IComponent تعریف می‌کنیم و بطور جداگانه در کلاسهای Composite و Leaf پیاده سازی می‌کنیم.
امیدوارم توضیحات داده شده در مورد الگوی Composite مفید واقع شود.

مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت یازدهم - پیاده سازی پویای Decoratorها توسط Castle.Core
در قسمت قبل، نحوه‌ی پیاده سازی الگوی Decorator را با استفاده از امکانات تزریق وابستگی‌های NET Core. بررسی کردیم؛ اما ... این روزها کسی Decoratorها را دستی ایجاد نمی‌کند. یعنی اگر قرار باشد به ازای هر کلاسی و هر سرویسی، یکبار کلاس Decorator آن‌را با پیاده سازی همان اینترفیس سرویس اصلی و فراخوانی دستی تک تک متدهای سرویس اصلی تزریق شده‌ی در سازنده‌ی آن انجام دهیم، آنچنان کاربردی به نظر نمی‌رسد. به همین منظور کتابخانه‌هایی تحت عنوان Dynamic Proxy تهیه شده‌اند تا کار ساخت و پیاده سازی پویای Decorator‌ها را انجام دهند. در این بین ما فقط منطق برای مثال مدیریت استثناءها، لاگ کردن ورودی‌ها و خروجی‌های متدها، کش کردن خروجی متدها، سعی مجدد اجرای متدهای با شکست مواجه شده و ... تک تک متدهای یک سرویس را به آن‌ها معرفی می‌کنیم و سپس پروکسی‌های پویا، کار محصور سازی خودکار اشیاء ساخته شده‌ی از سرویس اصلی را برای ما انجام می‌دهند. این روش نه فقط کار نوشتن دستی Decorator کلاس‌ها را حذف می‌کند، بلکه عمومی‌تر نیز بوده و به تمام سرویس‌ها قابل اعمال است.


Interceptors پایه‌ی پروکسی‌های پویا هستند

برای پیاده سازی پروکسی‌های پویا نیاز است با مفهوم Interceptors آشنا شویم. به کمک Interceptors فرآیند فراخوانی متدها و خواص یک کلاس، تحت کنترل و نظارت قرار خواهند گرفت. زمانیکه یک IOC Container کار وهله سازی کلاس سرویس خاصی را انجام می‌دهد، در همین حین می‌توان مراحل شروع، پایان و خطاهای متدها یا فراخوانی‌های خواص را نیز تحت نظر قرار داد و به این ترتیب مصرف کننده، امکان تزریق کدهایی را در این مکان‌ها خواهد یافت. مزیت مهم استفاده از Interceptors، عدم نیاز به کامپایل ثانویه اسمبلی‌های موجود، برای تغییری در کدهای آن‌ها است (برای تزریق نواحی تحت کنترل قرار دادن اعمال) و تمام کارها به صورت خودکار در زمان اجرای برنامه مدیریت می‌گردند.

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

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


پیاده سازی پروکسی‌های پویا توسط کتابخانه‌ی Castle.Core در برنامه‌های NET Core.

در ادامه از کتابخانه‌ی بسیار معروف Castle.Core برای پیاده سازی پروکسی‌های پویا استفاده خواهیم کرد. از این کتابخانه در پروژه‌ی EF Core، برای پیاده سازی Lazy loading نیز استفاده شده‌است.
برای دریافت آن یکی از دستورات زیر را اجرا نمائید:
> Install-Package Castle.Core
> dotnet add package Castle.Core
و یا به صورت خلاصه برای افزودن آن، فایل csproj برنامه به صورت زیر تغییر می‌کند:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <PackageReference Include="castle.core" Version="4.3.1" />
  </ItemGroup>
</Project>


تبدیل ExceptionHandlingDecorator مثال قسمت قبل، به یک Interceptor مخصوص Castle.Core

در قسمت قبل، کلاس MyTaskServiceDecorator را جهت اعمال یک try/catch به همراه logging، به متد Run سرویس MyTaskService، تهیه کردیم. در اینجا قصد داریم نگارش عمومی‌تر این تزئین کننده را با طراحی یک Interceptor مخصوص Castle.Core انجام دهیم:
using System;
using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;

namespace CoreIocSample02.Utils
{
    public class ExceptionHandlingInterceptor : IInterceptor
    {
        private readonly ILogger<ExceptionHandlingInterceptor> _logger;

        public ExceptionHandlingInterceptor(ILogger<ExceptionHandlingInterceptor> logger)
        {
            _logger = logger;
        }

        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.Proceed(); //فراخوانی متد اصلی در اینجا صورت می‌گیرد
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "An unhandled exception has been occurred.");
            }
        }
    }
}
برای تهیه‌ی یک کلاس Interceptor، کار با پیاده سازی اینترفیس IInterceptor شروع می‌شود. در اینجا فراخوانی متد ()invocation.Proceed، دقیقا معادل فراخوانی متد اصلی سرویس است؛ شبیه به کاری که در قسمت قبل انجام دادیم:
        public void Run()
        {
            try
            {
                _decorated.Run();
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "An unhandled exception has been occurred.");
            }
        }
فراخوان، یک نمونه‌ی تزئین شده‌ی از سرویس درخواستی را دریافت می‌کند. زمانیکه متد Run این نمونه‌ی ویژه را اجرا می‌کند، در حقیقت وارد متد Run این Decorator شده‌است که اینبار در پشت صحنه، توسط Dynamic proxy ما به صورت پویا ایجاد می‌شود. اکنون جائیکه ()invocation.Proceed فراخوانی می‌شود، دقیقا معادل همان ()decorated.Run_ قسمت قبل است؛ یا همان اجرای متد اصلی سرویس مدنظر. اینجا است که می‌توان منطق‌های سفارشی را مانند لاگ کردن، کش کردن، سعی مجدد در اجرا و بسیاری از حالات دیگر، پیاده سازی کرد.


اتصال ExceptionHandlingInterceptor تهیه شده به سیستم تزریق وابستگی‌ها

در ادامه روش معرفی ExceptionHandlingInterceptor تهیه شده را به سیستم تزریق وابستگی‌ها، توسط متد Decorate کتابخانه‌ی Scrutor که آن‌را در قسمت قبل بررسی کردیم، ملاحظه می‌کنید:
namespace CoreIocSample02
{
    public class Startup
    {
        private static readonly ProxyGenerator _dynamicProxy = new ProxyGenerator();

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ITaskService, MyTaskService>();
            services.AddTransient<ExceptionHandlingInterceptor>();
            services.Decorate(typeof(ITaskService),
             (target, serviceProvider) =>
                _dynamicProxy.CreateInterfaceProxyWithTargetInterface(
                  interfaceToProxy: typeof(ITaskService),
                  target: target,
                  interceptors: serviceProvider.GetRequiredService<ExceptionHandlingInterceptor>())
            );
- ProxyGenerator به همین نحو static readonly باید تعریف شود و در کل برنامه یک وهله از آن مورد نیاز است.
- سپس نیاز است خود سرویس اصلی غیر تزئین شده، به نحو متداولی به سیستم معرفی شود.
- در ادامه توسط متد الحاقی Decorate، کار تزئین ITaskService را با یک dynamicProxy که ExceptionHandlingInterceptor را به صورت پویا تبدیل به یک Decorator کرده و بر روی تک تک متدهای سرویس ITaskService اجرا می‌کند، انجام می‌دهیم.
- کاری که Scrutor در اینجا انجام می‌دهد، یافتن سرویس ITaskService معرفی شده‌ی پیشین و تعویض آن با dynamicProxy می‌باشد. بنابراین نیاز است تعریف services.AddTransient، پیش از تعریف services.Decorate انجام شده باشد.

یک نکته: چون ExceptionHandlingInterceptor دارای پارامتر تزریق شده‌ای در سازنده‌ی آن است، بهتر است خود آن‌را نیز به صورت یک سرویس ثبت کنیم و سپس وهله‌ای از آن‌را از طریق serviceProvider.GetRequiredService در قسمت interceptors متد CreateInterfaceProxyWithTargetInterface معرفی کنیم تا نیازی به مقدار دهی دستی تک تک پارامترهای سازنده‌ی آن نباشد.

اکنون اگر برنامه را اجرا کنیم و برای مثال ITaskService را در سازنده‌ی یک کنترلر تزریق کنیم، بجای دریافت وهله‌ای از کلاس MyTaskService، اینبار وهله‌ای از Castle.Proxies.ITaskServiceProxy را دریافت می‌کنیم.


به این معنا که Castle.Core به صورت پویا وهله‌ی سرویس MyTaskService را داخل یک Castle.Proxies پیچیده‌است و از این پس ما از طریق این واسط، با سرویس اصلی MyTaskService ارتباط برقرار خواهیم کرد. برای درک بهتر این مراحل، بر روی سازنده‌ی کلاس کنترلر و همچنین متد Intercept، تعدادی break-point را قرار دهید.
نظرات مطالب
آشنایی با الگوی طراحی Decorator
فرض من این بود که کاربر نیازی به رفرنس گیری از هر آبجکت ندارد.
مثلا طبق مقاله:
 
// ساخت کیک مخصوص مخلوط با روکش کاکائویی و روکش میوه‌ای به صورت همزمان
Cake c = new SpecialCake();
Chocolate ch = new Chocolate(c);
Fruity f = new Fruity(ch);
همانطور که در مقاله گفته شده:
Cake cc1 = new Cake();
Type1 cd1 = new Type1 (cc1); 
Type2 cd2 = new Type2(cc1);

کد فوق را میتوان اینگونه هم داشت:
// ساخت کیک مخصوص مخلوط با روکش کاکائویی و روکش میوه‌ای به صورت همزمان
Cake c = new SpecialCake();
c = new Chocolate(c);
c = new Fruity(c);

بدون اینکه شیء جدید برای تایپ جدید بسازیم.
مطالب
نحوه استفاده از Text template ها در دات نت - قسمت سوم
 خوب در دو قسمت قبلی (^ و ^) با T4 و قواعد کد نویسی Text Template آشنا شدید. در این قسمت برخی مفاهیم را با یک مثال کاربردی‌تر بررسی می‌کنیم.
در ادامه قواعد زیر را در نظر بگیرید :
1 - برای استفاده از یک کتابخانه خارجی (dll) در داخل کد Text Template از بلوک <#@ assembly #> استفاده می‌شود .
مثلا برای استفاده از System.xml کد <#@ assembly name="System.xml" #> رو قرار بدید .
2- برای import کردن یک فضای نام نیز می‌توانید از بلوک #> <#@ استفاده نمایید .
به عنوان مثال : <#@ import namespace="System.Data" #>
3- encoding خروجی قابل تظیم می‌باشد. مثال: <#@ " output  extension = ".html" encoding = "utf-8 #>
4- برای استفاده از یک فایل tt درون فایل دیگر میتوان از include  استفاده کرد. مثال :   <#@ " include file=" testpath \basetest.tt #>
 
حالا به مثال زیر توجه کنید:

در بسیاری از پروژه‌ها، معادل تمامی جداول موجود در یک دیتابیس Class هایی به نام DTO  یا Data transfer object ساخته میشود که عموما" کلاسهای سبکی هستند که فقط شامل خصوصیت‌های معادل فیلدهای جداول می‌باشند و از آن‌ها جهت مدل کردن داده‌ها و ... استفاده می‌گردد. تولید این کلاسهای ساده می‌تواند بصورت اتوماتیک صورت گیرد و از این جهت در زمان تولید پروژه صرفه جویی شود. همچنین با تغییر ساختار دیتابیس می‌توان همواره کلاسها را بروزرسانی کرد.  نمونه ای از کد T4 برای تولید تمامی کلاسهای DTO به شکل زیر میباشد . فقط برای تست کد زیر دقت کنید که ConnectionString  را در داخل کد متناسب با دیتابیس خود تغییر دهید:  
<#@ template language="C#" debug="True" hostspecific="True" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Data" #>

<#@ assembly name="System.xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Data" #>

using System;
namespace MyProject.Entities 
{    
        <#
            string connectionString = "Password=22125110;Persist Security Info=True;User ID=sa;Initial Catalog=DnnDB;Data Source=ABBASPOOR299"; 
            SqlConnection conn = new SqlConnection(connectionString); 
            conn.Open(); 
            System.Data.DataTable schema = conn.GetSchema("TABLES"); 
            string selectQuery = "select * from @tableName"; 
            SqlCommand command = new SqlCommand(selectQuery,conn); 
            SqlDataAdapter ad = new SqlDataAdapter(command); 
            System.Data.DataSet ds = new DataSet(); 
            
            foreach(System.Data.DataRow row in schema.Rows) 
            { 
            #> 
            public class <#= row["TABLE_NAME"].ToString().Trim('s') #>                
            
            {
                <#                 
                    command.CommandText = selectQuery.Replace("@tableName",row["TABLE_NAME"].ToString()); 
                    ad.FillSchema(ds, SchemaType.Mapped, row["TABLE_NAME"].ToString());

                    foreach (DataColumn dc in ds.Tables[row["TABLE_NAME"].ToString()].Columns)
                    {
                        #>
                        
                    private <#= dc.DataType.Name  #> _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower())      #>;
                    public <#= dc.DataType.Name #> <#= dc.ColumnName #> 
                    {
                        get { return _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower()) #>; } 
                        set { _<#= dc.ColumnName.Replace(dc.ColumnName[0].ToString(), dc.ColumnName[0].ToString().ToLower()) #> = value; }
                    }                    
                    
                <#    }                 #>
                
            }                
            
            <#    
            }  #>            
}
خوب؛ خودتون می‌توانید تست کنید ... این یکی از رایج‌ترین استفاده‌های T4 Text Template هست که برنامه نویسان از آن استفاده می‌کنند. 
مطالب
همه چیز در مورد CLR : قسمت اول
در حال حاضر من کتاب CLR Via Csharp ویرایش چهارم نوشته آقای جفری ریچر را مطالعه می‌کنم و نه قسمت از این مقالات، از بخش اول فصل اول آن به پایان رسیده که همگی آن‌ها را تا 9 روز آینده منتشر خواهم کرد. البته سعی شده که مقالات ترجمه صرف نباشند و منابع دیگری هم در کنار آن استفاده شده است. بعضی موارد را هم لینک کرده‌ام. تمام سعی خود را می‌کنم تا ادامه کتاب هم به مرور به طور مرتب ترجمه شود؛ تا شاید نسخه‌ی تقریبا کاملی از این کتاب را به زبان فارسی در اختیار داشته باشیم.
بعد از اینکه برنامه را تحلیل کردید و نیازمندی‌های یک برنامه را شناسایی کردید، وقت آن است که زبان برنامه نویسی خود را انتخاب کنید. هر زبان ویژگی‌های خاص و منحصر به فرد خود را دارد و این ممکن هست انتخاب شما را سخت کند. برای مثال شما در زبان‌های ++unmanaged C/C، کنترل بسیار زیادی روی امور سیستمی از قبیل حافظه و تردها دارید و به هر روشی که می‌خواهید می‌توانید آن‌ها را پیکربندی کنید. در زبان‌هایی چون Visual basic قدیم و مشابه‌های آن عموما اینگونه بود که طراحی یک اپلیکیشن از رابط کاربری گرفته تا اتصال به دیتابیس و اشیاء COM در آن ساده باشد؛ ولی در زبان‌های CLR چطور؟

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

زبان‌های CLR چگونه کار می‌کنند؟
در اولین گام بعد از نوشتن برنامه، کامپایلر آن زبان دست به کار شده و برنامه را برای شما کامپایل می‌کند. ولی اگر تصور می‌کنید که برنامه را به کد ماشین تبدیل می‌کند و از آن یک فایل اجرایی می‌سازد، سخت در اشتباه هستید. کامپایلر هر زبان  CLR، کد‌ها را به یک زبان میانی Intermediate Language به اختصار IL تبدیل می‌کند. فرقی نمی‌کند چه زبانی کار کرده‌اید، کد شما تبدیل شده است به یک زبان میانی مشترک. CLR نمی‌تواند برای تک تک زبان‌های شما یک مفسر داشته باشد. در واقع هر کمپایلر قواعد زبان خود را شناخته و آن را به یک زبان مشترک تبدیل می‌سازد و حالا CLR می‌تواند حرف تمامی زبان‌ها را بفهمد. به فایل ساخته شده  managed module گویند و به زبان‌هایی که از این قواعد پیروی نمی‌کنند unmanaged گفته می‌شود؛ مثل زبان سی ++ که در دات نت هم managed و هم unmanaged داریم که اولی بدون فریم ورک دات نت کار می‌کند و مستقیما به کد ماشین تبدیل می‌شود و دومی نیاز به فریم ورک دات نت داشته و به زبان میانی کامپایل می‌شود. جدول زیر نشان می‌دهد که کد همه‌ی زبان‌ها تبدیل به یک نوع شده است.


 
فایل هایی که ساخته می‌شوند بر دو نوع هستند؛ یا بر اساس استاندارد windows Portable Executable 32bits برای سیستم‌های 32 بیتی و 64 بیتی هستند و یا بر اساس windows Portable Executable 64bits مختص سیستم‌های 64 بیتی هستند که به ترتیب PE32 و +PE32 نامیده می‌شوند که CLR بر اساس این اطلاعات آن‌ها را به کد اجرایی تبدیل می‌کند. زبان‌های CLR همیشه این مزیت را داشته‌اند که اصول امنیتی چون DEP یا Data Execution Prevention و همچنین ASLR یا Address Space Layout Randomization در آن‌ها لحاظ شده باشد.
پروژه‌ها
شمسی ساز تاریخ اکسپلورر ویندوز
چقدر خوب می‌شد اگر تاریخ و ساعت کنار صفحه ویندوز، شمسی بود


یا اگر لیست فایل‌ها را بررسی می‌کردیم، ستون تاریخ آن‌ها نیز قابل درک بود


و یا اگر به خواص یک فایل مراجعه می‌کردیم، تاریخ ایجاد آن نیز شمسی بود


- با استفاده از برنامه سورس باز ExplorerPCal.exe می‌توانید به این قابلیت‌ها دسترسی پیدا کنید.
- این برنامه برای اجرا نیاز به دات نت فریم ورک 4 دارد. بنابراین بر روی ویندوزهای XP SP3 به بعد قابل اجرا است. ضمنا برنامه ExplorerPCal.exe با هر دو نگارش 32 بیتی و 64 بیتی ویندوز سازگار است.
- برای اجرای آن تنها کافی است فایل «ExplorerPCal.exe» را اجرا کنید. همچنین بهتر است برنامه را در درایو C کپی نکنید.
- برای حذف آن از سیستم، نیاز است پوشه‌ی مربوطه را حذف نمائید. در این حال اگر ویندوز پیام در حال استفاده بودن فایلی را می‌دهد، ویندوز را یکبار ری‌استارت کنید و پس از آن فایل‌های باقیمانده را بدون مشکل می‌توانید حذف نمائید.
- برای ویندوز 10 به این مطلب مراجعه کنید.
مطالب
آشنایی با Oslo - قسمت دوم

قبل شروع این قسمت بد نیست با یک سری از وبلاگ‌های اعضای تیم Oslo آشنا شویم:


در ادامه‌ی مثال قسمت قبل، اکنون می‌خواهیم entity جدیدی به نام Project را به مدل اضافه کنیم:

//mschema to define a Project type
type Project
{
ProjectID : Integer64 = AutoNumber();
ProjectName : Text#25;
ConectionStringSource : Text;
ConectionStringDestination : Text;
DateCompared: DateTime;
Comment: Text?;
ProjectOwner: ApplicationUser;
} where identity ProjectID;

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

//this will define a SQL foreign key relationship
ProjectCollection : Project* where item.ProjectOwner in ApplicationUserCollection;

پس از افزودن این سطر، Intellipad بلافاصله اسکریپت T-SQL آن‌را برای ما ایجاد می‌کند که به شرح زیر است:

set xact_abort on;
go

begin transaction;
go

set ansi_nulls on;
go

create schema [Test1];
go

create table [Test1].[ApplicationUserCollection]
(
[UserID] bigint not null identity,
[FirstName] nvarchar(max) null,
[LastName] nvarchar(25) not null,
[Password] nvarchar(10) not null,
constraint [PK_ApplicationUserCollection] primary key clustered ([UserID])
);
go

create table [Test1].[ProjectCollection]
(
[ProjectID] bigint not null identity,
[Comment] nvarchar(max) null,
[ConectionStringDestination] nvarchar(max) not null,
[ConectionStringSource] nvarchar(max) not null,
[DateCompared] datetime2 not null,
[ProjectName] nvarchar(25) not null,
[ProjectOwner] bigint not null,
constraint [PK_ProjectCollection] primary key clustered ([ProjectID]),
constraint [FK_ProjectCollection_ProjectOwner_Test1_ApplicationUserCollection] foreign key ([ProjectOwner]) references [Test1].[ApplicationUserCollection] ([UserID])
);
go

insert into [Test1].[ApplicationUserCollection] ([FirstName], [LastName], [Password])
values (N'user1', N'name1', N'1@34')
;

insert into [Test1].[ApplicationUserCollection] ([FirstName], [LastName], [Password])
values (N'user2', N'name2', N'123@4')
;

insert into [Test1].[ApplicationUserCollection] ([FirstName], [LastName], [Password])
values (N'user3', N'name3', N'56#2')
;

insert into [Test1].[ApplicationUserCollection] ([FirstName], [LastName], [Password])
values (N'user4', N'name4', N'789@5')
;
go

commit transaction;

Go

همانطور که ملاحظه‌ می‌کنید، هنگام کار کردن با یک مدل، نگهداری و توسعه‌ی آن واقعا ساده‌تر است از ایجاد این دستورات T-SQL .

نکته:
جهت آشنایی با انواع داده‌های مجاز در زبان M می‌توان به مستندات رسمی آن مراجعه نمود:
The "Oslo" Modeling Language Specification

اکنون قصد داریم همانند مثال قسمت قبل، تعدادی رکورد آزمایشی را برای این جدول تعریف کنیم:

ProjectCollection
{
Project1{
ProjectName = "My Project 1",
ConectionStringSource = "Data Source=.;Initial Catalog=MyDB1;Integrated Security=True;",
ConectionStringDestination = "Data Source=.;Initial Catalog=MyDB2;Integrated Security=True;",
Comment="Project Comment",
DateCompared=2009-01-01T00:00:00,
ProjectOwner=ApplicationUserCollection.User1 //direct ref to User1 (FK)
},
Project2{
ProjectName = "My Project 2",
ConectionStringSource = "Data Source=.;Initial Catalog=MyDB1;Integrated Security=True;",
ConectionStringDestination = "Data Source=.;Initial Catalog=MyDB2;Integrated Security=True;",
Comment="Project Comment",
DateCompared=2009-01-01T00:00:00,
ProjectOwner=ApplicationUserCollection.User2 //direct ref to User2 (FK)
}

}

چون بین ProjectOwner و ApplicationUserCollection رابطه ایجاد کرده‌ایم، هنگام استفاده از آن‌ها، برنامه Intellipad جهت سهولت کار، IntelliSense مربوطه را نیز نمایش خواهد داد :


ادامه دارد ...

مطالب
برش تصاویر قبل از آپلود (Crop)
چند روز پیش در یکی از سیستم‌های مدیریتی که امکان آپلود چند تصویر وجود داشت، مسئله‌ای پیش آمد که نیاز بود تصاویر وارد شده، اندازه‌ای به یک نسبت را داشته باشند و کاربران مرتبا تصاویری با اندازه‌های مختلف را وارد میکردند که باعث میشد UI در حین نمایش تصاویر، از شکل اصلی خود دور شود. به همین دلیل امکان برش تصاویر یا Crop برای این امر احساس میشد. کتابخانه‌های مختلف و زیادی برای برش تصاویر و کار بر روی تصاویر وجود دارند، ولی این کتابخانه باید چند خصوصیت زیر را دارا باشد:

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

کتابخانه jcrop کتابخانه‌ای است که این امکانات را برای شما فراهم می‌کند. این کتابخانه بدین صورت است که در حین برش به شما 4 عدد x1,x2,y1,y2 را داده و شما با ارسال آن به سمت سرور میتوانید بر اساس این اعداد، عکس اصلی را برش بزنید. بدین صورت شما هم عکس اصلی را دارید و هم مختصات برش را دارید و اگر دوست دارید در جاهای مختلف از عکس اصلی برش داشته باشید، بسیار مفید خواهد بود.

مرحله اول:
ابتدا فایل jcrop را دانلود نمایید.

مرحله دوم:
 کد Html زیر را به صفحه اضافه کنید:
<div>
    <!-- upload form -->
    <!-- hidden crop params -->
    <input type="hidden" id="x1" name="x1" />
    <input type="hidden" id="y1" name="y1" />
    <input type="hidden" id="x2" name="x2" />
    <input type="hidden" id="y2" name="y2" />
    <h2>ابتدا تصویر خود را انتخاب کنید</h2>
    <div><input type="file" name="postedFileBase" data-buttonText="انتخاب تصویر" id="image_file" onchange="fileSelectHandler()" /></div>
    <div></div>
    <div>
        <h2>قسمتی از تصویر را انتخاب نمایید</h2>
        <img id="preview" />
        <div>
            <label>حجم فایل </label> <input type="text" id="filesize" name="filesize" />
            <label>نوع فایل</label> <input type="text" id="filetype" name="filetype" />
            <label>ابعاد فایل</label> <input style="direction: ltr;" type="text" id="filedim" name="filedim" />

        </div>
    </div>

</div>
در اینجا 4 عدد input به صورت مخفی قرار گرفته‌اند که مختصات برش به ترتیب در آن‌ها ذخیره می‌شوند و هنگام post یا قرار دادن در formData جهت ارسال ایجکسی نیز می‌توانند مورد استفاده قرار بگیرند. جهت input file مورد نظر برای زیبایی و پشتیبانی از عبارت فارسی دلخواه به جای «Browse» از کتابخانه  Bootstrap FileStyle استفاده شده است و رویداد Onchange آن نیز به یک تابع جاوا اسکریپتی بایند شده تا بعد از تایید تصویر، باقی عملیات برش تصویر انجام شوند.

 تگ step2 نیز بعد از نمایش موفقیت آمیز تصویر نشان داده میشود که کاربر میتواند در آن تصویر را برش دهد و شامل بخش info نیز می‌باشد تا بتوان اندازه اصلی تصویر، نوع فایل تصویر Content Type و حجم آن را نمایش داد.

مرحله سوم:
سپس برای استایل دهی کدهای بالا از کد Css زیر استفاده میکنیم:
.bheader {
background-color: #DDDDDD;
border-radius: 10px 10px 0 0;
padding: 10px 0;
text-align: center;
}
.bbody {
color: #000;
overflow: hidden;
padding-bottom: 20px;
text-align: center;
background: -moz-linear-gradient(#ffffff, #f2f2f2);
background: -ms-linear-gradient(#ffffff, #f2f2f2);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f2f2f2));
background: -webkit-linear-gradient(#ffffff, #f2f2f2);
background: -o-linear-gradient(#ffffff, #f2f2f2);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2')";
background: linear-gradient(#ffffff, #f2f2f2);
}
.bbody h2, .info, .error {
margin: 10px 0;
}
.step2, .error {
display: none;
}
.error {
color: red;
}
.info {
}
label {
margin: 0 5px;
}
.roundinput {
border: 1px solid #CCCCCC;
border-radius: 10px;
padding: 4px 8px;
text-align: center;
width: 150px;
}
.jcrop-holder {
display: inline-block;
}
input[type=submit] {
background: #e3e3e3;
border: 1px solid #bbb;
border-radius: 3px;
-webkit-box-shadow: inset 0 0 1px 1px #f6f6f6;
box-shadow: inset 0 0 1px 1px #f6f6f6;
color: #333;
padding: 8px 0 9px;
text-align: center;
text-shadow: 0 1px 0 #fff;
width: 150px;
}
input[type=submit]:hover {
background: #d9d9d9;
-webkit-box-shadow: inset 0 0 1px 1px #eaeaea;
box-shadow: inset 0 0 1px 1px #eaeaea;
color: #222;
cursor: pointer;
}
input[type=submit]:active {
background: #d0d0d0;
-webkit-box-shadow: inset 0 0 1px 1px #e3e3e3;
box-shadow: inset 0 0 1px 1px #e3e3e3;
color: #000;
}

مرحله چهارم:
افزودن کد جاوااسکریپتی زیر برای کار کردن با کتابخانه Jcrop می‌باشد:
function bytesToSize(bytes) {
    var sizes = ['بایت', 'کیلو بایت', 'مگابایت'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};

function updateInfo(e) {
    $('#x1').val(e.x);
    $('#y1').val(e.y);
    $('#x2').val(e.x2);
    $('#y2').val(e.y2);
};

var jcrop_api, boundx, boundy;
function fileSelectHandler() {

    var oFile = $('#image_file')[0].files[0];

    $('.error').hide();
    var rFilter = /^(image\/jpeg|image\/png)$/i;
    if (!rFilter.test(oFile.type)) {
        $('.error').html('فقط تصویر معتبر انتخاب نمایید').show();
        return;
    }

   
    var oImage = document.getElementById('preview');

    var oReader = new FileReader();
    oReader.onload = function (e) {

        oImage.src = e.target.result;
        

        oImage.onload = function () { 
          

            $('.step2').fadeIn(500);

            var sResultFileSize = bytesToSize(oFile.size);
            $('#filesize').val(sResultFileSize);
            $('#filetype').val(oFile.type);
            $('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);
     
            if (typeof jcrop_api != 'undefined') {
                jcrop_api.destroy();
                jcrop_api = null;
                $('#preview').width(oImage.naturalWidth);
                $('#preview').height(oImage.naturalHeight);
            }
  
                $('#preview').Jcrop({

                    aspectRatio: 2,
                    bgFade: true,
                    bgOpacity: .3, 
                    onChange: updateInfo,
                    onSelect: updateInfo
                }, function () {
 
                    //var bounds = this.getBounds();
                    //var boundx = bounds[0];
                    //var boundy = bounds[1];

                    // Store the Jcrop API in the jcrop_api variable
                    jcrop_api = this;
                });
        };
    };
    oReader.readAsDataURL(oFile);
}

تابع fileSelectHandler

function fileSelectHandler() {
    var oFile = $('#image_file')[0].files[0];
    $('.error').hide();
    var rFilter = /^(image\/jpeg|image\/png)$/i;
    if (!rFilter.test(oFile.type)) {
        $('.error').html('فقط تصویر معتبر انتخاب نمایید').show();
        return;
    }
این تابع که مستقیما به input file رویداد Onchange متصل است. فایل انتخابی آن را خوانده و نوع تصویر را بررسی میکند. اگر تصویر از نوع Jpeg یا Png نباشد، یک خطا را به کاربر نشان میدهد و با return شدن از ادامه کد جلوگیری میکنیم.
در ادامه همین تابع بالا، کدهای زیر را اضافه می‌کنیم:
   var oImage = document.getElementById('preview');
    var oReader = new FileReader();
    oReader.onload = function (e) {

        oImage.src = e.target.result;
        oImage.onload = function () {
       
            $('.step2').fadeIn(500);

            var sResultFileSize = bytesToSize(oFile.size);
            $('#filesize').val(sResultFileSize);
            $('#filetype').val(oFile.type);
            $('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);
   
            if (typeof jcrop_api != 'undefined') {
                jcrop_api.destroy();
                jcrop_api = null;
                $('#preview').width(oImage.naturalWidth);
                $('#preview').height(oImage.naturalHeight);
            }
            $('#preview').Jcrop({

                aspectRatio: 2,
                bgFade: true, 
                bgOpacity: .3, 
                onChange: updateInfo,
                onSelect: updateInfo,
                onRelease: clearInfo
            }, function () {

                //var bounds = this.getBounds();
                //var boundx = bounds[0];
                //var boundy = bounds[1];

                jcrop_api = this;
            });
        };
    };
    oReader.readAsDataURL(oFile);

FileReader یکی از توابع موجود در HTML است که مستندات آن در سایت موزیلا موجود است و قابلیت خواندن غیرهمزمان فایلها و اشیا Blob را دارد. در خط آخر به عنوان پارامتر ما فایلی را که در آپلودر خوانده ایم و در مرحله قبل نوع فایل آن را بررسی کردیم، پاس میکنیم و باعث می‌شود که رویداد Load شیء FileReader صدا زده شود.
در این رویداد ابتدا اطلاعات این فایل را از قبیل سایز و ابعاد و نوع فایل، خوانده و در همان تگ Div که با کلاس info تعیین شده بود، نمایش می‌دهیم. سپس متغیر jcrop_api  را که به صورت global در بالای تابع صدا زدیم، بررسی میکنم که آیا از قبل پر شده‌است یا خیر؟ اگر از قبل پرشده‌است باید شیء Jcrop را که به آن اعمال شده است، نابود و آن را نال کنیم تا برای تصویر جدید آماده شود. این کد زمانی کاربرد دارد که کاربر از تصویر قبلی انصراف داده‌است و تصویر جدیدی را انتخاب نموده است یا اینکه عملیات دارد به صورت ایجکسی پیاده می‌شود. اگر عملیات نابودی روی این پلاگین صورت نگیرد، برای مرتبه دوم کار نخواهد کرد.


سپس پلاگین جی‌کوئری Jcrop را بر روی آن اعمال می‌کنیم. در پرامتر اول یک سری تنظیمات اولیه را انجام می‌دهیم که در ادامه با آن آشنا می‌شویم و در پارامتر دوم یک callback را به آن پاس میکنیم تا بعد از آماده شدن پلاگین اجرا شود که در آن شیء جدید ایجاد شده یعنی this را در متغیری به اسم jcrop_api دخیره میکنیم تا در بررسی‌های آتی که در بند بالا توضیح داده شد، در دسترس داشته باشیم. همچنین در این تابع شما می‌توانید اندازه تصویر انتخابی را نیز داشته باشید.

این پلاگین شامل option‌های متفاوتی در پارامتر اول است که آن‌ها را بررسی می‌کنیم:
MinSize : شما میتوانید حداقل پهنا و ارتفاعی را برای برش زدن تصویر در نظر بگیرید.
minSize:[40,20]
پارامتر دوم aspectRatio جهت نگهداری تناسب بین پهنا و ارتفاع می‌باشد. تنظیم زیر برای تناسب 3 به دو می‌باشد.
aspectRatio:1.5
bgFade اگر با true مقدار دهی شود عملیات برش در حین تاریک شدن صفحه (محلی که جزء برش نیست) و یا بالعکس آن با یک Fade صورت خواهد گرفت.
bgOpacity از 0 تا یک مقدار میگیرد و میزان opacity محل‌های تاریک را تعیین می‌کند. همچنین شامل سه رویداد onSelect,onChange,onrelease هم می‌باشد که به ترتیب در موارد زیر رخ میدهند:
ناحیه مورد نظر انتخاب شد.
ناحیه مورد نظر در حالت انتخاب است و ماوس در حال درگ شدن است و با هر حرکتی ماوس اجرا می‌گردد.
ناحیه انتخابی از حالت انتخاب خارج شد.


دو رویداد اول یعنی onchange و onSelect را برای به روزسانی فیلدهای مخفی و مختصات استفاده میکنیم:

function updateInfo(e) {
    $('#x1').val(e.x);
    $('#y1').val(e.y);
    $('#x2').val(e.x2);
    $('#y2').val(e.y2);
};

این مختصات از طریق یک پارامتر به آن‌ها پاس می‌شود. به غیر از این چهار عدد مختصات می‌توانید با استفاده از متغیرهای w و h هم اندازه پهنا و ارتفاع محل برش خورده را نیز به دست آورید. هر چند که این اعداد، از تفریق خود مختصات هم به دست می‌آیند.
یک تابع جزئی دیگر هم در این فایل وجود دارد که حین نمایش اندازه تصویر، واحد نمایش مناسب آن را برای ما انتخاب میکند:
function bytesToSize(bytes) {
    var sizes = ['بایت', 'کیلو بایت', 'مگابایت'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};

بعد از اینکه کدهای سمت کلاینت را تمام کردیم لازم است با نحوه برش تصویر در سمت سرور هم آشنا شویم:
public static byte[] Resize(this byte[] byteImageIn, int x1,int y1,int x2,int y2)
        {

            ImageConverter ic = new ImageConverter();
            Image src = (Image)(ic.ConvertFrom(byteImageIn)); 

            Bitmap target = new Bitmap(x2 - x1, y2 - y1);
            using (Graphics graphics = Graphics.FromImage(target))
                graphics.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
                    new Rectangle(x1,y1,x2-x1,y2-y1), 
                    GraphicsUnit.Pixel);
            src = target;
            using (var ms = new MemoryStream())
            {
                src.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                return ms.ToArray();
            }

 }

از آنجا که ما تصاویر را در دیتابیس به صورت آرایه‌ای از بایت‌ها ذخیره میکنیم، extension method ذکر شده در بالا تصویر را در حالت آرایه‌ای از بایت‌ها برش می‌دهد. بدیهی که بسته به نیاز شما کد بالا دست خوش تغییراتی خواهد شد. ابتدا تصویر باینری را به شی Image تبدیل میکنیم و یک شیء Bitmap جدید را به عنوان بوم خالی و به اندازه کادر برش ایجاد می‌کنیم تا تصویر برش خورده در آن قرار بگیرد و سپس توسط متد DrawImage میخواهیم که تصویر مبدا را با مختصات شیء Rectangle از نقطه 0 و 0 بوم آغاز کرده و تا انتهای آن شروع به ترسیم کند. سپس آن را ذخیره و مجددا در قالب همان آرایه‌ای از بایت‌ها بر می‌گردانیم.

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

پروژه‌ها
پَرباد - اتصال و پیاده‌سازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)
پَرباد یک کتابخانه رایگان و اوپن سورس است که امکان افزودن قابلیت پرداخت آنلاین را به وب اپلیکیشن‌ها محیا میکند.

مزایا و ویژگی‌ها
  • نصب آسان با استفاده از Nuget
  • بدون نیاز به هیچگونه وب سرویس و یا دانش پیاده سازی سیستم‌های پرداخت آنلاین 
  • پشتیبانی از درگاه‌های: ملت، ملی (سداد)، پارسیان، پاسارگاد، ایران کیش، سامان و آسان پرداخت ، زرین پال، پی آی آر و آی دی پی
  • انجام پرداخت، فقط با نوشتن ۳ خط کد
  • طراحی کاملا یکپارچه برای انجام عملیات پرداخت با تمامی بانک‌ها
  • رعایت نکات امنیتی پرداخت آنلاین
  • درگاه مجازی، برای شبیه سازی عملیات پرداخت 
  • امکان استفاده از پروکسی برای سرور‌های خارج از ایران در صورت نیاز
  • استفاده از تکنولوژی‌های مدرن و استاندارد
  • قابل نصب بر روی پروژه‌های: ASP.NET Core, ASP.NET MVC, ASP.NET WebForms