نظرات مطالب
مقابله با XSS ؛ یکبار برای همیشه!
سلام. مدل برنامه من به شرح زیر است:
 public class Post
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int PostId { get; set; }
        [Required(ErrorMessage = "فیلد عنوان الزامی است")]
        [Display(Name = "عنوان پست")]
        public string PostTitle { get; set; }
        [Display(Name = "محتوا")]
        [AllowHtml]
        public string Content { get; set; }
        [DataType(DataType.Date)]
        [Display(Name = "تاریخ نشر")]
        public DateTime PublishDate { get; set; }
    }
و برای فیلد Content یک TextArea قرار دادم که به Froala متصل شده و برای ذخیره اکشن زیر رو نوشتم
[HttpPost]

        public ActionResult CreateNewPost(Post post)
        {
            if (ModelState.IsValid)
            {
                post.Content = post.Content.ToSafeHtml();
                PostData.AddNewPost(post);
                return RedirectToAction("Index", "Home", new { area = "" });
            }
            return View(post);
        }
مشکل اینجاست که وقتی از Froala استفاده میکنم تابع ToSafeHtml از کتابخانه AntiXss مایکروسافت و ToSafeHtml مطرح شده در این بحث هیچ تاثیری روی فیلد Content نداره اما وقتی Froala را با TextArea جایگزین میکنم همه چی درست میشه. میخواستم ببینم چطور میشه این مشکل رو برطرف کرد؟
نظرات مطالب
EF Code First #2
ممنون از پاسخ دهی شما، ولی فکر کنم منظورم رو بد رسوندم، من دقیقا می خوام برام Alter Table بزنه، نه این که کاری به دیتابیس نداشته باشه، ( مثل NHibernate )

آیا با Migration ها چنین چیزی به سادگی ( در حد چند خط کد قابل استفاده برای کل برنامه )، امکانپذیر هست یا خیر ؟
متاسفانه من همچین چیزی ندیدم  تو امکاناتش
نظرات مطالب
استفاده از اسمبلی‌های دات نت 2 در یک پروژه دات نت 4
- یک نکته‌ی دیگر:
اگر حین کار با NHibernate و SQLite در یک برنامه‌ی دات نت 4 به خطای "Could not create the driver from NHibernate.Driver.SQLite20Driver" برخوردید، راه حل همان مورد فوق است (اضافه کردن useLegacyV2RuntimeActivationPolicy به فایل کانفیگ برنامه).
مطالب
توسعه کنترلر و مدل در F# MVC4
در پست قبلی با F# MVC4 Template آشنا شدید. در این پست به توسعه کنترلر و مدل در قالب مثال خواهم پرداخت. برای شروع ابتدا یک پروژه همانند مثال ذکر شده در پست قبلی ایجاد کنید. در پروژه #C ساخته شده که صرفا برای مدیریت View‌ها است یک View جدید به صورت زیر ایجاد نمایید:
@model IEnumerable<FsWeb.Models.Book>
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" 
        href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
</head>
 
<body>
    <div data-role="page" data-theme="a" id="booksPage">
        <div data-role="header">
            <h1>Guitars</h1>
        </div>
        <div data-role="content">
        <ul data-role="listview" data-filter="true" data-inset="true"> 
            @foreach(var x in Model) {
                <li><a href="#">@x.Name</a></li>
            }
        </ul>
        </div>
    </div>
    <script src="http://code.jquery.com/jquery-1.6.4.min.js">
    </script>
    <script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js">
    </script>

    <script>
        $(document).delegate("#bookPage", 'pageshow', function (event) {
            $("div:jqmData(role='content') > ul").listview('refresh');
        });
    </script>
</body>
</html>
از آنجا که هدف از این پست آشنایی با بخش #F پروژه‌های وب است در نتیجه نیازی به توضیح کد‌های بالا دیده نمی‌شود. 
برای ساخت کنترلر جدید، در پروژه #F ساخته شده یک Source File ایجاد نمایید و کد‌های زیر را در آن کپی نمایید:
namespace FsWeb.Controllers

open System.Web.Mvc
open FsWeb.Models

[<HandleError>]
type BooksController() =
    inherit Controller()
    member this.Index () =   
        seq { yield  Book(Name = "My F# Book")
              yield Book(Name = "My C# Book") }
        |> this.View
در کد‌های بالا ابتدا یک کنترلر به نام BookController ایجاد کردیم که از کلاس Controller ارث برده است(با استفاده از inherit).  سپس یک تابع به نام Index داریم(به عنوان Action مورد نظر در کنترلر) که آرایه ای از کتاب‌ها به عنوان پارامتر به تابع View می‌فرستد.( توسط اپراتور Pipe - <|). در نهایت دستور this.View  معادل فراخوانی اکشن ()View در پروژه‌های #C است که View متناظر با اکشن را فراخوانی می‌کند. همان طور که ملاحظه می‌نمایید بسیار شبیه به پیاده سازی #C است.
اما نکته ای که در مثال بالا وجود دارد این است که دو نمونه از نوع Book را برای ساخت seq وهله سازی می‌کند. در نتیجه باید Book Type را به عنوان مدل تعریف کنیم. به صورت زیر:
namespace FsWeb.Models
 type Book = { Id : Guid; Name : string }
البته در F# 3.0 امکانی فراهم شده است به نام Auto-Properties که شبیه تعریف خواص در #C است. در نتیجه می‌توان تعریف بالا را به صورت زیر نیز بازنویسی کرد:
namespace FsWeb.Models 
type Book() = member val Name = "" with get, set
Attribute‌ها مدل
اگر همچون پروژه‌های #C قصد دارید با استفاده از Attribute‌ها مدل خود را اعتبارسنجی نمایید می‌توانید به صورت زیر اقدام نمایید:
open System.ComponentModel.DataAnnotations
 type Book() = [<Required>] member val Name = "" with get, set
هم چنین می‌توان Attribute‌های مورد نظر برای مدل EntityFramework را نیز اعمال نمود(نظیر Key):
namespace FsWeb.Models 
open System 
open System.ComponentModel.DataAnnotations 
type Book() = [<Key>] member val Id = Guid.NewGuid() with get, set 
[<Required>] member val Name = "" with get, set

نکته: دستور open معادل با using در #C است.
در پست بعدی برای تکمیل مثال جاری، روش طراحی Repository با استفاده از EntityFramework بررسی خواهد شد.
مطالب
نصب یک اسمبلی دات نت در GAC

افزونه فارسی به پارسی را قبل از ارائه در سایت، بر روی یک ماشین مجازی هم تست کردم. برای این منظور از Microsoft virtual pc استفاده شد. البته در مقابل امکانات VMware شاید حرفی برای گفتن نداشته باشد ولی خوب جهت مقاصد تست نرم افزار بر روی یک سیستم عاری از وسایل برنامه نویسی مناسب است. (برای نصب یک سیستم عامل توسط آن برای مثال می‌شود از سی دی آن OS یک فایل ISO تهیه کرد و مسیر این فایل ISO را به ماشین مجازی معرفی کرد . سپس سیستم بوت شده و روال نصب مطابق معمول خواهد بود)
اولین مشکلی که پس از تست بر روی سیستم مجازی رخ داد، پیغام یافت نشدن اسمبلی مربوط به SQLite بود. نرم افزار word هنگام اجرای افزونه‌های دات نت، آنها را در مسیری با یک نامگذاری منحصربفرد کپی می‌کند و تنها هم همان اسمبلی افزونه را کپی می‌کند و نه سایر موارد همراه را. برای پیدا کردن این مسیر می‌شود از روش زیر استفاده کرد:
using System.Reflection;
Assembly.GetExecutingAssembly().Location

در این مسیر اسمبلی SQLite وجود ندارد و به همین دلیل هم بارگذاری نخواهد شد. بهترین راه حل برای رفع این مشکل، نصب اسمبلی مربوطه در GAC یا global assembly cache است.
برای نصب اسمبلی در GAC استفاده از برنامه gacutil توصیه شده است. این برنامه به همراه SDK دات نت فریم ورک ارائه می‌شود و الزامی ندارد که کاربر نهایی این برنامه را داشته باشد. خوشبختانه با استفاده از برنامه نویسی هم می‌شود یک نمونه از برنامه Gacutil را خودمان ایجاد کنیم (برای مثال ایجاد یک برنامه کنسول و دریافت مسیر از طریق آرگومان‌های ارسالی به آن):

new System.EnterpriseServices.Internal.Publish().GacInstall(path);

در اینجا باید ارجاعی از System.EnterpriseServices نیز به برنامه اضافه شود.
این روش در مورد اسمبلی SQLite که دارای امضای دیجیتال است کار خواهد کرد. اما اگر قصد داشته باشید به صورت عمومی از آن استفاده کنید، باید ابتدا بررسی کرد که آیا فایل اسمبلی دارای امضای دیجیتال است یا خیر. برای این منظور می‌توان مقدار عبارت زیر را ارزیابی کرد:
Assembly.LoadFile(path).GetName().GetPublicKey().Length

اگر این طول بزرگتر از صفر بود به این معنا است که فایل اسمبلی دارای امضای دیجیتال است و می‌توان آنرا در GAC نصب کرد.
لازم به ذکر است که متد معرفی شده برای نصب در GAC در صورت عدم موفقیت هیچ پیغام خطا یا exception ایی را در برنامه تولید نخواهد کرد. اما پیغام خطای حاصل را در event log ویندوز می‌توان مشاهده کرد.


نظرات مطالب
پیاده سازی پروژه نقاشی (Paint) به صورت شی گرا 3#
با اجازه دوست عزیزم مهندس فتح الهی
من به نظرم Helpers رو اگه به شکل زیر Re factor کنیم بهتر باشه :)
اول یه کلاس تعریف می‌کنیم و اطلاعات لازم برای ترسیم پیش نمایش رو تو اون کلاس میزاریم
public class ShapeSpecification
    {
        public PointF StartPoint{get;set;}
        public PointF EndPoint{get;set;}
        public Color ForeColor{get;set;}
        public byte Thickness{get;set;}
        public bool IsFill{get;set;}
        public Brush BackgroundBrush{get;set;}
    }
یه کلاس دیگه هم نقاط ابتدا و انتها و طول و عرض رو تو خودش داره
public class StartPoints
    {
        public float XPoint { get; set; }
        public float YPoint { get; set; }
        public float Width { get; set; }
        public float Height { get; set; }
    }
حالا یه اینترفیس تعریف می‌کنیم که فقط یه متد داره به نام   Draw
 public interface IPeiview
    {
        void Draw(ShapeSpecification shapeScepification);
    }
حالا می‌رسیم به کلاس Helpers اصلیمون که میتونه هم استاتیک باشه و هم معمولی به دو شکل زیر
public class Helpers
    {
        private readonly IPeiview peiview;

        public Helpers(IPeiview peiview)
        {
            this.peiview = peiview;
        }

        public void Draw(ShapeSpecification shapeSpecification)
        {
            peiview.Draw(shapeSpecification);
        }
    }
 public static  class Helpers
    {

        public static void Draw(ShapeSpecification shapeSpecification, IPeiview peiview)
        {
            peiview.Draw(shapeSpecification);
        }
    }
که فقط یه متد ساده Draw داره و اونم تابع Draw اینترفیسی که بش دادیم رو صدا میزینه
یه کلاس دیگه هم تعریف میکنیم که مسئولیتش تشخیص بوم‌های چهارگانه است برای شروع نقطه‌ی ترسیم
public static class AreaParser
    {
        public static StartPoints Parse(PointF startPoint, PointF endPoint)
        {
            var startPoints = new StartPoints();

            startPoints.Width = Math.Abs(endPoint.X - startPoint.X);
            startPoints.Height = Math.Abs(endPoint.Y - startPoint.Y);

            if (startPoint.X <= endPoint.X && startPoint.Y <= endPoint.Y)
            {
                startPoints.XPoint = startPoint.X;
                startPoints.YPoint = startPoint.Y;
            }

            else if (startPoint.X >= endPoint.X && startPoint.Y >= endPoint.Y)
            {
                startPoints.XPoint = endPoint.X;
                startPoints.YPoint = endPoint.Y;
            }

            else if (startPoint.X >= endPoint.X && startPoint.Y <= endPoint.Y)
            {
                startPoints.XPoint = endPoint.X;
                startPoints.YPoint = startPoint.Y;
            }

            else if (startPoint.X <= endPoint.X && startPoint.Y >= endPoint.Y)
            {
                startPoints.XPoint = startPoint.X;
                startPoints.YPoint = endPoint.Y;
            }
            return startPoints;
        }
    }
نکته: این کلاس رو اگه با Func ایجاد کنیم خیلی بهتر و تمیزتر  وقشنکتر هم میشد که من می‌گزرم فعلا ازش
حالا ما هر شکل جدید که اضافه کنیم به پروژه Paint خودمون و قصد پیش نمایش اونو داشته باشیم فقط کافیه یه کلاس برا پیش نمایشش ایجاد کنیم که کلاس Ipreview رو Implement کنه و متد Draw مخصوص به خودش را داشته باشه و از شر Swith‌های طولانی خلاص میشیم مثلا من برای دایره اینکارو کردم
public class CirclePreview:IPeiview
    {
        private readonly Graphics graphics;

        public CirclePreview(Graphics graphics)
        {
            this.graphics = graphics;
        }
        public void Draw(ShapeSpecification shapeScepification)
        {
            var startPoints = AreaParser.Parse(shapeScepification.StartPoint, shapeScepification.EndPoint);

            float raduis = Math.Max(startPoints.Width, startPoints.Height);

            if (shapeScepification.IsFill)
                this.graphics.FillEllipse(shapeScepification.BackgroundBrush, startPoints.XPoint, startPoints.YPoint, raduis, raduis);
            else
                this.graphics.DrawEllipse(new Pen(shapeScepification.ForeColor, shapeScepification.Thickness), startPoints.XPoint, startPoints.YPoint, raduis, raduis);
        }
    }


مطالب
Message Header سفارشی در WCF
فرض کنید در حال توسعه یک سیستم مبتنی بر WCF هستید. بنابر نیاز باید  یک سری اطلاعات مشخص در اکثر درخواست‌های بین سرور و کلاینت ارسال شوند یا ممکن است بعد از انجام بیش از 50 درصد پروژه این نیاز به وجود آید که  یک یا بیش از یک  پارامتر (که البته از سمت کلاینت تامین خواهند شد) در اکثر کوئری‌های گرفته شده سمت سرور شرکت داده شوند. خوب! در این وضعیت علاوه بر حس همدردی با اعضای تیم توسعه دهنده این پروژه چه می‌توان کرد؟
»اولین راه حلی که به ذهن می‌رسد این است که پارامتر‌های مشخص شده را در متد‌های سرویس‌های مورد نظر قرار داد و به نوعی تمام سرویس‌ها را به روز رسانی کرد. این روش به طور قطع در خیلی از قسمت‌های پروژه به صورت مستقیم اثرگذار خواهد بود و در صورت نبود ابزار‌های تست ممکن است با مشکلات جدی روبرو شوید.
»راه حل دوم این است که یک Message Header سفارشی بسازیم و در هر درخواست اطلاعات مورد نظر را در هدر قرار داده و سمت سرور این اطلاعات را به دست آوریم. این روش کمترین تغییر مورد نظر را برای پروژه دربر خواهد داشت و از طرفی نیاز متد‌های سرویس به پارامتر را از بین می‌برد و دیگر نیازی نیست تا تمام متد‌های سرویس‌ها دارای پارامتر‌های یکسان باشند.
پیاده سازی
برای شروع کلاس مورد نظر برای ارسال اطلاعات را به صورت زیر خواهیم ساخت:
 [DataContract]
    public class ApplicationContext
    {
        [DataMember( IsRequired = true )]
        public string UserId
        {
            get {  return _userId; }
            set
            {
                _userId = value;
            }
        }
        private string _userId;      

        [DataMember( IsRequired = true )]
        public static ApplicationContext Current
        {
            get
            {
                return _current;
            }
            private set { _current = value; }
        }
        private static ApplicationContext _current;  
 public static void Register( ApplicationContext appContext ) { Current = appContext; IsRegistered = true; } }
در این کلاس به عنوان نمونه مقدار Id کاربر جاری باید در هر درخواست به سمت سرور ارسال شود. حال نیاز به یک MessageInspector داریم ، کافیست که اینترفیس IClientMessageInspector را توسط یک کلاس به صورت زیر پیاده سازی نماییم:
public class ClientMessageHeaderInspector<T> : IClientMessageInspector
    {
        private readonly T _vaccine;

        public ClientMessageHeaderInspector( T vaccine )
        {
            this._vaccine = vaccine;
        }

        public void AfterReceiveReply( ref Message reply, object correlationState )
        {
        }

        public object BeforeSendRequest( ref Message request, IClientChannel channel )
        {
            MessageHeader messageHeader = MessageHeader.CreateHeader( typeof( T ).Name, typeof( T ).Namespace, this._vaccine );
            request.Headers.Add( messageHeader );
            return null;
        }
    }
نوع T مورد استفاده برای تعیین نوع داده ارسالی سمت سرور است که در این مثال کلاس ApplicationContext خواهد بود. در متد BeforeSendRequest باید Header سفارشی را ساخته و آن را به هدر درخواست اضافه نماییم. حال باید MessageInspector ساخته شده بالا را با استفاده از IEndPointBehavior به MessageInspcetor‌های نمونه ساخته شده از ClientRuntime اضافه نماییم. برای این کار به صورت زیر عمل می‌نماییم:
public class ApplicationContextMessageBehavior : IEndpointBehavior
    {
        ClientMessageHeaderInspector<ApplicationContext> inspector = null;

        public ApplicationContextMessageBehavior()
        {
            inspector = new ClientMessageHeaderInspector<ApplicationContext>( ApplicationContext.Current );
        }

        public void AddBindingParameters( ServiceEndpoint endpoint, BindingParameterCollection bindingParameters )
        {
        }

        public void ApplyClientBehavior( ServiceEndpoint endpoint, ClientRuntime clientRuntime )
        {
            clientRuntime.MessageInspectors.Add( inspector );
        }

        public void ApplyDispatchBehavior( ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher )
        {
        }

        public void Validate( ServiceEndpoint endpoint )
        {          
        }
    }
همان طور که می‌بینید در کلاس بالا یک نمونه از کلاس ClientMessageInspector را بر اساس ApplicationContext می‌سازیم و در متد ApplyClientBehavior به نمونه clientRuntime اضافه می‌نماییم. اگر دقت کرده باشید می‌توان هر تعداد MessageInspector را به clientRunTime اضافه کرد.
در مرحله آخر باید تنظیمات مربوط به ChannelFactory را انجام دهیم.
public class ServiceMapper<TChannel>
    {      
        internal static EndpointAddress EPAddress
        {
            get
            {
                return _epAddress;
            }
        }
        private static EndpointAddress _epAddress;

        public static TChannel CreateChannel( Binding binding, string uriBase, string serviceName, bool setCredential )
        {
            _epAddress = new EndpointAddress( String.Format( "{0}{1}", uriBase, serviceName ) );

            var factory = new ChannelFactory<TChannel>( binding, _epAddress );        
         
           ApplicationContext.Register( new ApplicationContext
            {
                UserId = Guid.NewGuid()
            } );  
factory.Endpoint.Behaviors.Add( new ApplicationContextMessageBehavior() ); TChannel proxy = factory.CreateChannel(); if ( factory.Endpoint.Behaviors.OfType<ApplicationContextMessageBehavior>().Any() ) { using ( var scope = new OperationContextScope( ( IClientChannel )proxy ) ) { OperationContext.Current.OutgoingMessageHeaders.Add( MessageHeader.CreateHeader( typeof( ApplicationContext ).Name, typeof( ApplicationContext ).Namespace, ApplicationContext.Current ) ); } } return proxy; }
چند نکته:
»در متد CreateChannel، ابتدا تنظیمات مربوط به EndPointAddress و ChannelFactory انجام می‌شود. سپس یک نمونه از کلاس ApplicationContext را  توسط متد Register به کلاس مورد نظر رجیستر می‌کنیم. به این ترتیب مقدار خاصیت Current در کلاس ApplicationContext برابر با نمونه ساخته شده می‌شود. سپس کلاس ApplicationContextMessageBehavior به خاصیت Behavior در ChannelFactory  اضافه می‌شود. در انتها نیز هدر سفارشی ساخته شده به MessageHeader‌های نمونه جاری OperationContext اضافه می‌شود. این عمل توسط کد زیر انجام می‌گیرد:
OperationContext.Current.OutgoingMessageHeaders.Add( MessageHeader.CreateHeader( typeof( ApplicationContext ).Name, typeof( ApplicationContext ).Namespace, AppConfiguration.Application ) );
از این پس هر درخواستی که از سمت کلاینت به سمت سرور ارسال شود به همراه خود یک نمونه از کلاس ApplicationContext را خواهد داشت. فقط دقت داشته باشید که برای ساخت ChanelFactory باید همیشه از متد CreateChannel استفاده نمایید.
استفاده از هدر سفارشی سمت سرور

حال قصد داریم که اطلاعات مورد نظر را از هدر درخواست در سمت سرور به دست آورده و از آن در کوئری‌های خود استفاده نماییم. کد زیر این کار را برای ما انجام می‌دهد:
 if ( OperationContext.Current != null && OperationContext.Current.IncomingMessageHeaders.FindHeader( typeof( ApplicationContext ).Name , typeof( ApplicationContext ).Namespace ) > 0 )
            {
                _application = OperationContext.Current.IncomingMessageHeaders.GetHeader<ApplicationContext>( typeof( ApplicationContext ).Name , typeof( ApplicationContext ).Namespace );
            }
متد FindHeader در خاصیت IncomingMessageHeader با استفاده از نام و فضای نام به دنبال هدر سفارشی می‌گردد. اگر خروجی متد از 0 بیشتر بود بعنی هدر مورد نظر موجود است. در پایان نیز با استفاده از متد GetHeader، نمونه ساخته شده کلاس ApplicationContext را به دست می‌آوریم.

مطالب
معرفی یک ابزار گزارشگیری رایگان مخصوص WPF

تا صحبت از گزارشگیری به میان بیاید احتمالا معرفی ابزارهای تجاری مانند Reporting services ، کریستال ریپورت، stimulsoft.com ، fast-report.com و امثال آن درصدر لیست توصیه کنندگان و مشاوران قرار خواهند داشت. اما خوب برای ایجاد یک گزارشگیری ساده حتما نیازی نیست تا به این نوع ابزارهای تجاری مراجعه کرد. ابزار رایگان و سورس باز جالبی هم در این باره جهت پروژه‌های WPF در دسترس است:



در ادامه در طی یک مثال قصد داریم از این کتابخانه استفاده کنیم:

1) تنظیم وابستگی‌ها
پس از دریافت کتابخانه فوق، ارجاعات زیر باید به پروژه شما اضافه شوند:
CodeReason.Reports.dll (از پروژه فوق) و ReachFramework.dll (جزو اسمبلی‌های استاندارد دات نت است)

2) تهیه منبع داده‌ گزارش
کتابخانه‌ی فوق به صورت پیش فرض با DataTable‌ کار می‌کند. بنابراین کوئری‌های شما یا باید خروجی DataTable داشته باشد یا باید از یک سری extension methods برای تبدیل IEnumerable به DataTable استفاده کرد (در پروژه پیوست شده در پایان مطلب، این موارد موجود است).
برای مثال فرض کنید می‌خواهیم رکوردهایی را از نوع کلاس Product زیر در گزارش نمایش دهیم:

namespace WpfRptTests.Model
{
public class Product
{
public string Name { set; get; }
public int Price { set; get; }
}
}
3) تعریف گزارش
الف) اضافه کردن فایل تشکیل دهنده ساختار و ظاهر گزارش
گزارش‌‌های این کتابخانه مبتنی است بر اشیاء FlowDocument استاندارد WPF . بنابراین از منوی پروژه گزینه‌ی Add new item در قسمت WPF آن یک FlowDocument جدید را به پروژه اضافه کنید ( باید دقت داشت که Build action این فایل باید به Content تنظیم گردد). ساختار ابتدایی این FlowDocument به صورت زیر خواهد بود که به آن FlowDirection و FontFamily مناسب جهت گزارشات فارسی اضافه شده است. همچنین فضای نام مربوط به کتابخانه‌ی گزارشگیری CodeReason.Reports نیز باید اضافه گردد.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="RightToLeft" FontFamily="Tahoma"
xmlns:xrd="clr-namespace:CodeReason.Reports.Document;assembly=CodeReason.Reports"
PageHeight="29.7cm" PageWidth="21cm" ColumnWidth="21cm">

</FlowDocument>

مواردی که در ادامه ذکر خواهند شد محتوای این گزارش را تشکیل می‌دهند:
ب) مشخص سازی خواص گزارش

<xrd:ReportProperties>
<xrd:ReportProperties.ReportName>SimpleReport</xrd:ReportProperties.ReportName>
<xrd:ReportProperties.ReportTitle>گزارش از محصولات</xrd:ReportProperties.ReportTitle>
</xrd:ReportProperties>
در اینجا ReportName و ReportTitle باید مقدار دهی شوند (دو dependency property که در کتابخانه‌ی CodeReason.Reports تعریف شده‌اند)

ج) مشخص سازی Page Header و Page Footer
اگر می‌خواهید عباراتی در بالا و پایین تمام صفحات گزارش تکرار شوند می‌توان از SectionReportHeader و SectionReportFooter این کتابخانه به صورت زیر استفاده کرد:
    <xrd:SectionReportHeader PageHeaderHeight="2" Padding="10,10,10,0" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportHeader>

<xrd:SectionReportFooter PageFooterHeight="2" Padding="10,0,10,10" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
نام کاربر:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
صفحه
<xrd:InlineContextValue PropertyName="PageNumber" FontWeight="Bold" /> از
<xrd:InlineContextValue PropertyName="PageCount" FontWeight="Bold" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportFooter>

دو نکته در اینجا حائز اهمیت هستند: xrd:InlineDocumentValue و xrd:InlineContextValue
InlineDocumentValue را می‌توان در کد‌های برنامه به صورت سفارشی اضافه کرد. بنابراین هر جایی که نیاز بود مقدار ثابتی از طریق کد نویسی به گزارش تزریق و اضافه شود می‌توان از InlineDocumentValue استفاده کرد. برای مثال در کدهای ViewModel برنامه که در ادامه ذکر خواهد شد دو مقدار PrintDate و RptBy به صورت زیر تعریف و مقدار دهی شده‌اند:
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");
برای مشاهده مقادیر مجاز مربوط به InlineContextValue به فایل ReportContextValueType.cs سورس کتابخانه مراجعه کنید که شامل PageNumber, PageCount, ReportName, ReportTitle است و توسط CodeReason.Reports به صورت پویا تنظیم خواهد شد.

د) مشخص سازی ساختار تولیدی گزارش

<Section Padding="80,10,40,10" FontSize="12">
<Paragraph FontSize="24" TextAlignment="Center" FontWeight="Bold">
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
<Paragraph TextAlignment="Center">
گزارش از لیست محصولات در تاریخ:
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
توسط:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
<xrd:SectionDataGroup DataGroupName="ItemList">
<Table CellSpacing="0" BorderBrush="Black" BorderThickness="0.02cm">
<Table.Resources>
<!-- Style for header/footer rows. -->
<Style x:Key="headerFooterRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Background" Value="LightGray"/>
</Style>

<!-- Style for data rows. -->
<Style x:Key="dataRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontSize" Value="12"/>
</Style>

<!-- Style for data cells. -->
<Style TargetType="{x:Type TableCell}">
<Setter Property="Padding" Value="0.1cm"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="0.01cm"/>
</Style>
</Table.Resources>

<Table.Columns>
<TableColumn Width="0.8*" />
<TableColumn Width="0.2*" />
</Table.Columns>
<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>نام محصول</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>قیمت</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

<TableRowGroup Style="{StaticResource dataRowStyle}">
<xrd:TableRowForDataTable TableName="Product">
<TableCell>
<Paragraph>
<xrd:InlineTableCellValue PropertyName="Name" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<xrd:InlineTableCellValue PropertyName="Price" AggregateGroup="Group1" />
</Paragraph>
</TableCell>
</xrd:TableRowForDataTable>
</TableRowGroup>

<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Right">
<Bold>جمع کل</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" />
</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

</Table>

<Paragraph TextAlignment="Center" Margin="5">
در این گزارش
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Count"
EmptyValue="هیچ"
FontWeight="Bold" /> محصول با جمع کل قیمت
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" /> وجود دارند.
</Paragraph>
</xrd:SectionDataGroup>
</Section>
برای اینکه بتوان این قسمت‌ها را بهتر توضیح داد، نیاز است تا تصاویر مربوط به خروجی این گزارش نیز ارائه شوند:




در ابتدا توسط دو پاراگراف، عنوان گزارش و یک سطر زیر آن نمایش داده شده‌اند. بدیهی است هر نوع شیء و فرمت مجاز در FlowDocument را می‌توان در این قسمت نیز قرار داد.
سپس یک SectionDataGroup جهت نمایش لیست آیتم‌ها اضافه شده و داخل آن یک جدول که بیانگر ساختار جدول نمایش رکوردهای گزارش می‌باشد، ایجاد گردیده است.
سه TableRowGroup در این جدول تعریف شده‌اند.
TableRowGroup های اولی و آخری دو سطر اول و آخر جدول گزارش را مشخص می‌کنند (سطر عناوین ستون‌ها در ابتدا و سطر جمع کل در پایان گزارش)
از TableRowGroup میانی برای نمایش رکوردهای مرتبط با نام جدول مورد گزارشگیری استفاده شده است. توسط TableRowForDataTable آن نام این جدول باید مشخص شود که در اینجا همان نام کلاس مدل برنامه است. به کمک InlineTableCellValue، خاصیت‌هایی از این کلاس را که نیاز است در گزارش حضور داشته باشند، ذکر خواهیم کرد. نکته‌ی مهم آن AggregateGroup ذکر شده است. توسط آن می‌توان اعمال جمع، محاسبه تعداد، حداقل و حداکثر و امثال آن‌را که در فایل InlineAggregateValue.cs سورس کتابخانه ذکر شده‌اند، به فیلدهای مورد نظر اعمال کرد. برای مثال می‌خواهیم جمع کل قیمت را در پایان گزارش نمایش دهیم به همین جهت نیاز بود تا یک AggregateGroup را برای این منظور تعریف کنیم.
از این AggregateGroup در سومین TableRowGroup تعریف شده به کمک xrd:InlineAggregateValue جهت نمایش جمع نهایی استفاده شده است.
همچنین اگر نیاز بود در پایان گزارش اطلاعات بیشتری نیز نمایش داده شود به سادگی می‌توان با تعریف یک پاراگراف جدید، اطلاعات مورد نظر را نمایش داد.

4) نمایش گزارش تهیه شده
نمایش این گزارش بسیار ساده است. View برنامه به صورت زیر خواهد بود:
<Window x:Class="WpfRptTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CodeReason.Reports.Controls;assembly=CodeReason.Reports"
xmlns:vm="clr-namespace:WpfRptTests.ViewModel"
Title="MainWindow" WindowState="Maximized" Height="350" Width="525">
<Window.Resources>
<vm:ProductViewModel x:Key="vmProductViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vmProductViewModel}}">
<c:BusyDecorator IsBusyIndicatorHidden="{Binding RptGuiModel.IsBusyIndicatorHidden}">
<DocumentViewer Document="{Binding RptGuiModel.Document}" />
</c:BusyDecorator>
</Grid>
</Window>

تعریف ابتدایی RptGuiModel به صورت زیر است (جهت مشخص سازی مقادیر IsBusyIndicatorHidden و Document در حین بایندینگ اطلاعات):

using System.ComponentModel;
using System.Windows.Documents;

namespace WpfRptTests.Model
{
public class RptGuiModel
{
public IDocumentPaginatorSource Document { get; set; }
public bool IsBusyIndicatorHidden { get; set; }
}
}
و این View اطلاعات خود را از ViewModel زیر دریافت خواهد نمود:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using CodeReason.Reports;
using WpfRptTests.Helper;
using WpfRptTests.Model;

namespace WpfRptTests.ViewModel
{
public class ProductViewModel
{
#region Constructors (1)

public ProductViewModel()
{
RptGuiModel = new RptGuiModel();
if (Stat.IsInDesignMode) return;
//انجام عملیات نمایش گزارش در یک ترد دیگر جهت قفل نشدن ترد اصلی برنامه
showReportAsync();
}

#endregion Constructors

#region Properties (1)

public RptGuiModel RptGuiModel { set; get; }

#endregion Properties

#region Methods (3)

// Private Methods (3)

private static List<Product> getProducts()
{
var products = new List<Product>();
for (var i = 0; i < 100; i++)
products.Add(new Product { Name = string.Format("Product{0}", i), Price = i });

return products;
}

private void showReport()
{
try
{
//Show BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = false;

var reportDocument =
new ReportDocument
{
XamlData = File.ReadAllText(@"Report\SimpleReport.xaml"),
XamlImagePath = Path.Combine(Environment.CurrentDirectory, @"Report\")
};

var data = new ReportData();

// تعریف متغیرهای دلخواه و مقدار دهی آن‌ها
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");

// استفاده از یک سری اطلاعات آزمایشی به عنوان منبع داده
data.DataTables.Add(getProducts().ToDataTable());

var xps = reportDocument.CreateXpsDocument(data);
//انقیاد آن به صورت غیر همزمان در ترد اصلی برنامه
DispatcherHelper.DispatchAction(
() => RptGuiModel.Document = xps.GetFixedDocumentSequence()
);
}
catch (Exception ex)
{
//وجود این مورد ضروری است زیرا بروز استثناء در یک ترد به معنای خاتمه آنی برنامه است
//todo: log errors
}
finally
{
//Hide BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = true;
}
}

private void showReportAsync()
{
var thread = new Thread(showReport);
thread.SetApartmentState(ApartmentState.STA); //for DocumentViewer
thread.Start();
}

#endregion Methods
}
}

توضیحات:
برای اینکه حین نمایش گزارش، ترد اصلی برنامه قفل نشود، از ترد استفاده شد و استفاده ترد به همراه DocumentViewer کمی نکته دار است:
- ترد تعریف شده باید از نوع STA باشد که در متد showReportAsync مشخص شده است.
- حین بایندیگ Document تولید شده توسط کتابخانه‌ی گزارشگیری به خاصیت Document کنترل، حتما باید کل عملیات در ترد اصلی برنامه صورت گیرد که سورس کلاس DispatcherHelper را در فایل پیوست خواهید یافت.

کل عملیات این ViewModel در متد showReport رخ می‌دهد، ابتدا فایل گزارش بارگذاری می‌شود، سپس متغیرهای سفارشی مورد نظر تعریف و مقدار دهی خواهند شد. در ادامه یک سری داده آزمایشی تولید و به DataTables گزارش ساز اضافه می‌شوند. در پایان XPS Document متناظر آن تولید شده و به کنترل نمایشی برنامه بایند خواهد شد.

دریافت سورس این مثال

مطالب دوره‌ها
تزریق خودکار وابستگی‌ها در ASP.NET Web API به همراه رها سازی خودکار منابع IDisposable
در انتهای مطلب « تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET MVC » اشاره‌ای کوتاه به روش DependencyResolver توکار Web API شد که این روش پس از بررسی‌های بیشتر (^ و ^) به دلیل ماهیت service locator بودن آن و همچنین از دست دادن Context جاری Web API، مردود اعلام شده و استفاده از IHttpControllerActivator توصیه می‌گردد. در ادامه این روش را توسط Structure map 3 پیاده سازی خواهیم کرد.

پیش نیازها
- شروع یک پروژه‌ی جدید وب با پشتیبانی از Web API
- نصب دو بسته‌ی نیوگت مرتبط با Structure map 3
 PM>install-package structuremap
PM>install-package structuremap.web

پیاده سازی IHttpControllerActivator توسط Structure map

using System;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using StructureMap;

namespace WebApiDISample.Core
{
    public class StructureMapHttpControllerActivator : IHttpControllerActivator
    {
        private readonly IContainer _container;
        public StructureMapHttpControllerActivator(IContainer container)
        {
            _container = container;
        }

        public IHttpController Create(
                HttpRequestMessage request,
                HttpControllerDescriptor controllerDescriptor,
                Type controllerType)
        {
            var nestedContainer = _container.GetNestedContainer();
            request.RegisterForDispose(nestedContainer);
            return (IHttpController)nestedContainer.GetInstance(controllerType);
        }
    }
}
در اینجا نحوه‌ی پیاده سازی IHttpControllerActivator را توسط StructureMap ملاحظه می‌کنید.
نکته‌ی مهم آن استفاده از NestedContainer آن است. معرفی آن به متد request.RegisterForDispose سبب می‌شود تا کلیه کلاس‌های IDisposable نیز در پایان کار به صورت خودکار رها سازی شده و نشتی حافظه رخ ندهد.


معرفی StructureMapHttpControllerActivator به برنامه

فایل WebApiConfig.cs را گشوده و تغییرات ذیل را در آن اعمال کنید:
using System.Web.Http;
using System.Web.Http.Dispatcher;
using StructureMap;
using WebApiDISample.Core;
using WebApiDISample.Services;

namespace WebApiDISample
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // IoC Config
            ObjectFactory.Configure(c => c.For<IEmailsService>().Use<EmailsService>());

            var container = ObjectFactory.Container;
            GlobalConfiguration.Configuration.Services.Replace(
                typeof(IHttpControllerActivator), new StructureMapHttpControllerActivator(container));


            // Web API routes
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}
 در ابتدا تنظیمات متداول کلاس‌ها و اینترفیس‌ها صورت می‌گیرد. سپس نحوه‌ی معرفی  StructureMapHttpControllerActivator را به GlobalConfiguration.Configuration.Services مخصوص Web API ملاحظه می‌کنید. این مورد سبب می‌شود تا به صورت خودکار کلیه وابستگی‌های مورد نیاز یک Web API Controller به آن تزریق شوند.


تهیه سرویسی برای آزمایش برنامه

namespace WebApiDISample.Services
{
    public interface IEmailsService
    {
        void SendEmail();
    }
}

using System;

namespace WebApiDISample.Services
{
    /// <summary>
    /// سرویسی که دارای قسمت دیسپوز نیز هست
    /// </summary>
    public class EmailsService : IEmailsService, IDisposable
    {
        private bool _disposed;

        ~EmailsService()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void SendEmail()
        {
            //todo: send email!
        }

        protected virtual void Dispose(bool disposeManagedResources)
        {
            if (_disposed) return;
            if (!disposeManagedResources) return;

            //todo: clean up resources here ...

            _disposed = true;
        }
    }
}
در اینجا یک سرویس ساده ارسال ایمیل را بدون پیاده سازی خاصی مشاهده می‌کنید.
نکته‌ی مهم آن استفاده از IDisposable در این کلاس خاص است (ضروری نیست؛ صرفا جهت بررسی بیشتر اضافه شده‌است). اگر در کدهای برنامه، یک چنین کلاسی وجود داشت، نیاز است متد Dispose آن نیز توسط IoC Container فراخوانی شود. برای آزمایش آن یک break point را در داخل متد Dispose قرار دهید.


استفاده از سرویس تعریف شده در یک Web API Controller

using System.Web.Http;
using WebApiDISample.Services;

namespace WebApiDISample.Controllers
{
    public class ValuesController : ApiController
    {
        private readonly IEmailsService _emailsService;
        public ValuesController(IEmailsService emailsService)
        {
            _emailsService = emailsService;
        }

        // GET api/values/5
        public string Get(int id)
        {
            _emailsService.SendEmail();
            return "_emailsService.SendEmail(); called!";
        }
    }
}
در اینجا مثال ساده‌ای را از نحوه‌ی تزریق سرویس ارسال ایمیل را در ValuesController مشاهده می‌کنید.
تزریق وهله‌ی مورد نیاز آن، به صورت خودکار توسط StructureMapHttpControllerActivator که در ابتدای بحث معرفی شد، صورت می‌گیرد.

فراخوانی متد Get آن‌را نیز توسط کدهای سمت کاربر ذیل انجام خواهیم داد:
<h2>Index</h2>

@section scripts
{
    <script type="text/javascript">
        $(function () {
            $.getJSON('/api/values/1?timestamp=' + new Date().getTime(), function (data) {
                alert(data);
            });
        });
    </script>
}
درون متد Get کنترلر، یک break point قرار دهید. همچنین داخل متد Dispose لایه سرویس نیز جهت بررسی بیشتر یک break point قرار دهید.
اکنون برنامه را اجرا کنید. هنگام فراخوانی متد Get، وهله‌ی سرویس مورد نظر، نال نیست. همچنین متد Dispose نیز به صورت خودکار فراخوانی می‌شود.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
WebApiDISample.zip 
مطالب
آشنایی با تست واحد و استفاده از کتابخانه Moq
تست واحد چیست؟

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


اهمیت انجام تست واحد چیست؟

درستی یک متد، مهمترین مسئله برای بررسی است و بارها مشاهده شده، استثناهایی رخ میدهند که توان تولید را به دلیل فرسایش تکراری رخداد میکاهند. نوشتن تست واحد منجر به این می‌شود چناچه بعدها تغییری در بیزنس متد ایجاد شود و ورودی و خروجی‌ها تغییر نکند، صحت این تغییر بیزنس، توسط تست بررسی مشود؛ حتی میتوان این تست‌ها را در build پروژه قرار داد و در ابتدای اجرای یک Solution تمامی تست‌ها اجرا و درستی بخش به بخش اعضا چک شوند.


شروع تست واحد:

یک پروژه‌ی ساده را داریم برای تعریف حساب‌های بانکی شامل نام مشتری، مبلغ سپرده، وضعیت و 3 متد واریز به حساب و برداشت از حساب و تغییر وضعیت حساب که به صورت زیر است:
    /// <summary>
    /// حساب بانکی
    /// </summary>
    public class Account
    {
        /// <summary>
        /// مشتری
        /// </summary>
        public string Customer { get; set; }
        /// <summary>
        /// موجودی حساب
        /// </summary>
        public float Balance { get; set; }
        /// <summary>
        /// وضعیت
        /// </summary>
        public bool Active { get; set; }

        public Account(string customer, float balance)
        {
            Customer = customer;
            Balance = balance;
            Active = true;
        }
        /// <summary>
        /// افزایش موجودی / واریز به حساب
        /// </summary>
        /// <param name="amount">مبلغ واریز</param>
        public void Credit(float amount)
        {
            if (!Active)
                throw new Exception("این حساب مسدود است.");
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount");
            Balance += amount;
        }
        /// <summary>
        /// کاهش موجودی / برداشت از حساب
        /// </summary>
        /// <param name="amount">مبلغ برداشت</param>
        public void Debit(float amount)
        {
            if (!Active)
                throw new Exception("این حساب مسدود است.");
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount");
            if (Balance < amount)
                throw new ArgumentOutOfRangeException("amount");
            Balance -= amount;
        }
        /// <summary>
        /// انسداد / رفع انسداد
        /// </summary>
        public void ChangeStateAccount()
        {
            Active = !Active;
        }
    }
تابع اصلی نیز به صورت زیر است:
    class Program
    {
        static void Main(string[] args)
        {
            var account = new Account("Ali",1000);

            account.Credit(4000);
            account.Debit(2000);
            Console.WriteLine("Current balance is ${0}", account.Balance);
            Console.ReadKey();
        }
    }
به Solution، یک پروژه از نوع تست واحد اضافه میکنیم.
در این پروژه ابتدا Reference ایی از پروژه‌ای که مورد تست هست میگیریم. سپس در کلاس تست مربوطه شروع به نوشتن متدی برای انواع تست متدهای پروژه اصلی میکنیم.
توجه داشته باشید که Data Annotation‌های بالای کلاس تست و متدهای تست، در تعیین نوع نگاه کامپایلر به این بلوک‌ها موثر است و باید این مسئله به درستی رعایت شود. همچنین در صورت نیاز میتوان از کلاس StartUp برای شروع تست استفاده کرد که عمدتا برای تعریف آن از نام ClassInit استفاده میشود و در بالای آن از [ClassInitialize] استفاده میشود.
در Library تست واحد میتوان به دو صورت چگونگی صحت عملکرد یک تست را بررسی کرد: با استفاده از Assert و با استفاده از ExpectedException، که در زیر به هر دو صورت آن میپردازیم.
    [TestClass]
    public class UnitTest
    {
        /// <summary>
        /// تعریف حساب جدید و بررسی تمامی فرآیند‌های معمول روی حساب
        /// </summary>
        [TestMethod]
        public void Create_New_Account_And_Check_The_Process()
        {
            //Arrange
            var account = new Account("Hassan", 4000);
            var account2 = new Account("Ali", 10000);
            //Act
            account.Credit(5000);
            account2.Debit(3000);
            account.ChangeStateAccount();
            account2.Active = false;
            account2.ChangeStateAccount();
            //Assert
            Assert.AreEqual(account.Balance,9000);
            Assert.AreEqual(account2.Balance,7000);
            Assert.IsTrue(account2.Active);
            Assert.AreEqual(account.Active,false);
        }
همانطور که مشاهده میشود ابتدا در قسمت Arrange، خوراک تست آماده میشود. سپس در قسمت Act، فعالیت‌هایی که زیر ذره بین تست هستند صورت می‌پذیرند و سپس در قسمت Assert درستی مقادیر با مقادیر مورد انتظار ما مطابقت داده میشوند.
برای بررسی خطاهای تعیین شده هنگام نوشتن یک متد نیز میتوان به صورت زیر عمل کرد:
        /// <summary>
        /// زمانی که کاربر بخواهد به یک حساب مسدود واریز کند باید جلوی آن گرفته شود.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof (Exception))]
        public void When_Deactive_Account_Wants_To_add_Credit_Should_Throw_Exception()
        {
            //Arrange
            var account = new Account("Hassan", 4000) {Active = false};
            //Act
            account.Credit(4000);
            //Assert
            //Assert is handled with ExpectedException
        }

        [TestMethod]
        [ExpectedException(typeof (ArgumentOutOfRangeException))]
        public void When_Customer_Wants_To_Debit_More_Than_Balance_Should_Throw_ArgumentOutOfRangeException()
        {
            //Arrange
            var account = new Account("Hassan", 4000);
            //Act
            account.Debit(5000);
            //Assert
            //Assert is handled with ArgumentOutOfRangeException
        }
همانطور که مشخص است نام متد تست باید کامل و شفاف به صورتی انتخاب شود که بیانگر رخداد درون متد تست باشد. در این متدها Assert مورد انتظار با DataAnnotation که پیش از این توضیح داده شد کنترل گردیده است و بدین صورت کار میکند که وقتی Act انجام میشود، متد بررسی می‌کند تا آن Assert رخ بدهد.


استفاده از Library Moq در تست واحد

ابتدا باید به این توضیح بپردازیم که این کتابخانه چه کاری میکند و چه امکانی را برای انجام تست واحد فراهم میکند.
در پروژه‌های بزرگ و زمانی که ارتباطات بین لایه‌ای زیادی موجود است و اصول SOLID رعایت میشود، شما در یک لایه برای ارایه فعالیت‌ها و خدمات متدهایتان با Interface‌های لایه‌های دیگر در ارتباط هستید و برای نوشتن تست واحد متدهایتان، مشکلی بزرگ دارید که نمیتوانید به این لایه‌ها دسترسی داشته باشید و ماهیت تست واحد را زیر سوال میبرید. Library Moq این امکان را به شما میدهد که از این Interface‌ها یک تصویر مجازی بسازید و همانند Snap Shot با آن کار کنید؛ بدون اینکه در لایه‌های دیگر بروید و ماهیت تست واحد را زیر سوال ببرید.
برای استفاده از متدهایی که در این Interface‌ها موجود است شما باید یک شیء از نوع Mock<> از آنها بسازید و سپس با استفاده از متد Setup به صورت مجازی متد مورد نظر را فراخوانی کنید و مقدار بازگشتی مورد انتظار را با Return معرفی کنید، سپس از آن استفاده کنید.
همچنین برای دسترسی به خود شیء از Property ایی با نام Objet از موجودیت mock شده استفاده میکنیم.
برای شناسایی بهتر اینکه از چه اینترفیس هایی باید Mock<> بسازید، میتوانید به متد سازنده کلاسی که معرف لایه ایست که برای آن تست واحد مینویسید، مراجعه کنید.
نحوه اجرای یک تست واحد با استفاده از Moq با توجه به توضیحات بالا به صورت زیر است:
پروژه مورد بررسی لایه Service برای تعریف واحد‌های سازمانی است که با الگوریتم DDD و CQRS پیاده سازی شده است.
ابتدا به Constructor خود لایه سرویس نگاه میکنیم تا بتوانید شناسایی کنید از چه Interface هایی باید Mock<> کنیم.
  public class OrganizationalService : ICommandHandler<CreateUnitTypeCommand>,
                                         ICommandHandler<DeleteUnitTypeCommand>,                                    
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IUnitTypeRepository _unitTypeRepository;
        private readonly IOrganizationUnitRepository _organizationUnitRepository;
        private readonly IOrganizationUnitDomainService _organizationUnitDomainService;

        public OrganizationalService(IUnitOfWork unitOfWork, IUnitTypeRepository unitTypeRepository, IOrganizationUnitRepository organizationUnitRepository, IOrganizationUnitDomainService organizationUnitDomainService)
        {
            _unitOfWork = unitOfWork;
            _unitTypeRepository = unitTypeRepository;
            _organizationUnitRepository = organizationUnitRepository;
            _organizationUnitDomainService = organizationUnitDomainService;
        }
مشاهده میکنید که 4 Interface استفاده شده و در متد سازنده نیز مقدار دهی شده اند. پس 4 Mock نیاز داریم. در پروژه تست به صورت زیر و در ClassInitialize عمل میکنیم.
    [TestClass]
    public class OrganizationServiceTest
    {
        private static OrganizationalService _organizationalService;
        private static Mock<IUnitTypeRepository> _mockUnitTypeRepository;
        private static Mock<IUnitOfWork> _mockUnitOfWork;
        private static Mock<IOrganizationUnitRepository> _mockOrganizationUnitRepository;
        private static Mock<IOrganizationUnitDomainService> _mockOrganizationUnitDomainService;

        [ClassInitialize]
        public static void ClassInit(TestContext context)
        {
            TestBootstrapper.ConfigureDependencies();
            _mockUnitOfWork = new Mock<IUnitOfWork>();
            _mockUnitTypeRepository = new Mock<IUnitTypeRepository>();
            _mockOrganizationUnitRepository = new Mock<IOrganizationUnitRepository>();
            _mockOrganizationUnitDomainService=new Mock<IOrganizationUnitDomainService>();
            _organizationalService = new OrganizationalService(_mockUnitOfWork.Object, _mockUnitTypeRepository.Object,  _mockOrganizationUnitRepository.Object,_mockOrganizationUnitDomainService.Object);
        }
از خود لایه سرویس با نام OrganizationService یک آبجکت میگیریم و 4 واسط دیگر به صورت Mock شده تعریف میشوند. همچنین در کلاس بارگذار از همان نوع مقدار دهی میگردند تا در اجرای تمامی متدهای تست، در دست کامپایلر باشند. همچنین برای new کردن خود سرویس از mock.obect‌ها که حاوی مقدار اصلی است استفاده می‌کنیم.
خود متد اصلی به صورت زیر است:
        /// <summary>
        /// یک نوع واحد سازمانی را حذف مینماید
        /// </summary>
        /// <param name="command"></param>
        public void Handle(DeleteUnitTypeCommand command)
        {
            var unitType = _unitTypeRepository.FindBy(command.UnitTypeId);
            if (unitType == null)
                throw new DeleteEntityNotFoundException();

            ICanDeleteUnitTypeSpecification canDeleteUnitType = new CanDeleteUnitTypeSpecification(_organizationUnitRepository);
            if (canDeleteUnitType.IsSatisfiedBy(unitType))
                throw new UnitTypeIsUnderUsingException(unitType.Title);
            _unitTypeRepository.Remove(unitType);
        }
متد‌های تست این متد نیز به صورت زیر هستند:
        /// <summary>
        /// کامند حذف نوع واحد سازمانی باید به درستی حذف کند.
        /// </summary>
        [TestMethod]
        public void DeleteUnitTypeCommand_Should_Delete_UnitType()
        {
            //Arrange
            var unitTypeId=new Guid();
            var deleteUnitTypeCommand = new DeleteUnitTypeCommand { UnitTypeId = unitTypeId };
            var unitType = new UnitType("خوشه");
            var org = new List<OrganizationUnit>();
            _mockUnitTypeRepository.Setup(d => d.FindBy(deleteUnitTypeCommand.UnitTypeId)).Returns(unitType);
            _mockUnitTypeRepository.Setup(x => x.Remove(unitType));
            _mockOrganizationUnitRepository.Setup(z => z.FindBy(unitType)).Returns(org);
            try
            {
                //Act
                _organizationalService.Handle(deleteUnitTypeCommand);
            }
            catch (Exception ex)
            {
                //Assert
                Assert.Fail(ex.Message);
            }
        }
همانطور که مشاهده میشود ابتدا یک Guid به عنوان آی دی نوع واحد سازمانی گرفته میشود و همان آی دی برای تعریف کامند حذف به آن ارسال میشود. سپس یک نوع واحد سازمانی دلخواه تستی ساخته میشود و همچنین یک لیست خالی از واحد‌های سازمانی که برای چک شدن توسط خود متد Handle استفاده شده‌است ساخته میشود. در اینجا این متد خالی است تا شرط غلط شود و عمل حذف به درستی صورت پذیرد.
برای اعمالی که در Handle انجام میشود و متدهایی که از Interface‌ها صدا زده میشوند Setup میکنیم و آنهایی را که Return دارند به object هایی که مورد انتظار خودمان هست نسبت میدهیم.
در Setup اول میگوییم که آن Guid مربوط به "خوشه" است. در Setup بعدی برای عمل Remove کدی مینویسیم و چون عمل حذف Return ندارد میتواند، این خط به کل حذف شود! به طور کلی Setup هایی که Return ندارند میتوانند حذف شوند.
در Setup بعدی از Interface دیگر متد FindBy که قرار است چک کند این نوع واحد سازمانی برای تعریف واحد سازمانی استفاده شده است، در Return به آن یک لیست خالی اختصاص میدهیم تا نشان دهیم لیست خالی برگشته است.
عملیات Act را وارد Try میکنیم تا اگر به هر دلیل انجام نشد، Assert ما باشد.
دو حالت رخداد استثناء که در متد اصلی تست شده است در دو متد تست به طور جداگانه تست گردیده است:
        /// <summary>
        /// کامند حذف یک نوع واحد سازمانی باید پیش از حذف بررسی کند که این شناسه داده شده برای حذف موجود باشد.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DeleteEntityNotFoundException))]
        public void DeleteUnitTypeCommand_ShouldNot_Delete_When_UnitTypeId_NotExist()
        {
            //Arrange
            var unitTypeId = new Guid();
            var deleteUnitTypeCommand = new DeleteUnitTypeCommand();
            var unitType = new UnitType("خوشه");
            var org = new List<OrganizationUnit>();
            _mockUnitTypeRepository.Setup(d => d.FindBy(unitTypeId)).Returns(unitType);
            _mockUnitTypeRepository.Setup(x => x.Remove(unitType));
            _mockOrganizationUnitRepository.Setup(z => z.FindBy(unitType)).Returns(org);

            //Act
            _organizationalService.Handle(deleteUnitTypeCommand);
        }

        /// <summary>
        /// کامند حذف یک نوع واحد سازمانی نباید اجرا شود وقتی که نوع واحد برای تعریف واحد‌های سازمان استفاده شده است.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(UnitTypeIsUnderUsingException))]
        public void DeleteUnitTypeCommand_ShouldNot_Delete_When_UnitType_Exist_but_UsedForDefineOrganizationUnit()
        {
            //Arrange
            var unitTypeId = new Guid();
            var deleteUnitTypeCommand = new DeleteUnitTypeCommand { UnitTypeId = unitTypeId };
            var unitType = new UnitType("خوشه");
            var org = new List<OrganizationUnit>()
            {
                new OrganizationUnit("مدیریت یک", unitType, null),
                new OrganizationUnit("مدیریت دو", unitType, null)
            };
            _mockUnitTypeRepository.Setup(d => d.FindBy(deleteUnitTypeCommand.UnitTypeId)).Returns(unitType);
            _mockUnitTypeRepository.Setup(x => x.Remove(unitType));
            _mockOrganizationUnitRepository.Setup(z => z.FindBy(unitType)).Returns(org);

            //Act
            _organizationalService.Handle(deleteUnitTypeCommand);
        }
متد DeleteUnitTypeCommand_ShouldNot_Delete_When_UnitTypeId_NotExist همانطور که از نامش معلوم است بررسی میکند که نوع واحد سازمانی که ID آن برای حذف ارسال میشود در Database وجود دارد و اگر نباشد Exception مطلوب ما باید داده شود.
در متد DeleteUnitTypeCommand_ShouldNot_Delete_When_UnitType_Exist_but_UsedForDefineOrganizationUnit بررسی میشود که از این نوع واحد سازمانی برای تعریف واحد سازمانی استفاده شده است یا نه و صحت این مورد با الگوی Specification صورت گرفته است. استثنای مطلوب ما Assert و شرط درستی این متد تست، میباشد.