مطالب
نکاتی درباره پرس و جو با استفاده از پردازش موازی

برای انجام عملیات  پرس و جوی LINQ با استفاده از روش پردازش موازی به راحتی میتوان الحاقیه AsParallel را به هر داده‌ای از نوع IEnumerable<T> افزود: 

var data = 
new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// پرس و جوی عادی
var q1 = from i in data select i;
// پرس و جو به شیوه موازی
var q2 = from i in data.AsParallel() select i;

الحاقیه .AsParallel() در پرس و جویq2  نسخه موازی LINQ  را بر روی متغیر data  اجرا میکند و اگر همه چیز به صورت صحیح انجام شود هر دو پرس و جو باید نتایج یکسانی داشته باشند، اما نتایج عبارتند از :

//نتیجه اجرای q1
0 1 2 3 4 5 6 7 8 9 10
//نتیجه اجرای q2
0 6 1 7 2 8 3 9 4 10 5

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

حال اگر چگونگی ترتیب داده‌ها، برایمان مهم است به دو روش می‌توانیم آن را انجام دهیم:

1- افزودن عبارت orderby به پرس و جو

2- استفاده از الحاقیه AsOrdered 

var q3 = from i in data.AsParallel() orderby i  select i;

var q4 = from i in data.AsParallel().AsOrdered() select i;

که نتیجه انجام هر دو پرس و جوی بالا یکی خواهد بود. حال مسأله دیگر این است که آیا همیشه استفاده از پردازش موازی مفید خواهد بود یا خیر؟پاسخ این سؤال وابسته است به نوع مسأله و حجم داده مورد نظر و مشخصات سیستمی که قرار است از آن کد استفاده کند. چگونگی اندازه سرعت و مقدار مصرف حافظه در اجرای چهار پرس و جوی فوق  در کامپیوتر من با پردازنده Intel Q9550 به شکل زیر است:

نتیجه تست پرس و جوی موازی

مطالب
مدیریت Instance در WCF
نحوه پیاده سازی و مدیریت Instance در پروژه‌های مبتنی بر WCF

نکته : آشنایی اولیه با مفاهیم WCF جهت درک صحیح مطالب الزامی است.

تشریح مسئله :  در صورتی که نیاز باشد که نمونه ساخته شده از سرویس (سمت سرور) به صورت Singleton  باشد بهترین روش برای پیاده سازی به چه صورت است.

برای شروع ابتدا مثال زیر را پیاده سازی می‌کنیم.
یک Contract به صورت زیر تعریف می‌کنیم:
[ServiceContract(SessionMode=SessionMode.Allowed)]
    public interface IMyService
    {
        [OperationContract]
        int GetData();             
    }

حالا یک سرویس برای پیاده سازی Interface بالا می‌نویسیم.
[ServiceBehavior( InstanceContextMode = InstanceContextMode.PerCall )]
    public class PerCallService : IMyService
    {
        int count;
        public int GetData()
        {
            return ++count;
        }
    }
همانطور که از نام سرویس مشخص است از این سرویس به ازای هر فراخوانی یک نمونه سمت سرور ساخته می‌شود.
حالا برای مشاهده نتیجه یک پروژه ConsoleApplication ایجاد کنید و سرویس مورد نظر را از روش AddServiceReference به پروژه اضافه کرده در فایل Program کد‌های زیر را کپی کنید.
 static void Main( string[] args )
        {
            Console.WriteLine( "PerCall Service" );

            MyPerCallService.MyServiceClient client = new MyPerCallService.MyServiceClient();
            int count = 0;
            for ( int i = 0 ; i < 5 ; i++ )
            {
                count = client.GetData();              
            }          
            Console.WriteLine( count );
            Console.ReadLine();         
        }
بعد از اجرا خروجی به صورت زیر است:

بعد از 5 بار فراخوانی متد GetData باز خروجی دارای مقدار 1 است. یعنی به ازای هر بار فراخوانی متد GetData یک نمونه از سرویس مورد نظر ساخته می‌شود.این عمل توسط خصوصیت InstanceContextMode که از نوع PerCall است به سرویس اعمال میشود.

حالا یک سرویس دیگر به صورت زیر ایجاد کنید.

 [ServiceBehavior( InstanceContextMode = InstanceContextMode.Single )]
    public class SingleService : IMyService
    {
        int count;
        public int GetData()
        {
            return ++count;
        }
    }
تنها تفاوت این سرویس با سرویس قبلی در این است که InstanceContextMode این سرویس  به صورت Single معرفی شده است. یعنی به ازای n فراخوانی فقط یک نمونه از کلاس ساخته می‌شود. این سرویس رو هم مثل روش قبلی به Client Application اضافه کنید.
کد کلاس Program رو به صورت زیر تغییر دهید.

static void Main( string[] args )
        {
            Console.WriteLine( "Single Service" );

            MySingleService.MyServiceClient client = new MySingleService.MyServiceClient();
            int count = 0;
            for ( int i = 0 ; i < 5 ; i++ )
            {
                count = client.GetData();              
            }          
            Console.WriteLine("Result is : {0}", count );
            Console.ReadLine();         
        }
که بعد از اجرا خروجی به صورت زیر است.

به ازای 5 بار فراخوانی سرویس متغیر Count سمت سرور مقدار قبلی خود را حفظ کرده است.

مطالب
عدم امکان تغییر اطلاعات مدل در HTML Helpers پس از Postback در ASP.NET MVC
یک مثال ساده برای شرح مساله
در اینجا مدل User، کنترلری به نام Home و View متناظر با آن را ملاحظه می‌کنید:
namespace ModelStateTest.Models
{
    public class User
    {
        public string Email { set; get; }
    }
}

using System.Web.Mvc;
using ModelStateTest.Models;

namespace ModelStateTest.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(User model)
        {           
            model.Email = "?";
            return View(model);
        }
    }
}

@model ModelStateTest.Models.User

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>User</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Email)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Email)
            @Html.ValidationMessageFor(model => model.Email)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
نکته‌ای که در اینجا مدنظر است، سطر زیر می‌باشد:
 model.Email = "?";
پیش از اینکه برنامه را اجرا کنید، به نظر شما پس از postback به سرور، چه اطلاعاتی در Html.EditorFor تعریف شده در View برنامه نمایش داده خواهد شد؟
احتمالا عنوان می‌کنید که خوب ... همان مقدار علامت سؤال انتساب داده شده. اما ... اینچنین نیست! دقیقا همان مقداری که در حین Postback به سرور ارسال شده، نمایش داده می‌شود.
این مورد نکته‌ای است که عدم آشنایی با آن ممکن است چندین ساعت را به دیباگ یک برنامه اختصاص دهد، بدون اینکه نتیجه مفیدی حاصل شود.

مطابق نظر طراحان اصلی ASP.NET MVC، اینکار و این رفتار، دقیقا به همین نحو صحیح است و باگ نیست.
«فرض کنید در فیلدی عددی، کاربر عبارت «تست» را وارد کرده است. نیاز است در خطای اعتبار سنجی پس از Postback به او عنوان کنیم، لطفا بجای «تست»، عدد وارد کنید. چون خاصیت متناظر قید شده در مدل، عددی است، مقدار «تست» وارد شده را از دست خواهیم داد. به همین جهت همان مقدار اولیه وارد شده را در HTML Helpers پس از Postback حفظ می‌کنیم.»


راه حل‌های ممکن، برای به روز رسانی وضعیت مدل پس از Postback

الف) استفاده از متد ModelState.Clear
این متد کلیه داده‌های موجود در ModelState را منجمله خطاهای حاصل از اعتبارسنجی، حذف می‌کند. در این حالت مطابق مثال فوق پس از Postback، مقدار علامت سؤال نسبت داده شده به خاصیت ایمیل، نمایش داده خواهد شد.

ب) استفاده از متد ModelState.Remove
 this.ModelState.Remove("Email");
این حالت نیز مانند حالت الف است، با این تفاوت که اطلاعات اعتبار سنجی و سایر موارد مرتبط را حذف نمی‌کند.

ج) عدم استفاده از HTML Helpers
این مورد را فقط با متدهای کمکی For دار، مانند Html.EditorFor مشاهده خواهید کرد. اگر نحوه تعریف را به شکل زیر تغییر دهیم، نیازی به استفاده از متد ModelState.Remove نخواهد بود. البته، مزیت‌های استفاده از HTML Helpers دارای متدهای For دار را که Strongly typed هستند، از دست می‌دهیم.
 <input type="text" name="Email" id="Email" value="@Model.Email" />
مطالب
نمایش یک فایل PDF پویا در یک iframe در ASP.NET
عموما در برنامه‌های وب برای نمایش فایل‌های پویای باینری تولید شده، یا ابتدا آن‌ها را بر روی سخت دیسک ذخیره کرده و مسیر نهایی را به نحوی به کاربر نمایش می‌دهند و یا فایل را بدون ذخیره سازی، در مرورگر کاربر اصطلاحا Flush می‌کنند. حالت Flush سبب نمایش صفحه دیالوگ ذخیره سازی فایل گردیده و در همینجا Response خاتمه خواهد یافت.
برای نمونه در اینجا توسط متد inMemoryFile، یک فایل PDF در حافظه تشکیل شده و سپس به صورت یک Byte Array بازگشت داده می‌شود. در ادامه کار، این اطلاعات در مرورگر کاربر Flush خواهد شد:
using System.IO;
using System.Net.Mime;
using System.Web;

namespace WebApplication
{
    public class PdfHandler : IHttpHandler
    {
        private static byte[] inMemoryFile()
        {
            //تولید پویای فایل در حافظه و یا حتی خواندن از یک نمونه موجود
            return File.ReadAllBytes(@"D:\path\DynamicCrosstabSampleRpt.pdf");
        }

        public void ProcessRequest(HttpContext context)
        {
            var pdf = inMemoryFile();

            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.ContentType = MediaTypeNames.Application.Pdf;
            context.Response.AddHeader("Content-Length", pdf.Length.ToString());
            context.Response.AddHeader("content-disposition", "attachment;filename=test.pdf");
            context.Response.Buffer = true;
            context.Response.Clear();
            context.Response.OutputStream.Write(pdf, 0, pdf.Length);
            context.Response.OutputStream.Flush();
            context.Response.OutputStream.Close();
            context.Response.End();
        }

        public bool IsReusable
        {
            get  {  return false;  }
        }
    }
}
و برای نمایش آن در یک iframe در صفحه:
 <iframe width="100%" src="PdfHandler.ashx" height="200px"></iframe>
نتیجه کار، نمایش صفحه دیالوگ ذخیره سازی فایل به کاربر است:


سؤال: فرض کنید Adobe reader بر روی سیستم نصب است و مرورگر با استفاده از Active-X آن می‌تواند این نوع فایل‌ها را نمایش دهد. آیا راهی وجود دارد تا بجای نمایش save popup dialog، این فایل توسط مرورگر نمایش داده شود؟
پاسخ: بلی. در کدهای فوق تنها کافی است یک سطر آن تغییر کند:
 Response.AddHeader("content-disposition", "inline;filename=test.pdf");
در اینجا تنها نحوه مقدار دهی content-disposition تفاوت کرده است. حالت attachment سبب نمایش save popup dialog می‌شود و مقدار inline، فایل را در مرورگر نمایش خواهد داد.
اینبار اگر برنامه را اجرا کنیم، iframe ایی که به PdfHandler.ashx اشاره می‌کند، فایل PDF را در صفحه نمایش می‌دهد.
مطالب
تبدیل فایل‌های pfx به snk
مرسوم است و توصیه شده است که جهت ارائه کتابخانه‌های دات نتی خود از امضای دیجیتال استفاده کنید. VS.NET برای این منظور در برگه signing خواص یک پروژه، چنین امکانی را به صورت توکار ارائه می‌دهد.
حال اگر بخواهیم همین پروژه را به صورت سورس باز ارائه دهیم، استفاده کنندگان نهایی به مشکل برخواهند خورد؛ زیرا فایل pfx حاصل، توسط کلمه عبور محافظت می‌شود و در سایر سیستم‌ها بدون درنظر گرفتن این ملاحظات قابل استفاده نخواهد بود.
معادل فایل‌های pfx، فایلهایی هستند با پسوند snk که تنها تفاوت مهم آن‌ها با فایل‌های pfx، عدم محافظت توسط کلمه عبور است و ... برای کارهای خصوصا سورس باز انتخاب مناسبی به شمار می‌روند. اگر دقت کنید، اکثر پروژه‌های سورس باز دات نتی موجود در وب (مانند NHibernate، لوسین، iTextSharp و غیره) از فایل‌های snk برای اضافه کردن امضای دیجیتال به کتابخانه نهایی تولیدی استفاده می‌کنند و نه فایل‌های pfx محافظت شده.
در اینجا اگر فایل pfx ایی دارید و می‌خواهید معادل snk آن‌را تولید کنید، قطعه کد زیر چنین امکانی را مهیا می‌سازد:
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace PfxToSnk
{
    class Program
    {
        /// <summary>
        /// Converts .pfx file to .snk file.
        /// </summary>
        /// <param name="pfxData">.pfx file data.</param>
        /// <param name="pfxPassword">.pfx file password.</param>
        /// <returns>.snk file data.</returns>
        public static byte[] Pfx2Snk(byte[] pfxData, string pfxPassword)
        {
            var cert = new X509Certificate2(pfxData, pfxPassword, X509KeyStorageFlags.Exportable);
            var privateKey = (RSACryptoServiceProvider)cert.PrivateKey;
            return privateKey.ExportCspBlob(true);
        }

        static void Main(string[] args)
        {
            var pfxFileData = File.ReadAllBytes(@"D:\Key.pfx");
            var snkFileData = Pfx2Snk(pfxFileData, "my-pass");
            File.WriteAllBytes(@"D:\Key.snk", snkFileData);
        }
    }
}
 
مطالب
ایجاد لیستی از کلاسی جنریک
کلاس جنریک زیر را در نظر بگیرید:
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");


مطالب
سطح دوم cache در NHibernate

عموما دو الگوی اصلی caching در برنامه‌ها وجود دارند: cache aside و cache trough .
در الگوی cache trough ، سیستم caching داخل DAL (که در اینجا همان NHibernate است)، تعبیه می‌شود؛ مانند سطح اول caching که پیشتر در مورد آن صحبت شد. در این حالت cache از دید سایر قسمت‌های برنامه مخفی است و DAL به صورت خودکار آن‌را مدیریت می‌کند.
در الگوی cache aside ، کار مدیریت سیستم caching دستی است و خارج از NHibernate قرار می‌گیرد و DAL هیچگونه اطلاعی از وجود آن ندارد. در این حالت لایه caching موظف است تا هنگام به روز شدن بانک اطلاعاتی، اطلاعات خود را نیز به روز نماید. این لایه عموما توسط سایر شرکت‌ها یا گروه‌ها برنامه نویسی تهیه می‌شود.
NHibernate جهت سهولت کار با این نوع cache providers خارجی، نقاط تزریق ویژه‌ای را تدارک دیده است که به second level cache معروف است. هدف از second level cache فراهم آوردن دیدی کش شده از بانک اطلاعاتی است تا فراخوانی‌های کوئری‌ها به سرعت و بدون تماس با بانک اطلاعاتی صورت گیرد.
در حال حاضر (زمان نگارش این مطلب)، entity framework این لایه‌ی دوم caching یا به عبارتی دیگر، امکان تزریق ساده‌تر cache providers خارجی را به صورت توکار ارائه نمی‌دهد.
در NHibernate طول عمر second level cache در سطح session factory (یا به عبارتی طول عمر تمام برنامه) تعریف می‌شود و برخلاف سطح اولیه caching محدود به یک سشن نیست. در این حالت هر زمانیکه یک موجودیت به همراه ID منحصربفرد آن تحت نظر NHibernate قرارگیرد و همچنین سطح دوم caching نیز فعال باشد، این موجودیت در تمام سشن‌های برنامه بدون نیاز به مراجعه به بانک اطلاعاتی در دسترس خواهد بود (بنابراین باید دقت داشت که هدف از این سیستم، کار سریعتر با اطلاعاتی است که سطح دسترسی عمومی دارند).

در ادامه لیستی از cache providers خارجی مهیا جهت استفاده در سطح دوم caching را ملاحظه می‌نمائید:

  • AppFabric Caching Services : بر اساس Microsoft's AppFabric Caching Services که یک پلتفرم caching محسوب می‌شود (+). (این پروژه پیشتر به نام Velocity معروف شده بود و قرار بود تنها برای ASP.NET ارائه شود که سیاست آن به گونه‌ای جامع‌تر تغییر کرده است)
  • MemCache : بر اساس سیستم معروف MemCached تهیه شده است (+).
  • NCache : (+)
  • ScaleOut : (+)
  • Prevalence : (+)
  • SysCache : بر اساس همان روش آشنای متداول در برنامه‌های ASP.NET به کمک System.Web.Caching.Cache کار می‌کند؛ یا به قولی همان IIS caching
  • SysCache2 : همانند SysCache است با این تفاوت که SQL dependencies ویژه SQL Server را نیز پشتیبانی می‌کند.
  • SharedCache : یک سیستم distributed caching نوشته شده برای دات نت است (+).
این موارد و پروایدرها جزو پروژه‌ی nhcontrib در سایت سورس فورج هستند (+).


مطالب تکمیلی:



مطالب
کتابچه‌ی رایگان نصب و راه اندازی Exchange Server 2010

در طی این چند سالی که کارم برنامه نویسی ASP.Net بوده است، هیچ نرم افزار پایه‌ای همانند Exchange server در کیفیت کارهای من تاثیر نداشته است و اساسا تمام هماهنگی‌های برنامه‌های من با کمک Exchange server و Outlook صورت می‌گیرد. از گرفتن تائید تا آلارم فلان درخواست تا یک سری از گزارشات زمانبندی شده و غیره. دیگر کار به جایی رسیده است که اگر کاربران ایمیلی را دریافت نکنند اطلاعات وارد شده در برنامه‌ها را معتبر نمی‌دانند و به برنامه‌ها مراجعه نمی‌کنند!
به همین منظور کتابچه‌ی راهنمای نصب و راه اندازی Exchange server 2010 را تهیه کرده‌ام که در طی حدودا 120 صفحه، به صورت کاربردی و ساده، آنچه را که باید برای راه اندازی و مدیریت این برنامه در یک سازمان بدانید، در اختیار شما قرار می‌دهد.

مقدمه‌ی کتابچه

برنامه‌ی Exchanger server ، نرم افزار ارسال و دریافت ایمیل سازمانی مایکروسافت است که در رده‌ی محصولات سرور آن شرکت قرار می‌گیرد. اولین نگارش Exchanger server در سال 1993 ارائه شد و به صورت محدود در اختیار حدود 500 شرکت قرار گرفت. در سال 1996 اولین نگارش عمومی آن به نام Exchange server 4.0 به عموم ارائه گشت و در سال 1997 با ارائه‌ی Exchange server 5.0 آمار مشتریان این محصول به 32 هزار کاربر رسید. به همراه این نگارش، اولین نسخه‌ی برنامه Outlook web access نیز ارائه گردید. با افزایش استقبال از این محصول، در همان سال نگارش 5.5 آن نیز ارائه شد و ظرفیت بانک‌های اطلاعاتی آن تا 16TB در نگارش سازمانی آن افزایش یافت. در سال 2000، اولین نگارش یکپارچه‌ی آن با Active directory ارائه شد. بزرگترین مشکل این محصول در سال 2000، کوچ از نگارش‌های قدیمی به نگارش جدید بودند که این مشکل در سال 2003 با ارائه Exchange server 2003 برطرف گردید. در اواخر سال 2006 ، Exchange server 2007 ارائه گشت که مهمترین تفاوت آن با نگارش‌های قبلی، ارائه‌ی آن تنها برای سکوهای 64 بیتی بود به همراه بهبودهایی در اندازه‌ی صندوق‌های پستی تا 2 گیگابایت، تضمین فعالیت بی‌وقفه بهبود یافته ، شروع به کنار گذاری Public folders و تمهیداتی جهت ارسال و دریافت پیغام‌های صوتی. در اواخر سال 2009 نگارش 2010 این محصول ارائه گشت که تنها با ویندوز سرور 2008 سازگار می‌باشد و در آن عمده‌ی مشکلات به همراه نگارش 2007 آن برطرف شده است همانند Outlook web access سازگار با تمامی مرورگرهای امروزی، امکان مدیریت تحت وب صندوق‌های پستی کاربران، بازنگری در گزینه‌های تضمین فعالیت بی‌وقفه و ساده سازی آن‌ها، افزایش تعداد بانک‌های اطلاعاتی قابل تعریف در یک سرور و بسیاری از موارد دیگر که در طی چندین فصل به بررسی آن‌ها خواهیم پرداخت.

لینک دریافت: exchange2010.v1.rar

مطالب
یکی از مزایای استفاده از SVN در یک پروژه تک نفره

حتما لازم نیست که در یک تیم برنامه نویسی مشغول به کار باشید تا به یک سورس کنترل نیاز پیدا کنید. در ادامه یکی از مزایای استفاده از SVN را با هم مرور خواهیم کرد.

چند روز قبل هنگام کار با VS.Net ، ناگهان IDE‌ کرش کرد. (از لطایف استفاده از یک دو جین افزونه و ضعف در برنامه نویسی یکی از این‌ها که می‌تواند سبب ناپایدار شدن IDE شود)
پس از کرش با صفحه‌ی زیر مواجه شدم!


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

چون همیشه از SVN به عنوان سورس کنترل استفاده می‌کنم، به سادگی چند کلیک مشکل برطرف شد.
برای این‌کار می‌توان به صورت زیر عمل کرد:
الف) کلیک راست بر روی فایل frmMain.Designer.cs (این فایل تعاریف رابط کاربر فرم تخریب شده را در خود دارد)
ب) سپس انتخاب گزینه‌ی Showlog از منوی افزونه‌ی Visual SVN (شکل زیر)



اکنون صفحه‌ی گزارش تاریخچه‌ی ریز عملیات صورت گرفته بر روی این فایل ظاهر می‌شود:



در ادامه می‌توان بر روی یکی از سطرهای ظاهر شده در گزارش کلیک راست کرد و گزینه‌ی compare with working copy را انتخاب نمود (شکل زیر):



سپس ابزار diff ظاهر شده و می‌توان به سادگی تفاوت فایل تخریب شده فعلی و فایل سالم چند نگارش قبل را مشاهده نمود:



همانطور که در تصویر مشخص است، فایل مورد استفاده (working copy) در دو نقطه اساسی که مربوط به اضافه کردن منوها است تخریب شده. سمت چپ نگارش قدیمی است و سمت راست نگارش فعلی تخریب شده.
اکنون برای اصلاح کد تخریب شده فقط کافی است روی قسمت رنگی سمت راست کلیک راست کرده و گزینه copy to right‌ را انتخاب کنیم. به این صورت در اسرع وقت و به سادگی هر چه تمام‌تر یک فایل تخریب شده به روز اول یا حداقل به یک نگارش قبل بازگشت پیدا کرده و مشکل حل می‌شود. (البته در این مورد تخریب فرم، پس از انجام اصلاح فوق، یکبار باید IDE را کاملا بست و مجددا آنرا گشود تا نتیجه ظاهر شود)



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


مطالب
استفاده از Google Analytics در ASP.Net

قبل از استفاده از بلاگر، در سایت wordpress وبلاگ داشتم، که به‌دلایلی کنسل شد. تفاوت محسوسی را که اینجا مشاهده می‌کنم، نبود قسمت آمار سایت است. در سایت wordpress آمار مبسوطی را از بازدید کنندگان سایت می‌توانید در کنترل پنل مدیریتی وبلاگ مشاهده کنید، اما در اینجا خیر.
به همین جهت اولین کاری را که انجام دادم استفاده از سرویس رایگان persianstat بود که انصافا هم با کیفیت است و قابل مقایسه با آماری که wordpress ارائه می‌دهد، می‌باشد.
جالب اینجا است که هر چند هاست اینجا، گوگل است اما استفاده‌ی خودکار از ابزار Google analytics در آن مهیا نیست. احتمالا علت آن آماده نبودن API آن است که قرار است به زودی ارائه شود، بنابراین ارزش وقت گذاشتن را دارد.



برای استفاده از Google analytics ، پس از ثبت نام و ورود به آن، سایت مورد نظر را معرفی کرده (در قسمت Add Website Profile) و نهایتا یک کد جاوا اسکریپتی به شما خواهد داد که می‌توانید آنرا به صفحات مورد نظر خود در سایت اضافه نمائید تا تحت کنترل آماری قرار گیرد. محدودیتی هم در مورد تعداد سایت وجود ندارد و با یک اکانت می‌توانید چندین سایت را معرفی کرده و تحت کنترل قرار دهید.
اگر از ASP.Net استفاده می‌کنید، تنها کافی است به master page سایت مراجعه کنید و پیش از بسته شدن تگ body ، اسکریپت مربوط به Google analytics را اضافه کنید تا تمام سایت را تحت کنترل قرار دهید.
یا اگر علاقمند بودید که اینکار را به صورت "شیک‌تری" انجام دهید، می‌توان از این http module استفاده کرد. به این صورت ابتدا تگ بسته شدن body به صورت خودکار پیدا شده و سپس اسکریپت به پیش از آن اضافه می‌شود.
این روش بار بزرگ تهیه آمار سایت را حذف خواهد کرد. عموما دیتابیس جمع آوری آمار سایت خیلی زود (برای مثال پس از گذشت 6 ماه) حجیم می‌شود و تاثیر مشهودی را بر روی کارآیی سایت خواهد گذاشت. بنابراین، این سؤال مطرح می‌شود که چرا گوگل اینکار را برای ما انجام ندهد؟! هزینه بانک اس کیوال سرور بر روی هاست‌های اینترنتی بالا بوده و حجمی را هم که در اختیار قرار می‌دهند محدود است. در صورت نیاز به حجم‌های بالاتر باید هزینه بیشتری را پرداخت کرد. بنابراین هم از لحاظ قیمت و هچنین کارآیی سایت، استفاده از این سرویس واقعا مقرون به صرفه است. بعلاوه از تنوع آماری که ارائه می‌دهد نیز نمی‌توان چشم پوشی کرد. برای مثال کاربران چه واژه‌های کلیدی را در موتورهای جستجو وارد کرده‌اند تا به سایت شما رسیده‌اند؟ چند درصد کاربر وفادار دارید؟! (کاربرهای وفادار، منظور افرادی هستند که به صورت منظم به سایت سر می‌زنند) و امثال این. انصافا تهیه چنین ماژولی برای یک سایت از لحاظ برنامه نویسی شاید با برنامه نویسی کل یک سایت برابری کند.
اگر هم نیاز به یک برنامه سورس باز داشتید که هر روز به اکانت Google analytics شما سر بزند و اطلاعات آنرا استخراج کرده و در یک بانک SQL server ذخیره کند، می‌توانید به پروژه سی شارپ زیر مراجعه نمائید:
Google Analytics Data Extractor

البته باید دقت داشت که پس از ارائه API کامل Google analytics ، دیگر نیازی به این نوع روش‌های ابتکاری وجود نداشته و استخراج داده از آن بسیار ساده‌تر خواهد شد.