مطالب
خلاصه اشتراک‌های روز چهار شنبه 1390/06/30

مطالب
WPF4 و ویندوز 7 : به خاطر سپاری لیست آخرین فایل‌های گشوده شده توسط برنامه

اگر به برنامه‌های جدید نوشته شده برای ویندوز 7 دقت کنیم، از یک سری امکانات مخصوص آن جهت بهبود دسترسی پذیری به قابلیت‌هایی که ارائه می‌دهند، استفاده شده است. برای مثال برنامه‌ی OneNote مجموعه‌ی آفیس را در نظر بگیرید. اگر بر روی آیکون آن در نوار وظیفه‌ی ویندوز کلیک راست کنیم، لیست آخرین فایل‌های گشوده شده توسط آن مشخص است و با کلیک بر روی هر کدام، به سادگی می‌توان این فایل را گشود. یک چنین قابلیتی در منوی آغازین ویندوز نیز تعبیه شده است (شکل‌های زیر):




خبر خوب اینکه برای اضافه کردن این قابلیت به برنامه‌های WPF4 نیازی به کد نویسی نیست و این موارد که تحت عنوان استفاده از Jump list ویندوز 7 تعریف شده‌اند، با کمی دستکاری فایل App.Xaml برنامه، فعال می‌گردند:

<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
<JumpList.JumpList>
<JumpList ShowRecentCategory="True" />
</JumpList.JumpList>
</Application>
همین! از این پس هر فایلی که توسط برنامه‌ی شما با استفاده از common file dialog boxes باز شود به صورت خودکار به لیست مذکور اضافه می‌گردد (بدیهی است Jump lists جزو ویژگی‌های ویندوز 7 است و در سایر سیستم عامل‌ها ندید گرفته خواهد شد).


سؤال: من اینکار را انجام دادم ولی کار نمی‌کنه!؟

پاسخ: بله. کار نمی‌کنه! این قابلیت تنها زمانی فعال خواهد شد که علاوه بر نکته‌ی فوق، پسوند فایل یا فایل‌هایی نیز به برنامه‌ی شما منتسب شده باشد. این انتساب‌ها مطلب جدیدی نیست و در تمام برنامه‌های ویندوزی باید توسط بکارگیری API ویندوز مدیریت شود. قطعه کد زیر اینکار را انجام خواهد داد:
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace Common.Files
{
//from : http://www.devx.com/vb2themax/Tip/19554?type=kbArticle&trk=MSCP
public class FileAssociation
{
const int ShcneAssocchanged = 0x8000000;
const int ShcnfIdlist = 0;

public static void CreateFileAssociation(
string extension,
string className,
string description,
string exeProgram)
{
// ensure that there is a leading dot
if (extension.Substring(0, 1) != ".")
extension = string.Format(".{0}", extension);

try
{
if (IsAssociated(extension)) return;

// create a value for this key that contains the classname
using (var key1 = Registry.ClassesRoot.CreateSubKey(extension))
{
if (key1 != null)
{
key1.SetValue("", className);
// create a new key for the Class name
using (var key2 = Registry.ClassesRoot.CreateSubKey(className))
{
if (key2 != null)
{
key2.SetValue("", description);
// associate the program to open the files with this extension
using (var key3 = Registry.ClassesRoot.CreateSubKey(string.Format(@"{0}\Shell\Open\Command", className)))
{
if (key3 != null) key3.SetValue("", string.Format(@"{0} ""%1""", exeProgram));
}
}
}
}
}

// notify Windows that file associations have changed
SHChangeNotify(ShcneAssocchanged, ShcnfIdlist, 0, 0);
}
catch (Exception ex)
{
//todo: log ...
}
}

// Return true if extension already associated in registry
public static bool IsAssociated(string extension)
{
return (Registry.ClassesRoot.OpenSubKey(extension, false) != null);
}

[DllImport("shell32.dll")]
public static extern void SHChangeNotify(int wEventId, int uFlags, int dwItem1, int dwItem2);
}
}
و مثالی از نحوه‌ی استفاده از آن:
private static void createFileAssociation()
{
var appPath = Assembly.GetExecutingAssembly().Location;
FileAssociation.CreateFileAssociation(".xyz", "xyz", "xyz File",
appPath
);
}
لازم به ذکر است که این کد در ویندوز 7 فقط با دسترسی مدیریتی قابل اجرا است (کلیک راست و اجرا به عنوان ادمین) و در سایر حالات با خطای Access is denied متوقف خواهد شد. به همین جهت بهتر است برنامه‌ی نصاب مورد استفاده این نوع انتسابات را مدیریت کند؛ زیرا اکثر آن‌ها با دسترسی مدیریتی است که مجوز نصب را به کاربر جاری خواهند داد. اگر از فناوری Click once استفاده می‌کنید به این مقاله و اگر برای مثال از NSIS کمک می‌گیرید به این مطلب مراجعه نمائید.


سؤال: من این کارها را هم انجام دادم. الان به چه صورت از آن‌ استفاده کنم؟

زمانیکه کاربری بر روی یکی از این فایل‌های ذکر شده در لیست آخرین فایل‌های گشوده شده توسط برنامه کلیک کند، آدرس این فایل به صورت یک آرگومان به برنامه ارسال خواهد شد. برای مدیریت آن در WPF باید به فایل App.Xaml.cs مراجعه کرده و چند سطر زیر را به آن افزود:
    public partial class App
{
public App()
{
this.Startup += appStartup;
}

void appStartup(object sender, StartupEventArgs e)
{
if (e.Args.Any())
{
this.Properties["StartupFileName"] = e.Args[0];
}
}
//...

در این کد، e.Args حاوی مسیر فایل انتخابی است. برای مثال در اینجا مقدار آن به خاصیت StartupFileName انتساب داده شده است. این خاصیت در برنامه‌های WPF به صورت یک خاصیت عمومی تعریف شده است و در سراسر برنامه (مثلا در رخداد آغاز فرم اصلی آن یا هر جای دیگری) به صورت زیر قابل دسترسی است:
var startupFileName = Application.Current.Properties["StartupFileName"];

سؤال: برنامه‌ی من از OpenFileDialog برای گشودن فایل‌ها استفاده نمی‌کند. آیا راه دیگری برای افزودن مسیرهای باز شده به Jump lists ویندوز 7 وجود دارد؟

پاسخ: بله. همانطور که می‌دانید عناصر XAML با اشیاء دات نت تناظر یک به یک دارند. به این معنا که JumpList تعریف شده در ابتدای این مطلب در فایل App.XAML ، دقیقا معادل کلاسی به همین نام در دات نت فریم ورک است (تعریف شده در فضای نام System.Windows.Shell) و با کد نویسی نیز قابل دسترسی و مدیریت است. برای مثال:
var jumpList = JumpList.GetJumpList(App.Current);
var jumpPath = new JumpPath();
jumpPath.Path = "some path goes here....";
// If the CustomCategory property is null
// or Empty, the item is added to the Tasks category
jumpPath.CustomCategory = "Files";
JumpList.AddToRecentCategory(jumpPath);
jumpList.Apply();
به همین ترتیب،‌ JumpPath ذکر شده در کدهای فوق، در کدهای XAML نیز قابل تعریف است:
<Application x:Class="Win7Wpf4.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
<JumpList.JumpList>
<JumpList ShowRecentCategory="True">
<JumpPath
CustomCategory="Files"
Path="Some path goes here..."
/>
</JumpList>
</JumpList.JumpList>
</Application>

نظرات مطالب
پایان پروژه ASP.NET Ajax Control Toolkit !
مایکروسافت ASP.NET Ajax را با اون عظمت و زحمتی که براش کشیده بودند، در مقابل jQuery بازنده اعلام کرده الان شما صحبت از Anthem.Net می‌کنید که فقط منحصر است به ASP.NET web forms آن هم نگارش‌های قبل از سه آن + پشتیبانی از مرورگرهای مختلف هم در آن ضعیف است.
هدف مایکروسافت از اینکار مدیریت هر دو پروژه MVC و Web forms است آن هم با هزینه کم، کیفیت بالا و سازگار با تمام مرورگرها.
ضمنا این رو در نظر باشید که یکی از توانمندی‌های jQuery، Ajax است (از کار با DOM گرفته تا دستکاری CSS نمایش داده شده، تا Animation ، مدیریت ساده‌تر رخدادها، اعمال قالب به سایت و غیره) و این مورد مزیت مهمی است نسبت به تمام کتابخانه‌هایی که فقط برای یک کار و آن هم سهولت تولید برنامه‌های مبتنی بر Ajax ایجاد شده‌اند و از چند مشکل مهم رنج می‌برند:
- تک کاره‌اند. فقط Ajax .
- مشکل سازگاری با مرورگرهای مختلف را دارند.
- به صورت فعال توسعه داده نمی‌شوند؛ رفع باگ نمی‌شوند و غیره. برای مثال فواصل به روز رسانی همان Anthem.Net را بررسی کنید.
- توسعه پذیر نیستند. برای مثال آیا می‌توان برای Anthem.Net افزونه نوشت؟
- حجم بالایی دارند.
- سرعت پایینی دارند.

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

و RAD هزینه بر است. یعنی چی؟ یعنی حجم بالای کدهای اسکریپتی که باید به برنامه‌ی شما مثلا توسط ASP.NET Ajax تزریق شود که مبادا شما بخواهید دست خودتان را به نوشتن چند سطر کد جاوا اسکریپتی آلوده کنید. همچنین حجم تبادل اطلاعات ASP.NET Ajax را هم که مبتنی است بر RAD را هم با حجم اطلاعات مبادله شده توسط jQuery مقایسه کنید (با پلاگین فایرباگ مربوط به فایرفاکس). این حجم واقعا زیاد است و قابل مقایسه نیست. (تمام این‌ها هزینه‌های RAD است)
ضمنا وجود 100 ها افزونه و پلاگین نوشته شده برای jQuery کار شما را بسیار بسیار ساده می‌کنند، مانند پاسخ قبلی من در این مطلب.
فقط باید کمی وقت بگذارید و چیزی را بیاموزید که واقعا ارزش دارد. گیرم فردا نخواستید با ASP.NET کار کنید. تمام این اطلاعات در PHP هم به درد شما می‌خورد، چون قسمت سمت کلاینت آنچنان تفاوتی نمی‌کند و jQuery یک کتابخانه‌ی سمت کلاینت است.
نظرات مطالب
آشنایی با NHibernate - قسمت سوم
سؤال شما مرتبط است به موضوع "nhibernate derived properties" (+) و برای بررسی مشکل شما نیاز به این موارد است:

- چگونه نگاشت‌ها را تعریف کرده‌اید. (نیاز به سورس است)
- دقیقا چه خطایی می‌گیرید. متن آن‌ خیلی مهم است.

لطفا از امکانات انجمن‌ها برای ادامه‌ی بحث استفاده کنید.

+ اگر از fluent NHibernate استفاده می‌کنید، نگارش سازگار با NHibernate 3 آن هنوز ارائه نشده (به زودی) : (+)
مطالب
آشنایی با KnownTypeAttribute در WCF
تشریح مسئله : KnownTypeAttribute چیست و چگونه از آن استفاده کنیم؟
پیش نیاز : آشنایی اولیه با مفاهیم WCF برای فهم بهتر مطالب
در ابتدا یک WCf Service Application ایجاد کنید  و مدل زیر را بسازید:
 [DataContract] 
    public abstract class Person
    {
        [DataMember]
        public int Code { get; set; }

        [DataMember]
        public string Name { get; set; }     
}
یک کلاس پایه برای Person ایجاد کردیم به صورت abstract که وهله سازی از آن میسر نباشد و 2 کلاس دیگر می‌سازیم که از کلاس بالا ارث ببرند:
کلاس #1
 [DataContract]  
    public class Student : Person
    {
        [DataMember]
        public int StudentId { get; set; }
    }
کلاس #2
[DataContract]
    public class Teacher : Person
    {
        public int TeacherId { get; set; }
    }
فرض کنید قصد داریم سرویسی ایجاد کنیم که لیست تمام اشخاص موجود در سیستم را در اختیار ما قرار دهد. (هم Student و هم Teacher). ابتدا Contract مربوطه را به صورت زیر تعریف می‌کنیم:
[ServiceContract]
    public interface IStudentService
    {
        [OperationContract]
        IEnumerable<Person> GetAll();
    }
همان طور که می‌بینید خروجی متد GetAll  از نوع Person است (نوع پایه کلاس Student , Teacher) . سرویس مربوطه بدین شکل خواهد شد.
 public class StudentService : IStudentService
    {
        public IEnumerable<Person> GetAll()
        {
            List<Person> listOfPerson = new List<Person>();

            listOfPerson.Add( new Student() { Code = 1, StudentId = 123, Name = "Masoud Pakdel" } );
            listOfPerson.Add( new Student() { Code = 1, StudentId = 123, Name = "Mostafa Asgari"} );
            listOfPerson.Add( new Student() { Code = 1, StudentId = 123, Name = "Saeed Alizadeh"} );          
listOfPerson.Add( new Teacher() { Code = 1, TeacherId = 321, Name = "Mahdi Rad"} ); listOfPerson.Add( new Teacher() { Code = 1, TeacherId = 321, Name = "Mohammad Heydari" } ); listOfPerson.Add( new Teacher() { Code = 1, TeacherId = 321, Name = "Saeed Khatami"} ); return listOfPerson; }
در این سرویس در متد GetAll لیستی از تمام اشخاص رو ایجاد می‌کنیم . 3 تا Student و 3 تا Teacher رو به این لیست اضافه میکنیم. برای نمایش اطلاعات در خروجی یک پروژه Console Application ایجاد کنید و سرویس بالا رو از روش AddServiceReference به پروژه اضافه کنید سپس در کلاس Program کد‌های زیر رو کپی کنید.
 class Program
    {
        static void Main( string[] args )
        {
            StudentService.StudentServiceClient client = new StudentService.StudentServiceClient();

            client.GetAll().ToList().ForEach( _record => 
            {
                Console.Write( "Name : {0}", _record.Name );
                Console.WriteLine( "Code : {0}", _record.Code );
            } );

            Console.ReadLine();
        }
    }
پروژه رو کامپایل کنید. تا اینجا هیچ گونه مشکلی مشاهده نشد و انتظار داریم که خروجی مورد نظر رو مشاهده کنیم. بعد از اجرای پروژه با خطای زیر متوقف می‌شویم:

مشکل از اینجا ناشی می‌شود که هنگام عمل سریالایز ، WCF Runtime با توجه به وهله سازی از کلاس Person می‌دونه که باید کلاس Student یا Teacher رو سریالایز کنه ولی در هنگام عمل دی سریالایز، WCF Runtime این موضوع رو درک نمی‌کنه به همین دلیل یک Communication Exception پرتاب می‌کنه. برای حل این مشکل و برای اینکه WCF Deserialize Engine  رو متوجه نوع وهله سازی کلاس‌های مشتق شده از کلاس پایه کنیم باید از KnownTypeAttribute استفاده کنیم. فقط کافیست که این Attribute رو بالای کلاس Person به ازای تمام کلاس‌های مشتق شده از اون قرار بدید.بدین صورت:

   [DataContract]
    [KnownType( typeof( Student ) )]
    [KnownType( typeof(Teacher) )]
    public abstract class Person
    {
        [DataMember]
        public int Code { get; set; }

        [DataMember]
        public string Name { get; set; }
    }
حالا پروژه سمت سرور رو دوباره کامپایل کنید و سرویس سمت کلاینت رو Update کنید. بعد پروژه رو دوباره اجرا کرده تا خروجی زیر رو مشاهده کنید.


با وجود KnownType دیگه WCF Deserialize Engine میدونه که باید از کدام DataContact برای عمل دی سریالاز نمونه ساخته شده از کلاس Person استفاده کنه. دانستن این مطلب هنگام پیاده سازی مفاهیم ارث بری در ORM ها زمانی که از WCF استفاده می‌کنیم ضروری است.
مطالب
آموزش Coded UI Test #1
اکثر برنامه نویسان با مباحث Unit Testing  آشنایی دارند و بعضی برنامه نویسان هم، از این مباحث در پروژه‌های خود استفاده می‌کنند. ساختار الگو‌های MVC و MVVM به گونه ای است که به راحتی می‌توان برای این گونه پروژه‌ها Unit Test بنویسیم. در پروژه‌های MVC به دلیل عدم وابستگی بین View و Controller به طور مستقیم، امکان نوشتن Unit Test برای Controller امکان پذیر است و از طرفی در الگوی MVVM به دلیل منطق وجود ViewModel می‌توان برای اینگونه پروژه‌ها نیز Unit Test نوشت. اما ساختار سایر پروژه‌ها به گونه ای است که نوشتن Unit Test برای آن‌ها مشکل و در بعضی مواقع غیر ممکن می‌شود. برای مثال در پروژهای Desktop نظیر Windows Application و حتی وب به صورت Asp.Net Web Forms به دلیل وابستگی مستقیم کنترل‌های UI به منطق اجرای برنامه، طراحی و نوشتن Unit Test بسیار مشکل و در برخی موارد بیهوده است. در VS.Net ابزاری وجود دارد به نام Coded UI Test که برای تست این گونه پروژه‌ها طراحی شده است و همان طور که از نامش پیداست صرفا برای تست کنترل‌های UI و رویداد‌های کنترل‌ها و تست درستی برنامه با توجه به داده‌های ورودی به کار می‌رود. یکی از مزیت‌های اصلی آن تسریع عملیات تست در حجم بالا است و زمان ایجاد unit  test را به حداقل می‌رساند. مزیت دوم آن امکان ایجاد unit test برای پروژه‌های که در مراحل پایانی تولید هستند ولی هنوز اطمینانی به عملکرد صحیح برنامه در حالات مختلف نیست.
 در این پست قصد دارم روش استفاده از این گونه پروژه‌های تست را با ذکر یک مثال بررسی کنیم و در پست‌های بعدی به بررسی امکانات دیگر خواهیم پرداخت.

نکته : فقط در Vs.Net با نسخه‌های Ultimate و Premium می‌تونید از Code UI Test استفاده کنید که البته به دلیل اینکه در ایران پیدا کردن نسخه‌های دیگر Vs.Net به غیر از Ultimate سخت‌تر است به طور قطع این محدودیت برای برنامه نویسان ما وجود نخواهد داشت. برای اینکه از نسخه Vs.Net خود اطمینان حاصل کنید از منوی Help گزینه About Microsoft Visual Studio رو انتخاب کنید. پنجره ای به شکل زیر مشاهده خواهید کرد که در آن مشخصات کامل Vs.Net ذکر شده است.

در این مرحله قصد داریم برای فرم زیر Unit Test طراحی کنیم. پروژه به صورت زیر است:

کاملا واضح است که در این فرم دو عدد به عنوان ورودی دریافت می‌شود و بعد از کلیک بر روی CalculateSum نتیجه در textbox سوم نمایش داده می‌شود. برای تست عملکرد صحیح فرم بالا ایتدا به Solution مورد نظر از منوی test Project یک Coded UI Test Project اضافه می‌کنیم. به دلیل اینکه این قبلا در این Solution پروژه تست از نوع Coded UI Test نبود بلافاصله یک پنجره نمایش داده می‌شود. مطمئن شوید گزینه اول انتخاب شده و بعد بر روی Ok کلیک کنید.(گزینه اول به معنی است که قصد داریم عملیات مورد نظر بر روی UI را رکورد کنیم و گزینه دوم به معنی است که قصد داریم از عملیات رکورد شده قبلی استفاده کنیم).  یک کلاس به نام CodeUITest1 به همراه یک متد تست به نام CodedUITestMethod1 ساخته می‌شود. اولین چیزی که جلب توجه می‌کند این است که این کلاس به جای TestClassAttribute دارای نشان CodeUITestAttrbiute است. در گوشه سمت راست Vs.Net خود یک پنجره کوچک به نام UI Map Test Builder مانند شکل زیر خواهید دید.

دکمه قرمز رنگ به نام Record Button است و عملیات تست را رکورد خواهد کرد. دکمه دایره ای به رنگ مشکی برای تعیین Assertion به کار می‌رود. و در نهایت گزینه آخر کد‌های مورد نظر مراحل قبل را به صورت خودکار تولید خواهد کرد.

#روش کار

روش کار به این صورت است که ابتدا شما مراحل تست خود را شبیه سازی خواهید کرد و بعد از آن Test Builder مراحل تست شما را به صورت کامل به صورت کد‌های قابل فهم تولید خواهد کرد. (دقیقا شبیه به ایجاد UnitTest به روش Arrange/Act/Assert است با این تفاوت که این مراحل توسط UI Map رکورد شده و نیازی به کد نویسی ندارد). در پایان باید یک Data Driven Coded UI Test طراحی کنید تا بتوانید از این مراحل رکورد استفاده نمایید. 

#چگونگی شبیه سازی :

پروژه را اجرا نمایید. زمانی که فرم مورد نظر  ظاهر شد بر روی گزینه Record در TestBuilder کلیک کنید. عملیات ذخیره سازی شروع شده است. در نتیجه به فرم مربوطه رفته و در Textbox اول مقدار 10 و در textbox دوم مقدار 5 را وارد نمایید. با کلیک بر روی دکمه CalculateSum مقدار 15 نمایش داده خواهد شد. از برنامه خارج شوید و بعد بر روی گزینه Generate Code در TestBuilder کلیک کنید با از کلید‌های ترکیبی Alt + G استفاده نمایید.(اگر در این مرحله، از برنامه خارج نشده باشید با خطا مواجه خواهید شد.) در پنجره نمایش داده شده یک نام به متد اختصاص دهید. عملیات تولید کد شروع خواهد شد. بعد کدی مشابه زیر را در متد مربوطه مشاهده خواهید کرد.

   [TestMethod]
        public void CodedUITestMethod1()
        {            
            this.UIMap.CalculateSum();
            this.UIMap.txtSecondValueMustBe10();
        }
بخشی از سورس کد تولید شده برای متد CalculateSum به شکل زیر است:
        public void CodedUITestMethod1 ()
        {
            #region Variable Declarations
            WinEdit uITxtFirstNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtFirstNumberWindow.UITxtFirstNumberEdit;
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            WinButton uICalculateSumButton = this.UIدوعددصحیحواردنماییدWindow.UICalculateSumWindow.UICalculateSumButton;
            #endregion

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText;

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(83, 12));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText1;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys1, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText1;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(49, 11));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText2;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys2, ModifierKeys.None);

            // Type '5' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText2;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys1, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(74, 16));
        }
همان طور که می‌بینید تمام مراحل تست شما رکورد شده است و به صورت کد قابل فهم بالا ایجاد شده است.
چگونگی ایجاد Assertion
اگر به کد متد تست CodedUITestMethod1 در بالا دقت کنید یک متد به صورت this.UIMap.txtSecondValueMustBe10 فراخوانی شده است. این در واقع یک Assertion  است که در هنگام عملیات رکورد ایجاد کردم و به این معنی است که مقدار TextBox دوم حتما باید 10 باشد. حال روش تولید Assertion‌ها را بررسی خواهیم کرد.
بعد از شروع شدن مرحله رکورد اگر قصد دارید برای یک کنترل خاص Assert بنویسید، دکمه assertion (به رنگ مشکی و به صورت دایره است) را بر روی کنترل مورد نظر drag&drop کنید. یک border آبی برای کنترل مورد نظر ایجاد خواهد شد:

به محض اتمام عملیات drag&drop منوی زیر ظاهر خواهد شد:

از گزینه Add Assertion استفاده کنید و برای کنترل مورد نظر یک assert بنویسید. در شکل زیر یک assert برای textbox دوم نوشتم به صورتی که مقدار آن باید با 5 برابر باشد.


از گزینه آخر برای نمایش پیغام مورد نظر خودتون در هنگامی که aseert با شکست مواجه می‌شود استفاده کنید.
کد تولید شده زیر برای عملیات assert بالا است:
  public void txtSecondValueMustBe10()
        {
            #region Variable Declarations
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            #endregion

            // Verify that the 'ControlType' property of 'txtSecondNumber' text box equals '10'
            Assert.AreEqual(this.txtSecondValueMustBe10ExpectedValues.UITxtSecondNumberEditControlType, uITxtSecondNumberEdit.ControlType.ToString());
        }

مرحله اول انجام شد. برای تست این مراحل باید یک Data DrivenTest بسازید که در پست بعدی به صورت کامل شرح داده خواهد شد.
مطالب
نصب Mono Develop 4.x در Ubuntu
پیشنیازها

در قسمت قبل، موفق به نصب Mono 3.0 در لینوکس شدیم. در ادامه قصد داریم یک IDE لینوکسی مخصوص کار با Mono را به نام Mono Develop بر روی Ubuntu نصب کنیم. اگر مونو را نصب کرده‌اید، نیاز است پیشنیازهای ذیل را بر روی سیستم خود نصب کنید:
 sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y build-essential libc6-dev g++ gcc libglib2.0-dev pkg-config \
 git-core apache2 apache2-threaded-dev bison gettext autoconf automake libtool \
libpango1.0-dev libatk1.0-dev libgtk2.0-dev libtiff5-dev libgif-dev libglade2-dev curl \
python-software-properties gawk libjpeg-dev libexif-dev flex checkinstall intltool git \
libcairo2-dev libgnomecanvas2-dev libgnome2-dev libgnomeui-dev libgnomeprint2.2-dev \
libgnomeprintui2.2-dev libgtkhtml3.14-dev libgtksourceview2.0-dev librsvg2-dev libvte-dev \
libnspr4-dev libnss3-dev libwebkit-dev apache2-threaded-dev libpng12-dev libfontconfig1-dev \
libfreetype6-dev zlib1g-dev libjpeg8-dev libjpeg-turbo8-dev libart-2.0-dev libgnomevfs2-dev \
libgnome-desktop-dev libnautilus-extension-dev libwnck-dev libvala-0.18-dev \
mono-addins-utils gtk-sharp2 gnome-sharp2
نصب این پیشنیارها ضروری بوده و در غیر اینصورت موفق به build کامل Mono Develop نخواهید شد. برای مثال پیغام خطای ذیل را در انتهای build دریافت می‌کنید؛ به این معنا که اسمبلی‌های ذیل کامپایل نشده‌اند:
 * art-sharp.dll: no
* gnomevfs-sharp.dll: no
* gnome-sharp.dll: no
و یا عنوان می‌کند که  gnome-sharp.dll برای کامپایل نیاز به یک سری کتابخانه کمکی دیگر نیز دارد:
  gnome-sharp.dll requires libgnomecanvas, libgnome, and libgnomeui.


نصب متداول محیط برنامه نویسی Mono Develop

برای نصب یک IDE که بتوان تحت همان لینوکس نیز کار برنامه نویسی دات نت را انجام داد، می‌توان از Mono deveop استفاده کرد. برای نصب آن فرمان ذیل را در خط فرمان لینوکس صادر نکنید !
 sudo apt-get install monodevelop
این روش هر چند کار می‌کند، اما تا این تاریخ، نگارش 3 را نصب خواهد کرد؛ با توجه به اینکه آخرین نگارش موجود در سایت Mono Develop، نگارش 4 است. همچنین نصب آن نیز نگارش جاری Mono را به نگارش 2 آن تنظیم می‌کند که جالب نیست. اگر به اشتباه آن‌را نصب کرده‌اید، برای حذف مونو از دستور ذیل استفاده کنید:
 sudo apt-get purge cli-common mono-runtime
همچنین الان کلیه مسیرهای سیستم به هم ریخته است. برای رفع آن مسیر نصب Mono-3.0 را باید به نحو ذیل مجددا تنظیم کرد:
 export PATH=/opt/mono-3.0/bin:$PATH
export PKG_CONFIG_PATH=/opt/mono-3.0/lib/pkgconfig:$PKG_CONFIG_PATH


نصب محیط برنامه نویسی Mono Develop از روی مخزن کد آن

دریافت و نصب وابستگی‌های Monodevelop جهت کامپایل سورس آن، شاید نصف روز شما را به خود اختصاص دهد؛ به علاوه حداقل مصرف حدود 500 مگابایت حجم اینترنت. راه ساده‌تری نیز برای دریافت آخرین نگارش سازگار با Ubuntu آن وجود دارد و آن هم استفاده از بسته‌های شخصی کامپایل شده است؛ که اصطلاحا به آن‌ها PPA نیز گفته می‌شود. برای مثال: (^ و ^ )
چند نمونه بسته شخصی برای دریافت ساده آخرین نگارش Mono develop جهت نصب بر روی Ubuntu : (^ و ^ و ^ )
و به صورت خلاصه فرامین ذیل را در ترمینال لینوکس اجرا کنید تا از بسته شخصی keks9n استفاده کنیم:
 sudo add-apt-repository ppa:keks9n/monodevelop-latest
sudo apt-get update
sudo apt-get install monodevelop-latest
این روش، از تمام روش‌های ذکر شده تا کنون، ساده‌تر است. از این لحاظ که mono 3.2.1 را نیز به صورت خودکار بر روی سیستم شما نصب می‌کند (این بسته شخصی، به صورت خودکار هر از چندگاهی آخرین نگارش مونو، وابستگی‌های آن و monodevelop جدید را بسته بندی و ارائه می‌دهد).
بنابراین اگر مونو 3.2.1 یا جدیدتر را هنوز نصب نکرده‌اید، همین سه سطر فوق، کار نصب کلی آن‌را نیز انجام می‌دهد؛ علاوه بر نصب monodevelop در آخر کار به همراه تمام پیشنیازهای لازم مانند gtk-sharp و gnome-sharp.
پس از نصب کامل، برای اجرای آن در همان خط فرمان، دستور monodevelop را صادر کنید.


نظرات مطالب
راهبری در Silverlight به کمک الگوی MVVM
این هم خوبه؛ شبیه به ارسال اطلاعات به کمک یک delegate یا event . از سر ناچاری!
- ضمنا MVVM Light toolkit‌ سورس باز است. کلاس Messenger آن‌را جدا کنید و استفاده کنید (اگر از کل آن نمی‌خواهید استفاده کنید).

یک روش هم اینجا دیدم که خیلی جالب است:
http://forums.silverlight.net/forums/p/198684/463126.aspx
از NavigateUri یک HyperlinkButton استفاده کرده. فقط UriMapper را هم تنظیم کرده برای زیبایی کار.
(نیاز به هیچ کتابخانه جانبی هم ندارد. نیازی به دخالت MVVM هم ندارد.و مهم‌تر از همه، نیازی به کد نویسی هم اصلا ندارد.)

ولی برای پاس دادن یک وهله از صفحه جاری به صفحه بعد (مثل لینک داخل صفحات وب)، کلاس Messenger واقعا تر و تمیز و عالی است. (نیازی هم به استفاده از کوئری استرینگ یا هر روش دیگری نیست)
نظرات مطالب
دریافت کتاب از Google books
سلام
جالب بود مواجه شدن با محدودیت، برای هیچ کس جالب نیست.

ضمنا قابلیت انتخاب فرمت نمایش تاریخ به ShamsiXp 0.25 اضافه شده و سورس کد آن نیز پیوست شده است. (http://mohammadshams.blogspot.com/2009/09/shamsi-xp-025-source-code.html)

برای ادامه این کار به شکل صحیح و اضافه کردن Calendar Type فارسی به استانداردهای ویندوز، تاپیکی در بخش امنیت نرم‌افزار برنامه‌نویس اضافه شد.
امیدوارم با همکاری دیگر دوستان بتوان این "محدودیت" را هم از بین برد.
با تشکر
مطالب
استفاده از EF در اپلیکیشن های N-Tier : قسمت پنجم
در قسمت قبل پیاده سازی change-tracking در سمت کلاینت توسط Web API را بررسی کردیم. در این قسمت نگاهی به حذف موجودیت‌های منفصل یا disconnected خواهیم داشت.


حذف موجودیت‌های منفصل

فرض کنید موجودیتی را از یک سرویس WCF دریافت کرده اید و می‌خواهید آن را برای حذف علامت گذاری کنید. مدل زیر را در نظر بگیرید.

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

  • در ویژوال استودیو پروژه جدیدی از نوع WCF Service Library بسازید و نام آن را به Recipe5 تغییر دهید.
  • روی پروژه کلیک راست کنید و گزینه Add New Item را انتخاب کنید. سپس گزینه‌های Data -> ADO.NET Entity Data Model را برگزینید.
  • از ویزارد ویژوال استودیو برای اضافه کردن یک مدل با جداول Invoice و Payment استفاده کنید. برای ساده نگه داشتن مثال جاری، فیلد پیمایشی Payments را از موجودیت Invoice حذف کرده ایم (برای این کار روی خاصیت پیمایشی Payments کلیک راست کنید و گزینه Delete From Model را انتخاب کنید.) روی خاصیت TimeStamp موجودیت Payment کلیک راست کنید و گزینه Properties را انتخاب کنید. سپس مقدار Concurrency Mode آن را به Fixed تغییر دهید. این کار باعث می‌شود که مقدار این فیلد برای کنترل همزمانی بررسی شود. بنابراین مقدار TimeStamp در عبارت WHERE تمام دستورات بروز رسانی و حذف درج خواهد شد.
  • فایل IService1.cs را باز کنید و تعریف سرویس را مانند لیست زیر تغییر دهید.
[ServiceContract]
public interface IService1
{
    [OperationContract]
    Payment InsertPayment();
    [OperationContract]
    void DeletePayment(Payment payment);
}
  • فایل Service1.cs را باز کنید و پیاده سازی سرویس را مانند لیست زیر تغییر دهید.
public class Service1 : IService1
{
    public Payment InsertPayment()
    {
        using (var context = new EFRecipesEntities())
        {
            // delete the previous test data
            context.Database.ExecuteSqlCommand("delete from [payments]");
            context.Database.ExecuteSqlCommand("delete from [invoices]");
            var payment = new Payment { Amount = 99.95M, Invoice =
                new Invoice { Description = "Auto Repair" } };
            context.Payments.Add(payment);
            context.SaveChanges();
            return payment;
        }
    }

    public void DeletePayment(Payment payment)
    {
        using (var context = new EFRecipesEntities())
        {
            context.Entry(payment).State = EntityState.Deleted;
            context.SaveChanges();
        }
    }
}
  • برای تست این سرویس به یک کلاینت نیاز داریم. یک پروژه جدید از نوع Console Application به راه حل جاری اضافه کنید و کد آن را مطابق لیست زیر تغییر دهید. فراموش نکنید که ارجاعی به سرویس هم اضافه کنید. روی پروژه کلاینت کلیک راست کرده و Add Service Reference را انتخاب نمایید. ممکن است پیش از آنکه بتوانید سرویس را ارجاع کنید، نیاز باشد پروژه سرویس را ابتدا اجرا کنید (کلیک راست روی پروژه سرویس و انتخاب گزینه Debug -> Start Instance).
class Program
{
    static void Main()
    {
        var client = new Service1Client();
        var payment = client.InsertPayment();
        client.DeletePayment(payment);
    }
}
اگر روی خط اول متد ()Main یک breakpoint قرار دهید می‌توانید مراحل ایجاد و حذف یک موجودیت Payment را دنبال کنید.


شرح مثال جاری

در مثال جاری برای بروز رسانی و حذف موجودیت‌های منفصل از الگویی رایج استفاده کرده ایم که در سرویس‌های WCF و Web API استفاده می‌شود.

در کلاینت با فراخوانی متد InsertPayment یک پرداخت جدید در دیتابیس ذخیره می‌کنیم. این متد، موجودیت Payment ایجاد شده را باز می‌گرداند. موجودیتی که به کلاینت باز می‌گردد از DbContext منفصل (disconnected) است، در واقع در چنین وضعیتی آبجکت context ممکن است در فضای پروسس دیگری قرار داشته باشد، یا حتی روی کامپیوتر دیگری باشد.

برای حذف موجودیت Payment از متد DeletePayment استفاده می‌کنیم. این متد به نوبه خود با فراخوانی متد Entry روی آبجکت context و پاس دادن موجودیت پرداخت بعنوان آرگومان، موجودیت را پیدا می‌کند. سپس وضعیت موجودیت را به EntityState.Deleted تغییر می‌دهیم که این کار آبجکت را برای حذف علامت گذاری می‌کند. فراخوانی‌های بعدی متد ()SaveChanges موجودیت را از دیتابیس حذف خواهد کرد.

آبجکت پرداختی که برای حذف به context الحاق کرده ایم تمام خاصیت هایش مقدار دهی شده اند، درست مانند هنگامی که این موجودیت به دیتابیس اضافه شده بود. اما از آنجا که از foreign key association استفاده می‌کنیم، تنها فیلدهای کلید موجودیت، خاصیت همزمانی (concurrency) و TimeStamp برای تولید عبارت where مناسب لازم هستند که نهایتا منجر به حذف موجودیت خواهد شد. تنها استثنا درباره این قاعده هنگامی است که موجودیت شما یک یا چند خاصیت از نوع پیچیده یا Complex Type داشته باشد. از آنجا که خاصیت‌های پیچیده، اجزای ساختاری یک موجودیت محسوب می‌شوند نمی‌توانند مقادیر null بپذیرند. یک راه حل ساده این است که هنگامی که EF مشغول ساختن عبارت SQL Delete لازم برای حذف موجودیت بر اساس کلید و خاصیت همزمانی آن است، وهله جدیدی از نوع داده پیچیده خود بسازید. اگر فیلدهای complex type را با مقادیر null رها کنید، فراخوانی متد ()SaveChanges با خطا مواجه خواهد شد.

اگر از یک independent association استفاده می‌کنید که در آن کثرت (multiplicity) موجودیت مربوطه یک، یا صفر به یک است، EF انتظار دارد که کلید‌های موجودیت‌ها بدرستی مقدار دهی شوند تا بتواند عبارت where مناسب را برای دستورات بروز رسانی و حذف تولید کند. اگر در مثال جاری از یک رابطه independent association بین موجودیت‌های Invoice و Payment استفاده می‌کردیم، لازم بود تا خاصیت پیمایشی Invoice را با وهله ای از صورت حساب مقدار دهی کنیم که خاصیت InvoiceId آن نیز بدرستی مقدار دهی شده باشد. در این صورت عبارت where نهایی شامل فیلدهای PaymentId, TimeStamp و InvoiceId خواهد بود.

نکته: هنگام پیاده سازی معماری‌های n-Tier با Entity Framework، استفاده از رویکرد Foreign Key Association برای موجودیت‌های مرتبط باید با ملاحظات جدی انجام شود. پیاده سازی رویکرد Independent Association مشکل است و می‌تواند کد شما را بسیار پیچیده کند. برای مطالعه بیشتر درباره این رویکردها و مزایا و معایب آنها به این لینک مراجعه کنید که توسط یکی از برنامه نویسان تیم EF نوشته شده است.

اگر موجودیت شما تعداد متعددی Independent Association دارد، مقدار دهی تمام آنها می‌تواند خسته کننده شود. رویکردی ساده‌تر این است که وهله مورد نظر را از دیتابیس دریافت کنید و آن را برای حذف علامت گذاری نمایید. این روش کد شما را ساده‌تر می‌کند، اما هنگامی که آبجکت را از دیتابیس دریافت می‌کنید EF کوئری جاری را بازنویسی می‌کند تا تمام روابط یک، یا صفر به یک بارگذاری شوند. مگر آنکه از گزینه NoTracking روی context خود استفاده کنید. اگر در مثال جاری رویکرد Independent Association را پیاده سازی کرده بودیم، هنگامی که موجودیت Payment را از دیتابیس دریافت می‌کنیم (قبل از علامت گذاری برای حذف) EF یک Object state entry برای موجودیت پرداخت و یک Relationship entry برای رابطه بین Payment و Invoice می‌ساخت. سپس وقتی که موجودیت پرداخت را برای حذف علامت گذاری می‌کنیم، EF رابطه بین پرداخت و صورت حساب را هم برای حذف علامت گذاری می‌کند. در اینجا عبارت where تولید شده مانند قبل، شامل فیلدهای PaymentId, TimeStamp و InvoiceId خواهد بود.

یک گزینه دیگر برای حذف موجودیت‌ها در Independent Associations این است که تمام موجودیت‌های مرتبط را مشخصا بارگذاری کنیم (eager loading) و کل Object graph را برای حذف به سرویس WCF یا Web API بفرستیم. در مثال جاری می‌توانستیم موجودیت صورتحساب مرتبط با موجودیت پرداخت را مشخصا بارگذاری کنیم. اگر می‌خواستیم موجودیت Payment را حذف کنیم، می‌توانستیم کل گراف را که شامل هر دو موجودیت می‌شود به سرویس ارسال کنیم. اما هنگام استفاده از چنین روشی باید بسیار دقت کنید، چرا که این رویکرد پهنای باند بیشتری مصرف می‌کند و زمان پردازش بیشتری هم برای مرتب سازی (serialization) صرف می‌کند. بنابراین هزینه این رویکرد نسبت به سادگی کدی که بدست می‌آید به مراتب بیشتر است.