مطالب
Test Driven Development #2
در مطلب قبل شما با TDD آشنا شدید اکنون بهتر است با یک مثال نشان دهم منظور از Test Driven Development چیست.  
برای شروع کافی است یک پروژه کنسول ساخته و Nunit را از طریق کنسول Nuget نصب کنید.
PM> Install-Package NUnit
معمولا برای کلاس‌های تست یک پروژه جدا در نظر گرفته می‌شود، ولی برای شروع می‌توانید از همان پروژه اصلی استفاده کنید.
پس از نصب شدن Nunit  می توانیم شروع به ساختن کلاس‌های تست کنیم:
  [TestFixture]
    public class HelloWorldTest
    {
    }

همانطور که ملاحظه می‌کنید کلاس ما با Attribute به نام TestFixture مزین شده است که خاص فریمورک Nunit است، در صورتی که از فریمورک دیگری برای تست استفاده می‌کنید باید تنظیمات مربوط به آن را انجام دهید.متدهای تست ما نیز با Attribute به نام  Test مزین می‌شوند.
  [Test]
   public void ShouldSayHelloWorld()
  {
  }

همانطور که دقت کردید متد ما به صورتی نام گذاری شده است که مشخص کننده کاری باشد که قرار است انجام دهد.این یکی دیگر از مزایای تست نویسی است که یک داکیومنت تقریباً کامل در طول تولید نرم افزار  ایجاد میشود.همچنین متد تست باید  غیر استاتیک با خروجی void  باشد .متد‌های تست بهتر است فقط یک موضوع را تست کنند، به طور مثال نباید هم اضافه شدن یک رکورد و هم ریدایرکت شدن به صفحه ای خاص را  تست کرد .
حالا وقت آن است که قبل از نوشتن کد اول تستش را بنویسیم.
        [Test]
        public void ShouldSayHelloWorld()
        {
            const string result = "Hello World";
            Assert.AreEqual(result, HelloWorld.SayHello());
        }

کلاس Assert  شامل توابعی بسیار قدرتمند است که مارا در اجرای تست بهتر کمک میکند.شامل متد هایی مانند .
  • AreEqual 
  • AreNotEqual 
  • AreNotSame 
  • AssertDoublesAreEqual 
  • Contains 
  • DoesNotThrow 
  • Equals 
  • Fail
  • Greater 
  • GreaterOrEqual 
  • Ignore 
  • IsEmpty 
  • IsInstanceOf 
  • IsNaN 
  • IsNotNull 
  • True 
  • ...
است.
هر کدام از متد‌های بالا کاربرد خاصی را دارند که به طور جداگانه به آن می‌پردازیم.
به علت وجود نداشتن کلاس HelloWorld  در زمان کامپایل با خطا مواجه می‌شویم.سپس کلاس مربوطه را ساخته و متد SayHello طوری پیاده سازی می‌کنیم که تست ما را پاس کند..(برنامه resharper برای اجرای متد‌های تست بسیار کار آمد است) 
    public class HelloWorld
    {
        public static string SayHello()
        {
            return "Hello World";
        }
    }

حال دوباره تست را اجرا کرده و می‌بینید که تست ما پاس شد.

نیازی به مرحله ریفکتورینگ نیست زیرا کلاس ما به اندازه کافی ساده است.
برای مقایسه بین Nunit و ابزار توکار ویژوال استودیو  می توانید به این سوال نگاهی بیاندازید.
در مطلب بعدی با استفاده از تست پذیری Mvc.net شروع به نوشتن تست هایی جدی‌تر خواهیم کرد.
مطالب
Test Driven Development
نوشتن  تست برای نرم افزار امری ضروریست، چه پس از تولید نرم افزار چه در حین تولید، در کل به وسیله تست می‌توان از به وجود آمدن باگ‌ها در هنگام گسترش دادن برنامه تا حد قابل توجهی جلوگیری کرد.
از معروف ترین روش‌های تست می‌توان عناوین زیر را نام برد:
  • Unit test
  • Integration test
  • Smoke test
  • Regression test
  • Acceptance test

 Test Driven Development  یک پروسه تولید نرم افزار است که برای اولین بار توسط Kent_Beck معرفی شد.
TDD شامل 4 مرحله کلی است:
  1. نوشتن تست قبل از نوشتن کد.
  2. کامپایل کردن کد و اطمینان از Fail شدن کامپایل.
  3. پیاده سازی کد به طوری که تست ما پاس شود.
  4. Refactoring 
مراحل 4 گانه تست باید به صورت متناوب اجرا شوند.
البته بسیاری این 4 مورد را با عبارت  red/green/refactor نیز می‌شناسند.

همانطور که گفته شد در کل نوشتن تست باعث می‌شود که با اضافه شدن کدهای جدید در برنامه از به وجود آمدن باگ تا حدی جلوگیری شود.
اما مزایای TDD:
  • TDD باعث کاهش زمان تولید نرم افزار می‌شود.البته این حرف کمی عجیب است.(در ادامه بیشتر توضیح می‌دهم)
  • اعتماد شما نسبت به کد بالا می‌رود.
  • باگ کمتری تولید می‌شود بتابراین اعتماد مصرف کنندگان نیز نسبت به برنامه شما بالا می‌رود.
  • باعث نظم در کد می‌شود.
  • باعث انعطاف پذیری بیشتر در نرم افزار می‌شود.
  • ریسک تولید نرم افزار به علت باگ کمتر به حداقل می‌سد.
  • ...

البته باید به این نکته نیز اشاره داشت که مایکروسافت تحقیقی انجام داده که بر طبق آن نوشتن کد به روش TDD می‌تواند 15 تا 30 در صد روند تولید نرم افزار را افزایش دهد ولی در عوض بین 40 تا 90 در صد می‌تواند از به وجود آمدن باگ جدید جلوگیری کند.
در بسیاری از محیط‌های برنامه نویسی، نه تنها به این موضوع اهمیت داده نمی‌شود بلکه به طور کلی به اشتباه گرفته شده و حتی در پروژه هایی که تست نوشته می‌شود مفاهیم آن( که در بالا نام برده شده )جابجا شده و به اشتباه نام برده می‌شود.
هدف از نوشتن تست،تست کردن قطعات کوچک کد است,به عنوان مثال نباید تست به گونه ای باشد که یک متد با 300 خط کد را تحت پوشش قرار دهد.ابتدا باید کد به قطعات کوچک شکسته و بعد تست شود.
یک نمونه از متد تست:
        [Test]
        public void TestFullName()
        {
            
            Person person = new Person ();
            person.lname = "Doe";
            person.mname = "Roe";
            person.fname = "John";

            string actual = person.GetFullName();
            string expected = "John Roe Doe";
            Assert.AreEqual(expected, actual, ”The GetFullName returned a different Value”);
        }
هدف از نوشتن این پست مقدمه ای بر شروع سری پست‌های TDD باستفاده از MVC.Net و فریم ورک قدرت مند تست Nunit است. 
مطالب
نحوه انتقال اطلاعات استخراج شده از وب سرویس به SQL Server به کمک SSIS
ممکن است در مواقعی نیاز به اطلاعات استخراج شده از وب سرویسی داشته باشید که در همان مقطع زمانی به آن دسترسی ندارید . مسلما برای این منظور باید آن اطلاعات را ذخیره کرده تا در صورت نیاز بتوان به آنها رجوع کرد . یکی از راه حل‌ها ذخیره آن در پایگاه داده (در اینجا Sql Server) است که در این پست به کمک امکانات BIDS در پکیج‌های SSIS و کوئری‌های SQL این مشکل را برطرف میکنیم. برای مشاهده نحوه استخراج اطلاعات از وب سرویس به اینجا مراجعه کنید .
تنها تفاوتی که در این پست در کار با سرویس با پست اشاره شده در بالا وحود دارد ذخیره اطلاعات استخراج شده است که در آن پست در یک فایل xml ذخیره شدند ولی در اینجا ما نیاز داریم تا اطلاعات را در یک متغیر با حوزه کاری Package ذخیره کنیم (به این معنی که مختص به همان flow نباشد و در تمام پکیج دیده شود) 

به دلیل اینکه هدر xml خروجی از سرویس دارای چندین namespace هست هنگام کار با آنها به مشکل خواهیم خورد. (هم هنگام کار با xml task‌ها و هم هنگام کار با xml در sql) 

به همین دلیل باید این قسمت از محتوا را حذف کرد . برای همین پس از گرفتن اطلاعات از سرویس آن را به کمک یک Script task حذف می‌کنیم 

در این مرحله اطلاعات استخراج شده را باید در SQL درج کنیم . برای همین ساختاری که باید اطلاعات را در SQL نگه دارد را در دیتابیس ایجاد می‌کنیم : 

جدول person برای نگهداری اطلاعات سرویس و XmlContainer برای نگهداری xml‌های سرویس .(برای داشتن History) 

برای درج هم از SP استفاده می‌کنیم :

 

و پیکربندی SQL Task :
 

نکته اول ایجاد کانکشن به پایگاه داده است که در اینجا از نمایش جزییات آن صرف نظر شده است . نکته دیگری پارامتر SP است که چون یک پارامتر دارد یک علامت سوال قرار میگیرد . اگر چند پارامتر بود به صورت علامت‌های سوال با ویرگول از هم جدا می‌شوند . ?,?,?,?
سپس در بخش parameter mapping به ترتیب مقدار دهی می‌شوند : 

و مقدار خروجی SP که شناسه آیتم درج شده است را در یک متغیر نگهداری می‌کنیم :
 

برای قدم بعد می‌خواهیم اطلاعات موجود در XML استخراج شده در پایگاه داده را در جدول مربوطه ذخیره کنیم . برای این کار این SP را می‌نویسیم : 
 

نکات مهم موارد زیر هستند : 
1 - استفاده از OpenXml برای parse کردن xml 
2 - استفاده از sp سیستمی sp_xml_preparedocument و sp_xml_removedocument (بیشتر ) 
3- اطلاعات شناسه و نام و نام خانوادگی inner text‌های نود‌های xml هستند . اگر این موارد به صورت attribute باشند باید قبل از نام آنها علامت @ قرار بگیرند. 
پس از ایجاد این Sp باید آن را در package فراخوانی کنیم : 

و پیکربندی این SQL Task :
 

و پارامتر‌های این SP :

و ذخیره نتیجه تراکنش در متغیری در پکیج :
 

و اکنون پکیج ما ظاهری شبیه به این مورد خواهد داشت :
 

 

در نهایت به عنوان یک facility می‌توانیم وضعیت تراکنش را به کاربر نمایش دهیم ( به کمک Script Task ) : 

و تمام ...

 

موفق باشید 

مطالب
بدست آوردن برگهای یک درخت توسط Recursive CTE
امروز در یک تالار سوالی مطرح شد با این عنوان "چگونه می‌توانم گره‌های برگ یک شاخه را بدست بیاورم". خب راه حلی که فورا به ذهنم رسید استفاده از یک query بازگشتی (recursive) بود.
به  ساختار سلسله مراتبی زیر توجه بفرمایید:

گره هایی که با رنگ سبز علامت گذاری شده اند را گره‌های برگ می‌نامیم چون که آن گره‌ها بدون زیر شاخه هستند.
فرض کنید از ما خواسته شده است با داشتن گره A تمام برگهای این شاخه را بدست بیاوریم.
دو مرحله را باید طی کنیم ابتدا تمام گره هایی که زیر شاخه گره A هستند را باید بدست آورد سپس توسط یک گزاره گره‌های برگ را فیلتر کنیم.

در واقع گره هایی برگ هستند که پدر هیچ گره‌ی دیگری نباشند. 

declare @t table
(id char(1) primary key,
parent char(1));

insert @t values 
('A',null),                                   --Level 1
('B', 'A'), ('C', 'A'),                       --Level 2
('D', 'B'), ('E', 'B'),('R','B'), ('F', 'C'), --Level 3
('G', 'D'),  --Level 4
('H', 'G'), ('I', 'G');                       --Level 5

;with cte as
(
select id, rnk=0 from @t
where parent = 'A'
 
union all
 
select t.id, rnk+1
from cte join @t t
on cte.id = t.parent
)
select *
  from cte
 where not exists
       (select *
          from @t
         where parent = cte.id);


و حالا به درخت زیر توجه بفرمایید:

هدف پیدا کردن برگ هایی از شاخه مورد نظر است که در پایین‌ترین سطح قرار گرفته باشند. برای این منظور از همان query بازگشتی استفاده کرده و با کمک تابع dense_ranke گره‌های مورد نظر را بدست میاوریم.
;with cte as
(
select id, rnk=0 from @t
where parent = 'A'

union all

select t.id, rnk+1
from cte join @t t
on cte.id = t.parent
)
select *
from
(
   select *, dense_rank() over(order by rnk desc) rk
     from cte
)t
where rk = 1


مطالب
ایجاد و ویرایش XSLT List View در Sharepoint Designer
XSLT List View یک کامپوننت قوی و قابل انعطاف در شیرپوینت است . این ویژگی جایگزین List View Web Part در شیرپوینت 2007 گردیده است .در این پست یک لیست در محیط توسعه SPD ایجاد کرده و آن را ویرایش کرده و تغغیراتی در نحوه نمایش آن می‌دهیم
  پس از اتصال به سرور در SPD ، یک لیست ایجاد می‌کنیم :
 

در پنجره ای که باز می‌شود ، لیست را نام گذاری می‌کنیم

 

سپس یک View ایحاد می‌کنیم . برای این کار از نوار Ribbon بالا ، روی List View کلیک می‌کینم :

 

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

 

در این مرحله نمای نگاهی به لیست در مرور گر می‌اندازیم و تعدادی داده برای آزمایش در آن وارد می‌کنیم

 

حال برای اعمال تغییرات به SPD برمیگردیم و روی View ساخته شده کلیک می‌کنیم تا وارد محیط ویرایش آن شویم .

 سپس برای اعمال فرمت سطر‌ها بر مبنای فیلترینگ ، روی Format Raw کلیک می‌کنیم
 

در این مثال می‌خواهم روی فیلد نام شرکت فیلتر انجام دهم

 

سپس برای تغییر نحوه نمایش ، روی Set Style کلیک می‌کنیم :

در اینجا می‌خواهم تمام سطر هایی که در خروجی فیلتر قرار دارند ، رنگی شوند :
 

پس از اعمال تغییرات می‌توانید تغییرات را در کد مشاهده کنید

 

برای اعمال تغییرات در خروجی و ستون‌های آن ، روی Add/Remove Columns کلیک می‌کنیم :

 

برای مثال در اینجا ستون Job Title را به لیست اضافه کردم :

 

همچنین برای اعمال Inline Editing به عناصر لیست می‌توانید روی دکمه ای با همین نام در Ribbon کلیک کنید

در ادامه می‌توانید عملیات مرتب سازی و گروه بندی اطلاعات را هم تنها با چند کلیک مدیریت کنید

 

در اینجا گروه بندی و مرتب سازی را روی نام شرکت اعمال می‌کنیم :

 

تا کنون خروجی پیش نمایش داده شده در SPD به این صورت است :

تغییرات را ذخیره می‌کنیم :

 

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

اکنون می‌خواهم قسمتی از تغییرات اعمال شده را از طریق مرور گر ( و نه SPD) تغییر دهم . برای این کار روی List در Ribbon کلیک می‌کنیم :

 

در قسمت Group by تنظیمات ستون را روی none قرار می‌دهیم تا گروه بندی اعمال نشود :

 

و در نهایت خروجی به این شکل خواهد بود

شیرپوینت 2007 امکان تغییر View را از طریق مرور گر بعد از ذخیره تغییرات در SPD نمی‌داد ولی در شیرپوینت 2010 همانطور که دیدید این امکان وجود دارد
 
مطالب
ایجاد لیستی از کلاسی جنریک
کلاس جنریک زیر را در نظر بگیرید:
public class Column<T>

{
public string Name { set; get; }
public T Data { set; get; }
}

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

IList<Column<T>> myList = new List<Column<T>>();



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

using System.Collections;


namespace Tests
{
public interface IColumn
{
string Name { set; get; }
object Data { set; get; }
}

public class Column<T> : IColumn
{
public string Name { set; get; }

public T Data { set; get; }

object IColumn.Data
{
get { return this.Data; }
set { this.Data = (T)value; }
}
}
}

ابتدا یک اینترفیس عمومی را همانند اعضای کلاس Column تعریف می‌کنیم که در آن بجای T از object‌ استفاده شده است. سپس یک پیاده سازی جنریک از این اینترفیس را ارائه خواهیم داد؛ با این تفاوت که اینبار خاصیت Data مربوط به اینترفیس، به صورت خصوصی و صریح با استفاده از IColumn.Data تعریف می‌شود و نمونه‌ی جنریک هم نام آن، عمومی خواهد بود.
اکنون می‌توان نوشت:

var myList = new List<IColumn>();


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

var myList = new List<IColumn>

{
new Column<int> { Data = 1, Name = "Col1"},
new Column<double> { Data = 1.2, Name = "Col2"}
};

خوب، تا اینجا یک مرحله پیشرفت است.اکنون اگر بخواهیم در این لیست، Data مثلا عنصری را که نامش Col1 است، دریافت کنیم چه باید کرد؟ آن هم نه به شکل object بلکه از نوع T مشخص:

static T GetColumnData<T>(IList<IColumn> list, string name)

{
var column = (Column<T>)Convert.ChangeType(list.Single(s => s.Name.Equals(name)), typeof(Column<T>), null);
return column.Data;
}

و نمونه‌ای از استفاده آن:

int data = GetColumnData<int>(myList, "Col1");


مطالب
فعال سازی سطح دوم کش در Fluent NHibernate

سطح اول کش در NHibernate در یک تراکنش معنا پیدا می‌کند (+)؛ اما نتایج حاصل از اعمال سطح دوم (+) آن، در اختیار تمام تراکنش‌های جاری برنامه خواهند بود. در ادامه قصد داریم نحوه فعال سازی سطح دوم کش NHibernate را توسط Fluent NHibernate بررسی کنیم.

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

ب) اعمال به قسمت تعاریف اولیه
پس از دریافت اسمبلی NHibernate.Caches.SysCache.dll و افزودن ارجاعی به آن، اکنون نوبت به معرفی آن به تنظیمات Fluent NHibernate‌ می‌باشد. این‌کار هم بسیار ساده است:
...
.ConnectionString(x => x.FromConnectionStringWithKey(...))
.Cache(x => x.UseQueryCache()
.UseMinimalPuts()
.ProviderClass<NHibernate.Caches.SysCache.SysCacheProvider>())
...

ج) تعریف نوع کش در هنگام ایجاد نگاشت‌ها
اگر از ClassMap‌ها برای تعریف نگاشت‌ها استفاده می‌کنید، در انتهای تعاریف یک سطر Cache.ReadWrite را اضافه کنید.
اگر از AutoMapping استفاده می‌کنید، نیاز است تا با استفاده از IAutoMappingOverride (+) سطر یاد شده اضافه گردد؛ برای مثال:
using FluentNHibernate.Automapping.Alterations;

namespace NH3Test.MappingDefinitions.Domain
{
public class AccountOverrides : IAutoMappingOverride<Account>
{
public void Override(FluentNHibernate.Automapping.AutoMapping<Account> mapping)
{
mapping.Cache.ReadWrite();
}
}
}
تعریف یک سطر فوق هم مهم است؛ زیرا در غیراینصورت فقط primary key حاصل از بار اول فراخوانی کوئری‌های مرتبط کش می‌شوند؛ نه نتیجه عملیات. هرچند این مورد هم یک قدم مثبت به شمار می‌رود از این لحاظ که برای مثال تهیه نتایج کوئری بر روی فیلدی که ایندکس بر روی آن تعریف نشده است همیشه از حالت تهیه کوئری بر روی فیلد دارای ایندکس کندتر است. اما هدف ما در اینجا این است که پس از بار اول فراخوانی کوئری، بار‌های دوم و بعدی دیگر کوئری خاصی را به بانک اطلاعاتی ارسال نکرده و نتایج از کش خوانده شوند (جهت استفاده عموم کاربران در کلیه تراکنش‌های جاری برنامه).

د) اعمال متد Cacheable به کوئر‌ی‌ها
سه مرحله قبل نحوه برپایی مقدماتی سطح دوم کش را بیان می‌کنند و تنها یکبار نیاز است انجام شوند. در ادامه هر جایی که نیاز داشتیم نتایج کوئری مورد نظر کش شوند (و باید دقت داشت که این کش شدن سطح دوم به معنی در دسترس بودن نتایج آن جهت تمام کاربران برنامه در تمام تراکنش‌های جاری برنامه هستند؛ برای مثال نتایج آمار سایت که دسترسی عمومی دارد) تنها کافی است متد Cacheable را به کوئری مورد نظر اضافه کرد؛ برای مثال:
var data = session.QueryOver<Account>()
.Where(s => s.Name == "name")
.Cacheable()
.List();

ه) چگونه صحت اعمال سطح دوم کش را بررسی کنیم؟
برای بررسی این امر باید به خروجی SQL نهایی مراجعه کرد (+). سه تراکنش مجزا را تعریف کنید. در تراکنش اول یک insert ساده، در تراکنش دوم کوئری از اطلاعات قبل (به همراه اعمال متد Cacheable) و در تراکنش سوم مجددا همان کوئری تراکنش دوم را (به همراه اعمال متد Cacheable) تکرار کنید. حاصل کار تنها باید دو عبارت SQL باشند. یک مورد جهت insert و یک مورد هم select . در تراکنش سوم، از نتایج کش شده تراکنش دوم استفاده خواهد شد؛ به همین جهت دیگری کوئری سومی به بانک اطلاعاتی ارسال نخواهد شد.
اگر اعمال مورد (ج) فوق را فراموش کنید، سه کوئری را مشاهده خواهید کرد، اما کوئری سوم با کوئری دوم اندکی متفاوت خواهد بود و بهینه‌تر؛ چون به صورت هوشمند بر اساس جستجوی بر روی primary key تغییر کرده است (صرفنظر از اینکه قسمت where کوئری شما چیست).

مطالب
نحوه‌ی فعال سازی library caching زمانیکه یک Silverlight library را تولید کرده‌ایم

در مورد کاهش حجم فایل‌های XAP سیلورلایت زمانیکه از اسمبلی‌های کتابخانه‌های دیگر مانند Silverlight toolkit استفاده می‌شود، در این فصل بحث شده است و راه حل، استفاده از گزینه‌ی reduce XAP size by using application library caching است. به این صورت کاربران دیگر به ازای هر بار مشاهده‌ی سایت نیازی نخواهند داشت تا یک سری کتابخانه‌ی کمکی را که هیچ تغییری در آن‌ها حاصل نخواهد شد، دریافت کنند و اطلاعات آن‌ها از cache مرورگر خوانده می‌شود. این مورد با کتابخانه‌ها و ابزارهای کمکی تولید شده توسط مایکروسافت کار می‌کند. اما اگر خودتان یک Silverlight library را تولید کنید، چنین اتفاقی رخ نخواهد داد و باز هم فایل اسمبلی کتابخانه‌ی شما درون فایل XAP اصلی برنامه قرار گرفته و خبری از caching مجزای آن نیست. چرا اینطور است؟ چکار باید کرد؟!
علت آن بر می‌گردد به نحوه‌ی پیاده سازی library caching در VS.NET و Silverlight . برای این منظور چند مرحله باید طی شود تا این قابلیت برای کتابخانه‌های ساخت خودمان نیز فعال گردد:
الف) به کتابخانه‌ی خود باید امضای دیجیتال اضافه کنید:
اینکار با استفاده از امکانات خود VS.NET بسیار ساده است. به خواص پروژه مراجعه کنید. سپس برگه‌ی Signing را باز کرده و گزینه‌ی Sign the assembly را انتخاب کنید (شکل زیر). در قسمت choose a strong name key file ، گزینه‌ی new را انتخاب کرده و پس از وارد کردن یک نام دلخواه و گذر واژه‌ای، فایل pfx امضای دیجیتال اسمبلی شما تولید خواهد شد. اکنون تنها کافی است یکبار دیگر برنامه را کامپایل کنید.


ب) به یک فایل extMap.xml هم نیاز است:
هنگام پیاده سازی قابلیت library caching ، VS.NET به دنبال فایلی به نام AssemblyFileName.extmap.xml دقیقا در کنار فایل اسمبلی مورد نظر می‌گردد. ساختار عمومی این فایل XML به صورت زیر است:

<?xml version="1.0"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<assembly>
<name>SLHelper</name>
<version>1.0.0.0</version>
<publickeytoken>f265933def965412</publickeytoken>
<relpath>SLHelper.dll</relpath>
<extension downloadUri="SLHelper.zip" />
</assembly>
</manifest>

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



جهت ساده سازی قسمت (ب)، برنامه‌ی کمکی را از آدرس ذیل می‌توانید دریافت کنید:
Utility: Extmap Maker

برای مطالعه بیشتر
Silverlight 3: Cached Assemblies and you can to

مطالب
جمع آوری آمار لینک‌های خروجی از سایت توسط Google analytics

چندی قبل مطلب کوتاهی را در مورد Google analytics نوشتم. در حین جستجو درباره‌ی jQuery در وب، به نحوه ردیابی لینک‌های خروجی از سایت توسط Google analytics برخوردم که نحوه پیاده سازی آن به صورت زیر است.
بدیهی است قبل از هر کاری باید اسکریپت مربوط به Google analytics را به انتهای صفحه و جایی که تگ body بسته می‌شود اضافه کنید (قابل دریافت درقسمت Add Website Profile . شماره این اسکریپت برای هر پروفایلی که ایجاد می‌کنید متفاوت است).
سپس:
الف) افزودن ارجاعی از کتابخانه jQuery به هدر صفحه که آن‌را در مطلب شمسی کردن تاریخ بلاگر ملاحظه کردید.
ب) افزودن چند سطر زیر به هدر صفحه
<script type="text/javascript">
$(document).ready(function() {
$("a").click(function() {
var $a = $(this);
var href = $a.attr("href");

// see if the link is external
if ( (href.match(/^http/)) && (! href.match(document.domain)) ) {

// if so, register an event
var category = "outgoing";
var event = "click";
var label = href;

pageTracker._trackPageview('/outgoing/' + href);
pageTracker._trackEvent(category, event, href);
}
});
});
</script>

البته اگر قبلا اسکریپت شمسی کردن تاریخ بلاگر را اضافه کرده بودید فقط محتویات تابع document.ready را باید اضافه کنید (جهت مشاهده نمونه اعمال شده، روی صفحه جاری کلیک راست کنید و سورس صفحه را مشاهده نمائید).

توضیحاتی در مورد کد فوق:
این اسکریپت به روال رخ داد گردان onclick هر لینکی که به خارج از سایت ختم می‌شود (مثلا لینک به یک فایل یا یک سایت خارجی (خارج از سایت))، به صورت خودکار تابع trackPageview مربوط به Google analytics را اضافه می‌کند. این کار تاثیری در عملکرد سایت ندارد و کاربر چیزی را متوجه نخواهد شد، اما به این طریق لینک‌های خروجی در آمار Google analytics ظاهر می‌شوند (مطابق تصاویر زیر).





از این پس آمار تمام لینک‌های خروجی از سایت ، متمایز شده با outgoing ، جمع آوری و نمایش داده خواهند شد.

امکانات بیشتری مانند event tracking نیز قرار است به Google analytics اضافه شود که هنوز در مرحله آزمایشی است و بر روی تمامی اکانت‌ها فعال نشده است.

نظرات اشتراک‌ها
روش امن نگهداری پسورد کاربران
پیاده سازی روش گفته شده در این سایت :
/* 
 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
 * Copyright (c) 2013, Taylor Hornby
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Text;
using System.Security.Cryptography;

namespace PasswordHash
{
    /// <summary>
    /// Salted password hashing with PBKDF2-SHA1.
    /// Author: havoc AT defuse.ca
    /// www: http://crackstation.net/hashing-security.htm
    /// Compatibility: .NET 3.0 and later.
    /// </summary>
    public class PasswordHash
    {
        // The following constants may be changed without breaking existing hashes.
        public const int SALT_BYTE_SIZE = 24;
        public const int HASH_BYTE_SIZE = 24;
        public const int PBKDF2_ITERATIONS = 1000;

        public const int ITERATION_INDEX = 0;
        public const int SALT_INDEX = 1;
        public const int PBKDF2_INDEX = 2;

        /// <summary>
        /// Creates a salted PBKDF2 hash of the password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <returns>The hash of the password.</returns>
        public static string CreateHash(string password)
        {
            // Generate a random salt
            RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
            byte[] salt = new byte[SALT_BYTE_SIZE];
            csprng.GetBytes(salt);

            // Hash the password and encode the parameters
            byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
            return PBKDF2_ITERATIONS + ":" +
                Convert.ToBase64String(salt) + ":" +
                Convert.ToBase64String(hash);
        }

        /// <summary>
        /// Validates a password given a hash of the correct one.
        /// </summary>
        /// <param name="password">The password to check.</param>
        /// <param name="correctHash">A hash of the correct password.</param>
        /// <returns>True if the password is correct. False otherwise.</returns>
        public static bool ValidatePassword(string password, string correctHash)
        {
            // Extract the parameters from the hash
            char[] delimiter = { ':' };
            string[] split = correctHash.Split(delimiter);
            int iterations = Int32.Parse(split[ITERATION_INDEX]);
            byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
            byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

            byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
            return SlowEquals(hash, testHash);
        }

        /// <summary>
        /// Compares two byte arrays in length-constant time. This comparison
        /// method is used so that password hashes cannot be extracted from
        /// on-line systems using a timing attack and then attacked off-line.
        /// </summary>
        /// <param name="a">The first byte array.</param>
        /// <param name="b">The second byte array.</param>
        /// <returns>True if both byte arrays are equal. False otherwise.</returns>
        private static bool SlowEquals(byte[] a, byte[] b)
        {
            uint diff = (uint)a.Length ^ (uint)b.Length;
            for (int i = 0; i < a.Length && i < b.Length; i++)
                diff |= (uint)(a[i] ^ b[i]);
            return diff == 0;
        }

        /// <summary>
        /// Computes the PBKDF2-SHA1 hash of a password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="iterations">The PBKDF2 iteration count.</param>
        /// <param name="outputBytes">The length of the hash to generate, in bytes.</param>
        /// <returns>A hash of the password.</returns>
        private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
        {
            Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
            pbkdf2.IterationCount = iterations;
            return pbkdf2.GetBytes(outputBytes);
        }
    }
}