مطالب
یافتن مقادیر نال در Entity framework
کلاس شخص زیر را درنظر بگیرید
public class Person
{
        public int Id { get; set; }                
        public string Name { get; set; }
        public int? Age { get; set; }
}
در اینجا با توجه به اینکه Name از نوع string است، خودبخود به فیلدی نال‌پذیر نگاشت خواهد شد و همچنین Age عددی نیز در سمت کدهای ما Nullable است، بنابراین خاصیت سن هم به فیلدی نال‌پذیر نگاشت می‌شود.
اگر تمام مراحل متداول ایجاد Context را طی کنیم، به نظر شما خروجی SQL عبارت زیر چه خواهد بود؟
string name = null;
var list1 = ctx.Users.Where(x => x.Name == name).ToList();
در این عبارت، name به صورت یک متغیر ارسال شده است و نه یک مقدار ثابت (فرض کنید یک متد را تعریف کرده‌اید که name را به صورت پارامتر دریافت می‌کند).
خروجی SQL آن به نحو زیر است:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age]
FROM [dbo].[People] AS [Extent1]
WHERE [Extent1].[Name] = @p__linq__0
-- p__linq__0 (dbtype=String, size=-1, direction=Input) = null
به عبارتی خروجی مورد انتظار name is null را تولید نکرده است و کوئری ما حداقل با SQL Server نتیجه‌ای را به همراه نخواهد داشت. در مورد Age نیز به همین صورت است.


راه حل:

برای حالت Age، روش زیر خروجی age is null را تولید می‌کند:
 var list2 = ctx.Users.Where(x => !x.Age.HasValue).ToList();
و یا استفاده از object.Equals نیز مشکل را برطرف خواهد کرد:
int? age = null;
var list2 = ctx.Users.Where(x => object.Equals(x.Age, age)).ToList();
برای حالت Name رشته‌ای می‌توان از روش زیر استفاده کرد:
 var list1 = ctx.Users.Where(x => string.IsNullOrEmpty(x.Name)).ToList();
و یا روش کلی‌تر زیر نیز جواب می‌دهد:
string name = null;
var list1 = ctx.Users.Where(x => name == null ? x.Name == null : x.Name == name).ToList();
کاری که در اینجا انجام شده استفاده از x.Name == null در حالت نال بودن name است. از این جهت که EF با کوئری ذیل به علت عدم استفاده از پارامتر برای معرفی مقداری نال، مشکلی ندارد:
 var list1 = ctx.Users.Where(x => x.Name == null).ToList();

 
مطالب
آموزش MDX Query - قسمت اول

در طول این سری آموزش‌های MDX (البته هنوز نمی‌دانم چند قسمت خواهد بود) تلاش خواهم کرد تمامی موارد موجود در MDX‌ها را به طور کامل با شرح و توضیح مناسب پوشش دهم.

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

در قسمت اول این آموزش‌ها در نظر دارم در ابتدا مفاهیم اولیه OLAP و همچنین مفاهیم مورد نیاز در Multi Dimentional Data Base  ها برای شما عزیزان توضیح دهم و در قسمت‌های بعدی این مجموعه در خصوص MDX Query‌ها صحبت خواهم کرد.

انباره داده (Data Warehouse)

عملا یک یا چند پایگاه داده می‌باشد که اطلاعات تجمیع شده از دیگر پایگاه‌های داده را درخود نگه داری می‌کند. برای ارایه گزارشاتی که از پایگاه داده‌های OLTP نمی‌توانیم به راحتی بگیریم.

(OLTP (Online transaction processing

سیستم پردازش تراکنش بر‌خط می‌باشند . که عملا همان سیستم هایی می‌باشند که در طول روز دارای تغییرات بسیار زیادی می‌باشند (مانند سیستم‌های حسابداری، انبار داری و ... که در طول روز دایما دارای تغییرات در سطح داده می‌باشند.)

(OLAP (OnLine Analysis Processing 

این سیستم‌ها خدماتی در نقش تحلیل‌گر داده و تصمیم گیرنده ارائه می‌‌کند. چنین سیستمهایی می‌‌توانند، داده را در قالبهای مختلف برای هماهنگ کردن نیازهای مختلف کاربران مختلف، سازماندهی کنند.

تفاوت انبار داده (Data Warehouse) و پایگاه داده(Data Base)

وظیفه اصلی سیستم‌های پایگاه‌داده کاربردی OnLine ،پشتیبانی از تراکنش‌های بر‌خط و  پردازش کوئری است. این سیستم‌ها، سیستم پردازش تراکنش بر‌خط(OLTP)  نامیده می‌شوند و بیشتر عملیات روزمره یک سازمان را پوشش می‌‌دهند. از سوی دیگر انبار‌داده، خدماتی در نقش تحلیل‌گر داده و تصمیم گیرنده ارائه می‌‌کند. چنین سیستمهایی می‌‌توانند داده را در قالبهای مختلف برای هماهنگ کردن نیازهای مختلف کاربران مختلف، سازماندهی و ارائه می‌کند. این سیستم‌ها با نام سیستم‌های پردازش تحلیلی بر‌خط (OLAP) شناخته‌می‌شوند.

موارد تفاوت انبار داده (Data Warehouse) و پایگاه داده(Data Base)

• از لحاظ مدل‌های داده: پایگاه‌های داده برای مدل  OLTP بهینه سازی شده‌است. که بر اساس مدل داده رابطه‌ای امکان پردازش تعداد زیادی تراکنش همروند، که اغلب حاوی رکورد‌های اندکی هستند را دارد. اما در انبارهای داده که برای پردازش تحلیلی بر خط، طراحی شده‌اند امکان پردازش تعداد کمی کوئری پیچیده بر روی تعداد بسیار زیادی رکورد داده فراهم می‌شود. سرورهای OLAP می‌توانند از دو نوع رابطه‌ای  (ROLAP)  یا چند‌بعدی باشند (MOLAP).
• از لحاظ کاربران: کاربران پایگاه‌داده کارمندان دفتری و مسؤولان هستند در حالی‌که کاربران انبار‌داده مدیران و تصمیم‌گیرنده‌ها هستند.
• از لحاظ عملیات قابل اجرا بر روی آن‌ها: عملیات انجام شده برروی پایگاه‌های داده عمدتا عملیات (Select/Insert/Update/Delete) می‌باشد ، در حالی که عملیات روی انبار داده عمدتا Select  ها می‌باشند.
• از لحاظ مقدار داده‌ها: مقدار داده‌های یک پایگاه‌داده در حدود چند مگابایت تا چند گیگابایت است در حالی که این مقدار در انبار داده در حدود چند گیگابایت تا چند ترابایت است.
• از لحاظ زمان پرس و جو : به طور کلی سرعت پرس و جو  ها روی انباره‌ی داده بسیار بالاتر از کوئری مشابه آن روی پایگاه داده می‌باشد.
مراحل ساخت یک انباره‌ی داده (Data WareHouse) به شرح زیر می‌باشد 



• پاکسازی داده (Data Cleansing)

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

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

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

در این بخش عملیات مختلفی برای پاکسازی داده‌ها  قابل انجام است:

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

یکپارچه سازی داده‌ها از سه فاز کلی تشکیل شده است:
• شناسایی فیلدهای یکسان: فیلدهای یکسان که در جدول‌ها ی مختلف دارای نامهای مختلف میباشند. 

• شناسایی افزونگی‌ها ی موجود در داده‌ها ی ورودی:  داده‌های ورودی گاهی دارای افزونگی است. مثلا بخشی از رکورد در جداول مختلف وجود دارد.

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


• تبدیل داده‌ها(Data Transformation)
در این فاز، داده‌های ورودی طی مراحل زیر به شکلی که مناسب عمل داده کاوی باشند، در می‌آیند:
• از بین بردن نویز داده¬ها(Smoothing)
• تجمیع داده¬ها(Aggregation)
• کلی¬سازی(Generalization)
• نرمال¬سازی(Normalization)
• افزودن فیلدهای جدید
در ادامه به شرح  هر یک می‌پردازیم:
1. از بین بردن نویزهای داده ای(Smoothing): منظور از  داده‌های نویزی، داده هایی هستند که در خارج از بازه مورد نظر قرار می‌گیرند. مثلا اگر بازه حقوقی کارمندان بین یک صد هزار تومان و یک میلیون تومان باشد، داده‌های خارج از این بازه به عنوان داده‌های نویزی شناخته شده و در این مرحله اصلاح می‌گردند. برای اصلاح داده‌های نویزی از روشهای زیر استفاده می‌شود:
• استفاده از مقادیر مجاور برای تعیین یک مقدار مناسب برای فیلدهای دارای نویز
• دسته بندی داده‌های موجود و مقداردهی فیلد دارای داده نویزی با استفاده از دسته نزدیکتر
• ترکیب روشهای فوق با ملاحظات انسانی، در این روش، اصلاح مقادیر نویزی با استفاده از یکی از روشهای فوق انجام می‌گیرد اما افرادی برای بررسی و اصلاح نیز وجود دارند
2. تجمیع داده ها(Aggregation): تجمیع داده‌ها به معنی بدست آوردن اطلاعات جدید از ترکیب داده‌های موجود می‌باشد. به عنوان مثال بدست فروش ماهانه از حساب فروش‌های روزانه.
3. کلی سازی(Generalization): کلی سازی به معنی دسته بندی داده‌های موجود براساس ماهیت و نوع آنها است. به عنوان مثال می‌توان اطلاع رده‌های سنی خاص (جوان، بزرگسال، سالخورده) را از فیلد سن استخراج کرد. 
4. نرمال سازی(Normalization): منظور از نرمال سازی، تغییر مقیاس داده‌ها است. به عنوان مثالی از نرمال سازی، می‌توان به تغییر بازه یک فیلد از مقادیر موجود به بازه 0 تا 1 اشاره کرد.

5. افزودن فیلدهای جدید: گاهی اوقات برای سهولت عمل داده کاوی می‌توان فیلدهایی به مجموعه فیلدهای موجود اضافه کرد. مثلا می‌توان فیلد میانگین حقوق کارمندان یک شعبه را به مجموعه فیلدهای موجود اضافه نمود.

• کاهش داده‌ها(Reduction)

در این مرحله، عملیات کاهش داده‌ها انجام می‌گیرد که شامل تکنیکهایی برای نمایش کمینه اطلاعات موجود است

. این فاز از سه بخش  تشکیل می‌شود:

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

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

مدل داده‌ای رابطه‌ای (Relational) وچند بعدی (Multidimensional)  :

1. مدل داده رابطه‌ای (Relational data modeling)  بر اساس دو مفهوم اساسی موجودیت (entity)  و رابطه (relation) بنا نهاده شده است. از این رو آن را با نام مدل ER نیز می‌شناسند.

• موجودیت (entity) : نمایانگر همه چیزهایی که در پایگاه داده وجود خارجی دارند یا به تصور در می‌آیند. پدیده‌ها دارای مشخصاتی هستندکه به آن‌ها صفت (attribute) گفته می‌شود.

• رابطه (relation) : پدیده‌ها را به هم می‌پیوندد و چگونگی در ارتباط قرار گرفتن آن‌ها با یکدیگر را مشخص می‌کند.

2. مدل داده چند‌بعدی ( Multidimensional modeling ) یا MD بر پایه دو ساختار جدولی اصلی بنا نهاده شده است: 



• جدول حقایق (Fact Table)

• جداول ابعاد (Dimension Table)


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

جدول حقایق : قلب حجم داده‌ای ما را تشکیل می‌دهد و شامل دو سری فیلد است : کلیدهای خارجی به ابعاد و شاخص‌ها (Measure). 

شاخص‌ها (Measure) : معیارهایی هستند که بر روی آن‌ها تحلیل انجام می‌گیرد و درون جدول حقایق قرار دارند. شاخص‌ها قبل از شکل‌گیری انبار داده توسط مدیران و تحلیل‌گران به دقت مشخص می‌‌شوند. چون در مرحله کار با انبار اطلاعات اساسی هر تحلیل بر اساس همین شاخص‌ها شکل می‌گیرد. شاخص‌‌ها تقریباً همیشه مقادیر عددی را شامل می‌شوند. مثلا برای یک فروشگاه زنجیره‌ای این شاخص‌ها می‌توانند واحدهای فروخته‌شده کالاها و مبلغ فروش به تومان باشند.

بعد (Dimension) : هر موجودیت در این مدل می‌تواند با یک بعد تعریف شود. ولی بعدها با موجودیت‌های مدل ER متفاوتند زیرا آن‌ها سازمان شاخص‌ها را تعیین می‌کنند. علاوه بر این دارای یک ساختار سلسله مراتبی هستند و به طور کلی برای حمایت از سیستم‌های تصمیم گیری سازمان‌دهی شده‌اند.

اجزای بعدها member نام دارند و تقریباٌ همه بعدها، memberهای خود را در یک یا چند سطح سلسله مراتبی (hierarchies) سازمان‌دهی می‌نمایند، که این سلسله مراتب نمایانگر مسیر تجمیع (integration) و ارتباط بین سطوح پایین‌تر (مثل روز) و سطوح بالاتر (مثل ماه و سال) است. وقتی یک دسته از memberهای خاص با هم مفهوم جدیدی را ایجاد می‌‌کنند، به آنها یک سطح (Level) می‌گوییم. ( مثلاٌ هر سی روز را ماه می‌‌گوییم. در این حالت ماه یک سطح است. ) 

حجم‌های داده‌ای (Data Cube)

حجم‌های داده‌ای یا Cube از ارتباط تعدادی بعد با تعدادی شاخص تعریف می‌‌شود. ترکیب memberهای هر بعد از حجم داده‌ای فضای منطقی را تعریف می‌کند که در آن مقادیر شاخص‌ها  ظاهر می‌‌شوند. هر بخش مجزا که شامل یکی از memberهای بعد در حجم داده‌ای است ، سلول (cell) نامیده‌می‌شود. سلول‌ها شاخص‌های مربوط به تجمیع‌های مختلف را در خود نگهداری می‌نمایند. در واقع مقادیر مربوط به حقایق (Fact) که در جدول حقایق (Fact) تعریف می‌شوند در حجم داده‌ای (Data Cube) در سلول‌ها (Cell) نمایان می‌گردند.

     

شماهای داده‌ای (Data Schema)  : سه نوع Schema در طراحی Data Warehouse وجود دارد 

1. Stare
2. Snowflake
3. Galaxy
1. شمای ستاره‌ای (Star Schema) : متداولترین شما، همین شمای‌ستاره‌ای است. که در آن انبار‌داده با استفاده از اجزای زیر تعریف می‌شود:
• یک جدول مرکزی بزرگ به نام جدول حقایق که شامل حجم زیادی از داده‌های بدون تکرار است.

• مجموعه‌ای از جدول‌های کمکی کوچک‌تر به نام‏ جدول بعد ، که به ازای هر بعد یکی از این جداول موجود خواهد بود.

• شکل این شما به صورت یک ستاره است که جدول حقایق در مرکز آن قرار گرفته و هر یک از ‏ جداول بعد‏ به وسیله شعاع‌هایی به آن مربوط هستند.

مشکل این مدل احتمال پیشامد افزونگی در آن است.

2. شمای دانه‌برفی ( Snowflake Schema ) : در واقع شمای دانه‌برفی، نوعی از شمای ستاره‌ای است که در آن بعضی از ‏ جداول بعد نرمال شده‌اند. و به همین خاطر دارای تقسیمات بیشتری به شکل جداول اضافی می‌باشد که از ‏ جداول بعد‏ جدا شده‌اند. 

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

3. شمای کهکشانی (galaxy schema) : در کاربرد‌های پیچیده برای به اشتراک گذاشتن ابعاد نیاز به جداول حقایق چندگانه احساس می‌شود که یک یا چند ‏ جدول بعد‏ را در بین خود به اشتراک می‌گذارند. این نوع شما به صورت مجموعه‌ای از شماهای ستاره‌ای است و به همین دلیل شمای کهکشان یا شمای منظومه‌ای نامیده‌می‌شود. این شما به ما این امکان را می‌دهد که جداول بعد بین جداول حقایق مختلف به اشتراک گذاشته شوند.

عملیات بر روی حجم‌های داده‌ای :

• Roll Up  (یا Drill-up) : با بالا رفتن در ساختار سلسله مراتبی مفهومی یک حجم داده‌ای، یا با کاهش دادن بعد، یک مجموعه با جزئیات کمتر (خلاصه شده) ایجاد می‌نماید. بالا رفتن در ساختار سلسله مراتبی به معنای حذف قسمتی از جزئیات است. برای مثال اگر قبلاٌ بعد مکان بر حسب شهر بوده آن را با بالا رفتن در ساختار سلسله مراتبی بر حسب کشور درمی‌آوریم. ولی وقتی با کاهش دادن بعد سروکار داریم منظور حذف یکی از ابعاد و جایگزین کردن مقادیر کل است. در واقع همان عمل تجمیع (aggregation) است. 
• Drill Down : بر عکس عملRoll-up است و از موقعیتی با جزئیات داده‌ای کم به جزئیات زیاد می‌رود. این کار با پایین آمدن در ساختار سلسله مراتبی( به سمت جزئیات بیشتر) یا با ایجاد ابعاد اضافی انجام می‌گیرد.

نمونه‌ای از عملیات Drill Down و Roll Up

• Slice : با انتخاب و اعمال شرط بر روی یکی از ابعاد یک subcube به شکل یک برش دو بعدی ایجاد می‌کند. در واقع همان عمل انتخاب (select) است.

• Dice : با انتخاب قسمتی از ساختار سلسله مراتبی بر روی دو یا چند بعد یک subcube ایجاد می‌نماید.

نمونه‌ای از عملیات Dice و Slice

• Pivot (یا Rotate) : این عملیات بردارهای بعد را در ظاهر می‌چرخاند.

نمونه‌ای از عملیات pivot

• Drill-across : نتیجه اجرای کوئری‌هایی که نتیجه اجرای آنها حجم‌های داده‌ایهای مرکب با بیش از یک fact-table است.

• Ranking : سلول‌هایی را باز می‌گرداند که در بالا یا پایین شرط خاصی واقع هستند. مثلاٌ ده محصولی که بهترین فروش را داشته‌اند.

سرورهای OLAP :

در تکنولوژیOALP داده‌ها به دو صورت چند‌بعدی (Multidimensional OLAP) (MOLAP) و رابطه‌ای (Relational OLAP) (ROLAP) ذخیره می‌شوند. OLAP پیوندی(HOLAP) تکنولوژیی است که دو نوع قبل را با هم ترکیب می‌کند.

MOLAP : روشی است که معمولاٌ برای تحلیل‌های OLAP در تجارت مورد استفاده قرار می‌گیرد. در MOLAP، داده‌ها با ساختار یک حجم داده‌ای ( Data Cube ) چند بعدی ذخیره می‌شوند. ذخیره‌سازی در پایگاه‌داده‌های رابطه‌ای انجام نمی‌گیرد، بلکه با یک فرمت خاص انجام می‌شود. اغلب محصولات موفق MOLAP از یک روش چند‌بعدی استفاده می‌نمایند که در آن یک سری حجم‌های داده‌ای کوچک، انبوه و از پیش محاسبه‌شده، یک حجم داده‌ای بزرگ (hypercube  ) را می‌سازند. 

علاوه بر‌این MOLAP به شما امکان می‌دهد داده‌های دیدهای (View) تحلیل‌گران را دسته بندی کنید، که این در حذف اشتباهات و برخورد با ترجمه‌های پرغلط کمک بزرگی است.

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

از آنجا که یک کپی از داده‌های منبع در کامپیوتر Analysis server ذخیره‌می‌شود، کوئری‌‌ها می‌توانند بدون مراجعه به منابع مجدداً محاسبه شوند. کامپیوتر Analysis server ممکن است کامپیوترسرور که تقسیم بندی‌ها در آن انجام شده یا کامپیوتر دیگری باشد. این امر بستگی به این دارد که تقسیم‌بندی‌ها در کجا تعریف شده‌اند. حتی اگر پاسخ کوئری‌ها از روی تقسیمات تجمیع (integration) شده قابل دستیابی نباشند، MOLAP سریع‌ترین پاسخ را فراهم می‌کند. سرعت انجام این کار به طراحی و درصد تجمیع تقسیم‌بندی‌ها بستگی دارد. 

مزایا : کارایی عالی-  حجم‌های داده‌ای MOLAP برای بازیابی سریع داده‌ها ساخته شده‌اند و در فعالیت‌های slice و dice به صورت بهینه پاسخ می‌دهند. ترکیب سادگی و سرعت مزیت اصلی MOLAP است.

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

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

ROLAP : محدودیت MOLAP در حجم داده‌های قابل پرس‌و‌جو و نیاز به روشی که از داده‌های ذخیره‌شده به روش رابطه‌ای حمایت کند، موجب پیشرفت ROLAP شد.

مبنای این روش کارکردن با داده‌هایی که در پایگاه‌داده‌های رابطه‌ای ذخیره‌شده‌اند، برای انجام اعمال slicing و dicing معمولی است. با استفاده از این مدل ذخیره‌سازی می‌توان داده‌ها را بدون ایجاد واقعی تجمیع در پایگاه‌داده‌های رابطه‌ای به هم مربوط کرد.

مزایا : با این روش می‌توان به حجم زیادی از داده‌ها را رسیدگی کرد. محدودیت حجم داده در تکنولوژی ROLAP مربوط به محدودیت حجم داده‌های قابل ذخیره‌سازی در پایگاه‌داده‌های رابطه‌ای است. به بیان دیگر، خود ROLAP هیچ محدودیتی بر روی حجم داده‌ها اعمال نمی‌کند.

معایب : ممکن است کارایی پایین بیاید. زیرا هر گزارش ROLAP در واقع یک کواِری SQL (یا چند کواِری SQL )در پایگاه داده‌های رابطه‌ای است و اگر حجم داده‌ها زیاد باشد ممکن است زمان پاسخ کواِری طولانی شود. در مجموع ROLAP سنگین است، نگهداری آن سخت است و کند هم هست. بخصوص زمانی که نیاز به آدرس دهی جدول‌های ذخیره شده در سیستم چند بعدی داریم.

این محدودیت ناشی از عملکرد SQL است. زیرا تکنولوژی ROLAP بر پایه عبارات مولد SQL برای پرسش و پاسخ بر روی پایگاه داده رابطه‌ای است و عبارات SQL به همه نیازها پاسخ نمی‌دهند (مثلاٌ محاسبه حساب‌های پیچیده در SQL مشکل است)، بنابراین فعالیت‌های ROLAP به آن چه SQL قادر به انجام آن است محدود می‌گردد. 

تفاوت ROALP و MOLAP : تفاوت اصلی این دو در معماری آن‌ها است. محصولات MOLAP داده‌های مورد نیاز را در یک حافظه نهان (cache) مخصوص می‌گذارد. ولی ROLAP تحلیل‌های خود را بدون استفاده از یک حافظه میانی انجام می‌دهد، بدون آن که از یک مرحله میانی برای گذاشتن داده‌ها در یک سرور خاص استفاده کند. 

با توجه به کند بودن ROLAP در مقایسه باMOLAP ، باید توجه داشت که کاربرد این روش بیشتر در پایگاه داده‌های بسیار بزرگی است که  گاه‌گاهی پرس و جویی بر روی آن‌ها شکل می‌گیرد، مثل داده‌های تاریخی و کمتر جدید سال‌‌های گذشته.

نکته: اگر از Analysis Services که به وسیله Microsoft OLE DB Provider مهیا شده استفاده می‌کنید، تجمیع‌ها نمی‌توانند برای تقسیم‌بندی از روش ROLAP استفاده نمایند.

HOLAP : با توجه به نیاز رو به رشدی که برای کارکردن با داده‌های بلادرنگ (real time) در بخش‌های مختلف در صنعت و تجارت احساس می‌شود، مدیران تجاری انتظار دارند بتوانند با دامنه وسیعی از اطلاعات که فوراً و بدون حتی لحظه‌ای تأخیر در دسترس باشند، کار کنند. در حال حاضر شبکه اینترنت و سایر کاربرد‌ها یی که به داده‌هایی از منابع مختلف مراجعه دارند و نیاز به فعالیت با یک سیستم بلادرنگ هم دارند، همگی از سیستم HOLAP بهره می‌گیرند.

named set :

Named Set مجموعه‌ای از memberهای بعد یا مجموعه‌ای از عبارات است که برای استفاده مجدد ایجاد می‌شود.

Calculated member 

Calculated Memberها memberهایی هستند که بر اساس داده‌ها نیستند بلکه بر اساس عبارات ارزیابی MDX هستند. آنها دقیقاَ به سبک سایر memberهای معمولی هستند. MDX یک مجموعه قوی از عملیاتی را تامین میکند که میتوانند برای ساختCalculated Memberها مورد استفاده قرار گیرند به طوری که به شما امکان داشتن انعطاف زیاد در کار کردن با داده‌های چند بعدی را بدهد. 

امیدوارم در این قسمت با مفاهیم نخستین OLAP آشنا شده باشید.

تلاش خواهم کرد در قسمت بعدی در خصوص نصب SQL Server Analysis Services و نصب پایگاه داده‌ی Adventure Work DW 2008 شرح کاملی را ارایه کنم.

 

مطالب
آشنایی با ساختار IIS قسمت سوم
همانطور که در مطلب قبلی گفتم، در این مطلب قرار است به WAS بپردازیم؛ در دنباله متن قبلی گفتیم که دومین وظیفه WWW Service این است: موقعی‌که یک درخواست جدید در صف درخواست‌ها وارد شد، به اطلاع WAS برساند.

WAS یا Windows Process Activation Service
در نسخه 7 به بعد، WAS مدیریت پیکربندی application pool و پروسه‌های کارگر را به جای WWW Service به عهده گرفته است. این مورد شما را قادر می‌سازد تا همان پیکربندی که برای Http در نظر گرفته‌اید، بر روی درخواست هایی که Http نیستند هم اعمال کنید. همچنین موقعی که سایت شما نیازی به درخواست‌های Http ندارد می‌توانید WAS را بدون WWW Service راه اندازی کنید. به عنوان یک مثال فرض کنید شما یک وب سرویس WCF را از طریق WCF Listener Adapter مدیریت می‌کنید و احتیاجی به درخواست‌های نوع Http listener ندارید و http.sys کاری برای انجام ندارد پس نیازی هم به راه اندازی www service نیست.

پیکربندی مدیریتی در WAS
در زمان شروع کار IIS، سرویس WAS اطلاعاتی را از فایل ApplicationHost.config می‌خواند و آن‌ها را به دست listener adapter‌های مربوطه می‌رساند و lsitener adapter‌ها ارتباط بین WAS و listener‌های مختلف را در IIS، برقرار می‌کنند. آداپتورها اطلاعات لازم را از WAS می‌گیرند و به listener‌های مربوطه انتقال می‌دهند تا listener‌ها بر اساس آن تنظیمات یا پیکربرندی‌ها، به درخواست‌ها گوش فرا دهند.
در مورد WCF ، ابتدا WAS تنظیمات را برای آداپتور WCF که NetTcpActivator نام دارد ارسال کرده و این آداپتور بر اساس آن  listener مربوطه را پیکربندی کرده تا به درخواست هایی که از طریق پروتوکل net.tcp می‌رسد گوش فرا دهد.
لیست زیر تعدادی از اطلاعاتی را که از فایل پیکربندی می‌خواند و ارسال می‌کند را بیان کرده است:
  • Global configuration information 
  • Protocol configuration information for both HTTP and non-HTTP protocols
  • Application pool configuration, such as the process account information 
  • Site configuration, such as bindings and applications 
  • Application configuration, such as the enabled protocols and the application pools to which the applications belong 
نکته پایانی اینکه اگر فایل ApplicationHost.config  تغییری کند، WAS یک اعلان دریافت کرده و اطلاعات آداپتورها را به روز می‌کند.

مدیریت پروسه‌ها Process Managment
گفتیم که مدیریت پول و پروسه‌های کارگر جزء وظایف این سرویس به شمار می‌رود. موقعی که یک protocol listener درخواستی را دریافت می‌کند، WAS چک می‌کند که آیا یک پروسه کارگر در حال اجراست یا خیر. اگر application pool پروسه‌ای داشته باشد که در حال سرویس دهی به درخواست هاست، آداپتور درخواست را به پروسه کارگر ارسال می‌کند. در صورتی که پروسه‌ای در application pool در حال اجرا نباشد، WAS یک پروسه جدید را آغاز می‌کند و آداپتور درخواست را به آن پاس می‌کند.
نکته: از آنجایی که WAS هم پروسه‌های http و هم non-http را مدیریت می‌کند، پس میتوانید از یک applicatio pool برای چندین protocol استفاده کنید. به عنوان مثال شما یکی سرویس XML دارید که می‌توانید از آن برای سرویس دهی به پروتوکل‌های Http و net.tcp بهره بگیرید.

ماژول‌ها در IIS
قبلا مقاله ای در مورد module‌ها با نام "کمی در مورد httpmoduleها" قرار داده بودیم که بهتر است برای آشنایی بیشتر، به آن رجوع کنید. به غیر از وب کانفیگ که برای معرفی ماژول‌ها استفاده می‌کردیم ، میتوانید به صورت گرافیکی و دستی هم این کار را انجام بدهید. ابتدا یک پروژه class library ایجاد کرده و ماژول خود را بنویسید و سپس آن را به یک dll تبدیل کنید و dll را در شاخه bin که این شاخه در ریشه وب سایتتان قرار دارد کپی کنید. سپس در IIS قسمت module گزینه Add را انتخاب کنید و در قسمت اول نامی برای آن و در قسمت بعدی دقیقا همان قوانین type که در وب کانفیگ مشخص می‌کردید را مشخص کنید: Namespace.ClassName
گزینه invoke only for requests to asp.net and manage handlers را هم تیک بزنید. کار تمام است.

ماژول‌های کد ماشین یا  native
این ماژول‌ها به صورت پیش فرض به سیستم اضافه شده‌اند و در صورتی که میخواهید جایگزینی به منظور خصوصی سازی انجام دهید آن‌ها را پاک کنید و ماژول جدید را اضافه کنید.

جدول ماژول‌های HTTP
نام ماژول
توضیحات
نام فایل منبع
CustomErrorModule  موقعی که هنگام response، کد خطایی تولید می‌گردد، پیام خطا را پیکربندی و سپس ارسال می‌کند.  Inetsrv\Custerr.dll 
 HttpRedirectionModule   تنظمیات redirection برای درخواست‌های http را در دسترس قرار می‌دهد.  Inetsrv\Redirect.dll 
 ProtocolSupportModule   انجام عملیات مربوط به پروتوکل‌ها بر عهده این ماژول است؛ مثل تنظیم کردن قسمت هدر برای response.  Inetsrv\Protsup.dll 
 RequestFilteringModule   این ماژول از IIS 7.5 به بعد اضافه شد. درخواست‌ها را فیلتر می‌کند تا پروتوکل و رفتار محتوا را کنترل کند.  Inetsrv\modrqflt.dll 
 WebDAVModule   این ماژول از IIS 7.5 به بعد اضافه شد. امنیت بیشتر در هنگام انتشار محتوا روی HTTP SSL  Inetsrv\WebDAV.dll 

ماژول‌های امنیتی
نام ماژول  توضیحات  نام فایل منبع 
 AnonymousAuthenticationModule  موقعی که هیچ کدام از عملیات authentication  با موفقیت روبرو نشود، عملیات  Anonymous authentication انجام می‌شود.  Inetsrv\Authanon.dll 
 BasicAuthenticationModule   عمل ساده و اساسی authentication  را انجام می‌دهد.  Inetsrv\Authbas.dll 
 CertificateMappingAuthenticationModule   انجام عمل Certificate Mapping authentication  در Active Directory  Inetsrv\Authcert.dll
 
 DigestAuthenticationModule   Digest authentication   Inetsrv\Authmd5.dll 
 IISCertificateMappingAuthenticationModule  همان Certificate Mapping authentication  ولی اینبار با IIS Certificate .  Inetsrv\Authmap.dll 
 RequestFilteringModule   عملیات اسکن URL از قبیل نام صفحات و دایرکتوری‌ها ، توع verb و یا کاراکترهای مشکوک و خطرآفرین  Inetsrv\Modrqflt.dll 
 UrlAuthorizationModule   عمل URL authorization   Inetsrv\Urlauthz.dll 
 WindowsAuthenticationModule   عمل NTLM integrated authentication   Inetsrv\Authsspi.dll 
 IpRestrictionModule   محدود کردن IP‌های نسخه 4 لیست شده در IP Security در قسمت پیکربندی  Inetsrv\iprestr.dll 
 

ماژول‌های محتوا
 نام ماژول  توضیحات نام فایل منبع
 CgiModule   ایجاد پردازش‌های (Common Gateway Interface (CGI به منظور ایجاد خروجی response  Inetsrv\Cgi.dll 
 DefaultDocumentModule   تلاش برای ساخت یک سند پیش فرض برای درخواست هایی که دایرکتوری والد ارسال می‌شود  Inetsrv\Defdoc.dll 
 DirectoryListingModule   لیست کردن محتوای یک دایرکتوری  Inetsrv\dirlist.dll 
 IsapiModule   میزبانی فایل های ISAPI Inetsrv\Isapi.dll
 IsapiFilterModule   پشتیبانی از فیلتر های ISAPI  Inetsrv\Filter.dll 
 ServerSideIncludeModule   پردازش کدهای include شده سمت سرور  Inetsrv\Iis_ssi.dll 
 StaticFileModule   ارائه فایل‌های ایستا  Inetsrv\Static.dll 
 FastCgiModule   پشتبانی از CGI  Inetsrv\iisfcgi.dll 

ماژول‌های فشرده سازی
 DynamicCompressionModule  فشرده سازی پاسخ response با gzip  Inetsrv\Compdyn.dll   
 StaticCompressionModule   فشرده سازی محتوای ایستا  Inetsrv\Compstat.dll 

ماژول‌های کش کردن
 FileCacheModule  تهیه کش در مد کاربری برای فایل‌ها.    Inetsrv\Cachfile.dll 
 HTTPCacheModule   تهیه کش مد کاربری و مد کرنل برای http.sys  Inetsrv\Cachhttp.dll 
 TokenCacheModule   تهیه کش مد کاربری بر اساس جفت نام کاربری و یک token که توسط  Windows user principals تولید شده است.   Inetsrv\Cachtokn.dll 
 UriCacheModule   تهیه یک کش مد کاربری از اطلاعات URL  Inetsrv\Cachuri.dll 

ماژول‌های عیب یابی و لاگ کردن
 CustomLoggingModule  بارگزاری ماژول‌های خصوصی سازی شده جهت لاگ کردن  Inetsrv\Logcust.dll
 FailedRequestsTracingModule   برای ردیابی درخواست‌های ناموفق  Inetsrv\Iisfreb.dll 
 HttpLoggingModule   دریافت اطلاعات  و پردازش وضعیت http.sys برای لاگ کردن  Inetsrv\Loghttp.dll 
 RequestMonitorModule   ردیابی درخواست هایی که در حال حاضر در پروسه‌های کارگر در حال اجرا هستند و گزارش اطلاعاتی در مورد وضعیت اجرا و کنترل رابط برنامه نویسی کاربردی.  Inetsrv\Iisreqs.dll 
 TracingModule   گزارش رخدادهای Microsoft Event Tracing for Windows یا به اختصار ETW  Inetsrv\Iisetw.dll 

ماژول‌های مدیریتی و نظارتی بر کل ماژول‌ها
 ManagedEngine   مدیرتی بر ماژول‌های غیر native که در پایین قرار دارند. Microsoft.NET\Framework\v2.0.50727\webengine.dll
 ConfigurationValidationModule  اعتبارسنجی خطاها، مثل موقعی که برنامه در حالت integrated اجرا شده و ماژول‌ها یا هندلرها در system.web تعریف شده‌اند. Inetsrv\validcfg.dll 
از IIS6 به بعد در حالت integrated و ماقبل، در حالت کلاسیک می‌باشند. اگر مقاله ماژول ها را خوانده باشید می‌دانید که تعریف آن‌ها در وب کانفیگ در بین این دو نسخه متفاوت هست و رویداد سطر آخر در جدول بالا این موقعیت را چک می‌کند و اگر به خاطر داشته باشید با اضافه کردن یک خط اعتبارسنجی آن را قطع می‌کردیم. در مورد هندلرها هم به همین صورت می‌باشد.
به علاوه ماژول‌های native بالا، IIS این امکان را فراهم می‌آورند تا از ماژول‌های کد مدیریت شده (یعنی CLR) برای توسعه توابع و کارکرد IIS بهره مند شوید:
 ماژول توضیحات   منبع
 AnonymousIdentification   مدیریت منابع تعیین هویت برای کاربران ناشناس مانند asp.net profile System.Web.Security.AnonymousIdentificationModule  
 DefaultAuthentication   اطمینان از وجود شی Authentication در context مربوطه  System.Web.Security.DefaultAuthenticationModule 
 FileAuthorization   تایید هویت کاربر برای دسترسی به فایل درخواست  System.Web.Security.FileAuthorizationModule 
 FormsAuthentication   با این قسمت که باید کاملا آشنا باشید؛ برای تایید هویت کاربر  System.Web.Security.FormsAuthenticationModule 
OutputCache  مدیریت کش  System.Web.Caching.OutputCacheModule 
 Profile   مدیریت پروفایل کاربران که تنظیماتش را در یک منبع داده‌ای چون دیتابیس ذخیره و بازیابی می‌کند.  System.Web.Profile.ProfileModule 
 RoleManager   مدیریت نقش و سمت کاربران  System.Web.Security.RoleManagerModule 
 Session   مدیریت session ها  System.Web.SessionState.SessionStateModule 
 UrlAuthorization   آیا کاربر جاری حق دسترسی به URL درخواست را دارد؟  System.Web.Security.UrlAuthorizationModule 
 UrlMappingsModule   تبدیل یک Url واقعی به یک Url کاربرپسند  System.Web.UrlMappingsModule 
 WindowsAuthentication  شناسایی و تایید و هویت یک کاربر بر اساس لاگین او به ویندوز   System.Web.Security.WindowsAuthenticationModule 
مطالب
انتقال SVN به یک سیستم جدید

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

الف) دریافت و نصب Visual SVN server
یا می‌توان SVN خالص را از سایت آن دریافت کرد و یا جهت سهولت کار و همچنین دسترسی به یک کنسول مدیریتی می‌توان برنامه‌ی رایگان Visual SVN server را از آدرس زیر دریافت و نصب کرد:

پس از نصب، ابتدا باید یا کاربر جدیدی را جهت استفاده از منابع آن تعریف کرد و یا از نحوه‌ی اعتبار سنجی یکپارچه با ویندوز هم می‌توان استفاده کرد که من از این روش دوم استفاده می‌کنم (شکل زیر، کلیک راست بر روی نود اصلی visual SVN server و سپس انتخاب خواص و مراجعه به برگه‌ی اعتبار سنجی آن):



ب)دریافت و نصب TortoiseSVN

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

ج) دریافت و نصب برنامه‌ی WinMerge
برنامه‌ی Diff پیش فرض TortoiseSVN آنچنان قوی نیست. به همین جهت می‌توان برنامه‌ی WinMerge را با آن یکپارچه کرد. برای این منظور ابتدا آن‌را دریافت نمائید:

اگر پس از نصب TortoiseSVN آن‌را نصب کنید، در حین نصب پیشنهاد یکپارچه سازی با TortoiseSVN را نیز می‌دهد. اگر ابتدا WinMerge را نصب کرده‌اید و سپس TortoiseSVN بر روی سیستم شما نصب شده، فقط کافی است مطابق شکل زیر ابتدا به قسمت Diff viewers آن مراجعه کرده و سپس با انتخاب گزینه‌ی external ، دستور خط فرمان زیر را وارد نمائید:

C:\Program Files (x86)\WinMerge\WinMergeU.exe -e -x -ub -dl %bname -dr %yname %base %mine



بدیهی است مسیر WinMergeU.exe مطابق مسیر نصب در سیستم شما باید تنظیم شود.

د) تنظیم مسیر تحت نظر قرار گرفتن سیستم
TortoiseSVN به صورت پیش فرض کل سیستم را جهت مشاهده‌ی تغییرات تحت نظر قرار می‌دهد که گاهی باعث کاهش کارآیی آن خواهد شد. برای رفع این مشکل می‌توان مسیرهایی را که پروژه‌های شما در آن قرار دارند را به آن معرفی نمود تا بار کلی سیستم کاهش یابد.



همانطور که در شکل نیز ملاحظه می‌کنید، Include path مقدار دهی شده است.

ه) مشخص سازی پسوندهایی که بهتر است از آن‌ها صرفنظر شود
به برگه‌ی general تنظیمات TortoiseSVN مراجعه کرده و در قسمت global ignore pattern آن، موارد زیر را وارد نمائید:



این موارد شامل پروژه‌های دات نت، دلفی ، VC و امثال آن است و همچنین یک سری فایل بایناری که عموما با پروژه‌های برنامه نویسی نیازی به ثبت نگارش آن‌ها نیست.

*.dcu *.~* dcu temp *.exe *.zip *.bkm *.ddp *.cfg *.dof *.dsk *.ini *.hlp *.gid *.bmp *.png *.gif ~* *.log bin debug release *.map *.chm *.bkf Thumbs.db *.mdb .obj *.elf *.stat *.ddp *.bpl *.map *.GID *.hlp *.opt *.dll *.raw *.BIN *.obj *.pdb *.scc Debug Release *.xml obj *.~* *.backup *.INI *.ArmLog *.KeyLog *.NanoLog *.Stats *.PreARM *.old *.drc *.*~ *.doc *.pdf *.bmp *.jpg *.MRW *.NEF *.ORF *.psd *.X3F __history *.local *.identcache *.bak Thumbs.db *.ldb *.dex *.rar DllDcu *.lck CVS cvs *.txt *.TXT *.jdbg *.HLP *.KWF *.xls *.cnt *.dsm *.dti *.tmp *.lnk *.cbk *.mes *.suo *.ncb *.user _ReSharper.* [Bb]in obj [Dd]ebug [Rr]elease *.aps *.eto


در همین برگه، اگر هنوز از VS2003 استفاده می‌کنید، تیک مربوط به استفاده از _svn بجای .svn را قرار دهید تا VS.Net با پوشه‌های مدیریتی ذکر شده مشکل پیدا نکند.

و) نصب افزونه‌های SVN سازگار با VS.Net
یا می‌توان از افزونه‌ی Visual SVN استفاده کرد (که رایگان نیست) و یا AnkhSVN که رایگان و سورس باز است.
ولی در کل یک مورد را بیشتر نصب نکنید. علت هم کند شدن VS.Net است به دلیل فعالیت‌های پشت صحنه‌ی هر کدام از این افزونه‌ها که زیاده روی در تعداد آن‌ها گاها باعث کرش هم می‌شود. بنابراین همان یک مورد کافی است.

ز) Import مخزن‌های قبلی
تا اینجا مقدمات کار فراهم شد. اکنون نوبت به import مخزن‌های بجا مانده از سیستم قبلی است. برای اینکار مطابق شکل زیر، گزینه‌ی import existing repositories را انتخاب کرده و مسیر مخزن‌های قبلی خود را باید معرفی نمود (به ازای هر کدام یکبار باید این عملیات صورت گیرد).



پس از انجام این مراحل یکبار باید سیستم reboot شود و اکنون همه چیز مثل قبل خواهد شد!


نکته:
اگر مسیر ریشه مخزن‌های جدید با مسیر آن‌ها در سیستم قبلی متفاوت است، هنگام commit کارهای خود با خطای زیر متوقف خواهید شد:
Commit failed (details follow): Unable to open an ra_local session to URL
Unable to open repository 'file:///C:/Repositories/tracking/trunk'




اشکالی ندارد! برای رفع آن باید از گزینه‌ی relocate مربوط به TortoiseSVN استفاده کرد.
بر روی پوشه کاری پروژه خود کلیک راست کرده، انتخاب گزینه‌ی TortoiseSVN و سپس انتخاب گزینه‌ی Relocate آن باید صورت گیرد. در اینجا می‌توان مسیر جدید ریشه اصلی مخزن را در سیستم جدید معرفی کرد.



مطالب
5 دلیل برای استفاده از یک ابزار ORM

چرا باید از ابزارهای Object relational Mapper یا به اختصار ORM استفاده کرد؟ در اینجا سخن در مورد ORM خاصی نیست. هدف تبلیغ یک محصول ویژه هم نمی‌باشد و یک بحث کلی مد نظر است.
کار ابزارهای ORM خواندن ساختار دیتابیس شما بوده و سپس ایجاد کلاس‌هایی بر اساس این ساختار ، برقراری ارتباط بین اشیاء ایجاد شده و جداول، ویووها، رویه‌های ذخیره شده و غیره می‌باشد. همچنین این ابزارها امکان تعریف روابط one-to-one, one-to-many, many-to-one, و many-to-many بین اشیاء را نیز بر اساس ساختار دیتابیس شما فراهم می‌کنند.
در ادامه به فواید استفاده از ORM ها خواهیم پرداخت:

الف) یک ابزار ORM زمان تحویل پروژه را کاهش می‌دهد

اولین و مهم‌ترین دلیلی که بر اساس آن در یک پروژه، استفاده از ORM حائز اهمیت می‌شود، بحث بالا بردن سرعت برنامه نویسی و کاهش زمان تحویل پروژه به مشتری است. این کاهش زمان بسته به نوع پروژه بین 20 تا 50 درصد می‌تواند خود را بروز دهد.
بدیهی است ابزارهای ORM کار شگفت انگیزی را قرار نیست انجام دهند و شما می‌توانید تمام آن عملیات ‌را دستی هم به پایان رسانید؛ اما اجازه دهید یک مثال کوتاه را با هم مرور کنیم.
برای پیاده سازی یک برنامه متداول با حدود 15 تا 20 جدول، حدودا به 30 شیء برای مدل سازی سیستم نیاز خواهد بود و برنامه نویسی این مجموعه بین 5000 تا 10000 سطر کد را به خود اختصاص خواهد داد. بدیهی است برنامه نویسی و آزمایش این سیستم چندین هفته یا ماه به طول خواهد انجامید.
اما با استفاده از یک ORM ، عمده وقت شما به طراحی سیستم و ایجاد ارتباطات بین اشیاء و دیتابیس در طی یک تا دو روز صرف خواهد شد. ایجاد کد بر اساس این مجموعه و با کمک ابزارهای ORM ، آنی است و با چند کلیک صورت می‌گیرد.


ب) یک ابزار ORM کدی با طراحی بهتر را تولید می‌کند

ممکن است شما بگوئید که کد نویسی من بی‌نظیر است و از من بهتر کسی را نمی‌توانید پیدا کنید! به تمامی زوایای کار خود مسلطم و نیازی هم به این‌گونه ابزارها ندارم!
عده‌ای از شما به طور قطع این‌گونه‌اید؛ اما نه همه. در یک تیم متوسط، همه نوع برنامه نویس با سطوح مختلفی را می‌توانید پیدا کنید و تمامی ‌‌آن‌ها برنامه نویس‌ها و یا طراح‌های آنچنان قابلی هم نیستند. بنابراین امکان رسیدن به کدهایی که مطابق اصول دقیق برنامه نویسی شیء گرا نیستند و در آن‌ها الگوهای طراحی به خوبی رعایت نشده، بسیار محتمل است. همچنین در یک تیم زمانیکه از یک الگوی یکسان پیروی نمی‌شود، نتایج نهایی بسیار ناهماهنگ خواهند بود.
در مقابل استفاده از ORM های طراحی شده توسط برنامه نویس‌های قابل (senior (architect level) engineers) ، کدهایی را بر اساس الگوهای استاندارد و پذیرفته شده‌ی شیء‌گرا تولید می‌کنند و همواره یک روند کاری مشخص و هماهنگ را در یک مجموعه به ارمغان خواهند آورد.

ج) نیازی نیست تا حتما یک متخصص دات نت فریم ورک باشید تا از یک ORM استفاده کنید

قسمت دسترسی به داده‌ها یکی از اجزای کلیدی کارآیی برنامه شما است. اگر طراحی و پیاده سازی آن ضعیف باشد، کل برنامه را زیر سؤال خواهد برد. برای طراحی و پیاده سازی دستی این قسمت از کار باید به قسمت‌های بسیاری از مجموعه‌ی دات نت فریم ورک مسلط بود. اما هنگام استفاده از یک ORM مهمترین موردی را که باید به آن تمرکز نمائید بحث طراحی منطقی کار است و ایجاد روابط بین اشیاء و دیتابیس و امثال آن. مابقی موارد توسط ORM انجام خواهد شد و همچنین می‌توان مطمئن بود که پیاده سازی خودکار انجام شده این قسمت‌ها، بر اساس الگوهای طراحی شیء‌گرا است.


د) هنگام استفاده از یک ابزار ORM ، مدت زمان آزمایش برنامه نیز کاهش می‌یابد

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

ه) استفاده از یک ابزار ORM ، کار برنامه نویسی شما را ساده‌تر می‌کند

توضیح این قسمت نیاز به ذکر یک مثال دارد. لطفا به مثال زیر دقت بفرمائید:

try {
Employees objInfo = new Employees();
EmployeesFactory objFactory = new EmployeesFactory();

objInfo.EmployeeID = EmployeeID;
objFactory.Load(objInfo);

// code here to use the "objInfo" object
}
catch(Exception ex) {
// code here to handle the exception
}

به نظر شما کار کردن با یک یا چند شیء تولید شده که نمایانگر ساختار دیتابیس شما هستند و با استفاده از اینترفیس عمومی آن‌ها می‌توان تمامی اعمال بارگذاری، درج و حذف و غیره را انجام داد، ساده‌تر است یا کار کردن با کوهی از دستورات ADO.Net ؟


برداشتی آزاد از Five Reasons for using an ORM Tool

مطالب
ذخیره‌ی سوابق کامل تغییرات یک رکورد در یک فیلد توسط Entity framework Core
در این مقاله، نوشته‌ی ایمان محمدی، ذخیره‌ی اطلاعات نظارتی هر Entity توسط دو فیلد CreatedSources و ModifiedSources به صورت JSON انجام می‌شود که در هر کدام از این فیلدها، اطلاعات مختلفی مانند ip کاربر، شناسه دستگاه، HostName، ClientName و یک سری اطلاعات دیگر ذخیره می‌شوند. بیایید به این اطلاعات متادیتا بگوییم. در این حالت اگر رکورد، چندین بار تغییر کند، متادیتای آخرین تغییرات در فیلد ModifiedSources ذخیره می‌شود. حالا اگر ما بخواهیم اطلاعات متادیتای همه‌ی تغییرات را داشته باشیم چه؟ اگر بخواهیم علاوه بر اطلاعات بالا، اینکه چه کسی و در چه زمانی این تغییرات را انجام داده است، نیز داشته باشیم چطور؟ اگر بخواهیم حتی اطلاعات متادیتای حذف یک رکورد را داشته باشیم چطور (در حالت soft-delete که رکورد واقعا پاک نمی‌شود)؟ سوال جالبتر اینکه اگر بخواهیم تمام تاریخچه‌ی مقادیر مختلف یک رکورد را از ابتدای ایجاد شدن داشته باشیم چطور؟ در این مقاله قصد داریم به همه‌ی این موارد اضافی برسیم؛ آن هم فقط با یک ستون در Entityهایمان، به اسم Audit!

ابتدا کلاس پایه موجودیت‌هایمان را تعریف می‌کنیم؛ تا بر روی Entityهایمان بتوانیم فیلد نظارتی Audit را اعمال کنیم:
public class BaseEntity : IBaseEntity
{
   [JsonIgnore]
   int Id { get; set; } 

   [JsonIgnore] 
    string? Audit { get; set; }
}
ویژگی [JsonIgnore]  به این منظور استفاده شده است تا از serialize کردن این فیلدها هنگام ایجاد Audit، جلوگیری شود؛ تا در نهایت حجم جیسن Audit کاهش یابد. با مطالعه‌ی ادامه‌ی مقاله، متوجه این قضیه خواهید شد.

دقیقا مانند مقاله‌ی اشاره شده (که خواندن آن توصیه می‌شود)، کلاس AuditSourceValues را ایجاد می‌کنیم:
public class AuditSourceValues
{
    [JsonProperty("hn")]
    public string? HostName { get; set; }

    [JsonProperty("mn")]
    public string? MachineName { get; set; }

    [JsonProperty("rip")]
    public string? RemoteIpAddress { get; set; }

    [JsonProperty("lip")]
    public string? LocalIpAddress { get; set; }

    [JsonProperty("ua")]
    public string? UserAgent { get; set; }

    [JsonProperty("an")]
    public string? ApplicationName { get; set; }

    [JsonProperty("av")]
    public string? ApplicationVersion { get; set; }

    [JsonProperty("cn")]
    public string? ClientName { get; set; }

    [JsonProperty("cv")]
    public string? ClientVersion { get; set; }

    [JsonProperty("o")]
    public string? Other { get; set; }
}
با تعریف کردن نام برای فیلد‌های JSON و نادیده گرفتن مقادیر نال، سعی کردیم حجم خروجی JSON پایین باشد.

اکنون کلاس EntityAudit را ایجاد می‌کنیم که شامل تمامی اطلاعات مورد نیاز ما برای ثبت تاریخچه‌ی کامل هر موجودیت است:
public class EntityAudit<TEntity>
{
    [JsonProperty("type")]
    [JsonConverter(typeof(StringEnumConverter))]
    public EntityEventType EventType { get; set; }

    [JsonProperty("user", NullValueHandling = NullValueHandling.Include)]
    public int? ActorUserId { get; set; }

    [JsonProperty("at")]
    public DateTime ActDateTime { get; set; }

    [JsonProperty("sources")]
    public AuditSourceValues? AuditSourceValues { get; set; }

    [JsonProperty("newValues", NullValueHandling = NullValueHandling.Include)]
    public TEntity NewEntity { get; set; } = default!;

    public string? SerializeJson()
    {
        return JsonSerializer.Serialize(this, 
            options: new JsonSerializerOptions { WriteIndented = false, IgnoreNullValues = true }); 
    }
}

دقت کنید که این کلاس به صورت جنریک تعریف شده است تا اگر بعدا بخواهیم آن را Deserialize کنیم و مثلا از آن API بسازیم، یا استفاده‌ی خاصی را از آن داشته باشیم، به‌راحتی به Entity مد نظر تبدیل شود. در این مقاله فقط به ذخیره‌ی آن پرداخته می‌شود و استفاده از این فیلد که به راحتی و با کمک DbFunctionها در Entity Framework قابل انجام است به خواننده واگذار می‌شود. 

همچنین اینام EntityEventType که تعریف آن در زیر می‌آید دارای ویژگی [JsonConverter(typeof(StringEnumConverter))]  می‌باشد تا مقدار رشته‌ای آن را بجای مقدار عددی، در خروجی جیسن داشته باشیم. این اینام، شامل  تمامی عملیاتی است که بر روی یک رکورد قابل انجام است و به این صورت تعریف می‌شود:
public enum EntityEventType
{
    Create = 0,
    Update = 1,
    Delete = 2
}

تامین اطلاعات کلاس AuditSourceValues به همان صورت است که در مقاله‌ی اشاره شده آمده‌است؛ ابتدا تعریف اینترفیس IAuditSourcesProvider و سپس ایجاد کلاس AuditSourcesProvider:
public interface IAuditSourcesProvider
{
    AuditSourceValues GetAuditSourceValues();
}
public class AuditSourcesProvider : IAuditSourcesProvider
{
    protected readonly IHttpContextAccessor HttpContextAccessor;

    public AuditSourcesProvider(IHttpContextAccessor httpContextAccessor)
    {
        HttpContextAccessor = httpContextAccessor;
    }

    public virtual AuditSourceValues GetAuditSourceValues()
    {
        var httpContext = HttpContextAccessor.HttpContext;

        return new AuditSourceValues
        {
            HostName = GetHostName(httpContext),
            MachineName = GetComputerName(httpContext),
            LocalIpAddress = GetLocalIpAddress(httpContext),
            RemoteIpAddress = GetRemoteIpAddress(httpContext),
            UserAgent = GetUserAgent(httpContext),
            ApplicationName = GetApplicationName(httpContext),
            ClientName = GetClientName(httpContext),
            ClientVersion = GetClientVersion(httpContext),
            ApplicationVersion = GetApplicationVersion(httpContext),
            Other = GetOther(httpContext)
        };
    }

    protected virtual string? GetUserAgent(HttpContext httpContext)
    {
        return httpContext.Request?.Headers["User-Agent"].ToString();
    }

    protected virtual string? GetRemoteIpAddress(HttpContext httpContext)
    {
        return httpContext.Connection?.RemoteIpAddress?.ToString();
    }

    protected virtual string? GetLocalIpAddress(HttpContext httpContext)
    {
        return httpContext.Connection?.LocalIpAddress?.ToString();
    }

    protected virtual string GetHostName(HttpContext httpContext)
    {
        return httpContext.Request.Host.ToString();
    }

    protected virtual string GetComputerName(HttpContext httpContext)
    {
        return Environment.MachineName;
    }
    protected virtual string? GetApplicationName(HttpContext httpContext)
    {
        return Assembly.GetEntryAssembly()?.GetName().Name;
    }

    protected virtual string? GetApplicationVersion(HttpContext httpContext)
    {
        return Assembly.GetEntryAssembly()?.GetName().Version.ToString();
    }

    protected virtual string? GetClientVersion(HttpContext httpContext)
    {
        return httpContext.Request?.Headers["client-version"];
    }
    protected virtual string? GetClientName(HttpContext httpContext)
    {
        return httpContext.Request?.Headers["client-name"];
    }

    protected virtual string? GetOther(HttpContext httpContext)
    {
        return null;
    }
}

حالا برای تامین اطلاعات کلاس EntityAudit کار مشابهی می‌کنیم. ابتدا اینترفیس IEntityAuditProvider را به صورت زیر تعریف می‌کنیم: 
public interface IEntityAuditProvider
{
    string? GetAuditValues(EntityEventType eventType, object? entity, string? previousJsonAudit = null);
}

  و سپس کلاس EntityAuditProvider را ایجاد می‌کنیم:
public class EntityAuditProvider : IEntityAuditProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IAuditSourcesProvider _auditSourcesProvider;

    #region Constructor Injections

    public EntityAuditProvider(IHttpContextAccessor httpContextAccessor, IAuditSourcesProvider auditSourcesProvider)
    {
        _httpContextAccessor = httpContextAccessor;
        _auditSourcesProvider = auditSourcesProvider;
    }

    #endregion

    public virtual string? GetAuditValues(EntityEventType eventType, object? newEntity, string? previousJsonAudit = null)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        int? userId;

        var user = httpContext.User;

        if (!user.Identity.IsAuthenticated)
            userId = null;
        else
            userId = user.Claims.Where(x => x.Type == "UserID").Select(x => x.Value).First().ToInt();

        var auditSourceValues = _auditSourcesProvider.GetAuditSourceValues();

        var auditJArray = new JArray();

        // Update & Delete
        if (eventType == EntityEventType.Update || eventType == EntityEventType.Delete)
        {
            auditJArray = JArray.Parse(previousJsonAudit!);
        }

        // Delete => No NewValues
        if (eventType == EntityEventType.Delete)
        {
            newEntity = null;
        }

        JObject newAuditJObject = JObject.FromObject(new EntityAudit<object?>
        {
            EventType = eventType,
            ActorUserId = userId,
            ActDateTime = DateTime.Now,
            AuditSourceValues = auditSourceValues,
            NewEntity = newEntity
        }, new JsonSerializer
        {
            NullValueHandling = NullValueHandling.Ignore,
            Formatting = Formatting.None
        });

        auditJArray.Add(newAuditJObject);

        return auditJArray.SerializeToJson(true);
    }
}
در این کلاس برای اینکه به جیسن قبلی Audit که تاریخچه‌ی قبلی رکورد می‌باشد یک آیتم را اضافه کنیم، از JArray و JObject پکیج Newtonsoft استفاده کرد‌ه‌ایم.

حالا همه چیز آماده است. مانند مقاله‌ی اشاره شده، از مفهوم Interceptor استفاده می‌کنیم. کلاس AuditSaveChangesInterceptor را که از کلاس SaveChangesInterceptor مشتق می‌شود، به صورت زیر ایجاد می‌کنیم: 
public class AuditSaveChangesInterceptor : SaveChangesInterceptor
{
    private readonly IEntityAuditProvider _entityAuditProvider;

    #region Constructor Injections

    public AuditSaveChangesInterceptor(IEntityAuditProvider entityAuditProvider)
    {
        _entityAuditProvider = entityAuditProvider;
    }

    #endregion

    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        ApplyAudits(eventData.Context.ChangeTracker);
        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result,
        CancellationToken cancellationToken = new CancellationToken())
    {
        ApplyAudits(eventData.Context.ChangeTracker);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    private void ApplyAudits(ChangeTracker changeTracker)
    {
        ApplyCreateAudits(changeTracker);
        ApplyUpdateAudits(changeTracker);
        ApplyDeleteAudits(changeTracker);
    }

    private void ApplyCreateAudits(ChangeTracker changeTracker)
    {
        var addedEntries = changeTracker.Entries()
            .Where(x => x.State == EntityState.Added);

        foreach (var addedEntry in addedEntries)
        {
            if (addedEntry.Entity is IBaseEntity entity)
            {              
                entity.Audit = _entityAuditProvider.GetAuditValues(EntityEventType.Create, entity);
            }
        }
    }

    private void ApplyUpdateAudits(ChangeTracker changeTracker)
    {
        var modifiedEntries = changeTracker.Entries()
            .Where(x => x.State == EntityState.Modified);

        foreach (var modifiedEntry in modifiedEntries)
        {
            if (modifiedEntry.Entity is IBaseEntity entity)
            {
                var eventType = entity.IsArchived ? EntityEventType.Delete : EntityEventType.Update; // Maybe Soft Delete
                entity.Audit = _entityAuditProvider.GetAuditValues(eventType, entity, entity.Audit);
            }
        }
    }

    private void ApplyDeleteAudits(ChangeTracker changeTracker)
    {
        var deletedEntries = changeTracker.Entries()
            .Where(x => x.State == EntityState.Deleted);

        foreach (var modifiedEntry in deletedEntries)
        {
            if (modifiedEntry.Entity is IBaseEntity entity)
            {
                entity.Audit = _entityAuditProvider.GetAuditValues(EntityEventType.Delete, entity, entity.Audit);
            }
        }
    }

}


و سپس آن را به سیستم معرفی می‌کنیم:

services.AddDbContext<ATADbContext>((serviceProvider, options) =>
{
    options
        .UseSqlServer(...)

    // Interceptors
    var entityAuditProvider = serviceProvider.GetRequiredService<IEntityAuditProvider>();
    options.AddInterceptors(new AuditSaveChangesInterceptor(entityAuditProvider));

});

یادمان باشد همه‌ی سرویس‌ها را باید در برنامه رجیستر کنیم تا بتوانیم از تزریق وابستگی‌ها مانند کدهای بالا استفاده نماییم. 

نمونه‌ی نتیجه‌ای را که از این روش بدست می‌آید، در این تصویر می‌بینید. اگر بخواهید به صورت نرم‌افزاری یا کدی از این دیتا استفاده کنید، باید آن را Deserialize کنید که همانطور که گفته شد با امکاناتی که SQL Server برای خواندن فیلدهای JSON دارد و معرفی آن به EF، قابل انجام است. در غیر اینصورت استفاده از این دیتا به صورت چشمی یا استفاده از Json Formatterها به‌راحتی امکان پذیر است. 

 
نمونه‌ی کامل فیلد Audit که در JsonFormatter قرار داده شده است، بعد از ایجاد شدن و یکبار آپدیت و سپس حذف نرم رکورد:
[
   {
      "type":"Create",
      "user":1,
      "at":"2020-11-24T23:05:54.2692711+03:30",
      "sources":{
         "hn":"localhost:44398",
         "mn":"DESKTOP-N1GAV2U",
         "rip":"::1",
         "lip":"::1",
         "ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
         "an":"Server.Api",
         "av":"1.0.0.0"
      },
      "newValues":{               
         "Name":"Farshad"
      }
   },
   {
      "type":"Update",
      "user":1,
      "at":"2020-11-24T23:06:20.0838188+03:30",
      "sources":{
         "hn":"localhost:44398",
         "mn":"DESKTOP-N1GAV2U",
         "rip":"::1",
         "lip":"::1",
         "ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
         "an":"Server.Api",
         "av":"1.0.0.0"
      },
      "newValues":{                 
         "Name":"Edited Farshad"
      }
   },
   {
      "type":"Delete",
      "user":null,
      "at":"2020-11-24T23:06:28.601837+03:30",
      "sources":{
         "hn":"localhost:44398",
         "mn":"DESKTOP-N1GAV2U",
         "rip":"::1",
         "lip":"::1",
         "ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
         "an":"Server.Api",
         "av":"1.0.0.0"
      },
      "newValues":null
   }
]

یک روش مرسوم داشتن تاریخچه‌ی تغییرات رکورد که با جستجو در اینترنت نیز می‌توان به آن رسید، داشتن یک جدول جداگانه به اسم Audit است که با هر بار تغییر هر Entity، یک رکورد در آن ایجاد می‌شود. ساختار آن مانند تصاویر زیر است:


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

مطالب
استفاده از pjax بجای ajax در ASP.NET MVC
عموما از ajax برای ارائه سایت‌هایی سریع، با حداقل ریفرش و حداقل مصرف پهنای باند سرور، استفاده می‌شود. اما این روش، مشکلات خاص خود را نیز دارا است. عموما محتوای پویای بارگذاری شده، سبب تغییر آدرس صفحه‌ی جاری در مرورگر نمی‌شود. برای مثال اگر قرار است چندین برگه در صفحه به صورت ajax ایی بارگذاری شوند، تغییر سریع محتوا را مشاهده می‌کنید، اما خبری از تغییر آدرس جاری صفحه در مرورگر نیست. همچنین روش‌های ajax ایی عموما SEO friendly نیستند. زیرا اکثر موتورهای جستجو فاقد پردازشگرهای جاوا اسکریپت می‌باشند و محتوای پویای ajax ایی را مشاهده نمی‌کنند. برای آدرس دهی این مشکلات مهم، افزونه‌ای به نام pjax طراحی شده‌است که کار آن دریافت محتوای HTML ایی از سرور و قرار دادن آن در یک جایگاه خاص مانند یک div است. در پشت صحنه‌ی آن از jQuery ajax استفاده شده، به همراه push state

pjax = pushState + AJAX
Push state API همان HTML5 History API است؛ به این معنا که هرچند محتوای صفحه‌ی جاری به صورت پویا بارگذاری می‌شود، اما آدرس مرورگر نیز به صورت خودکار تنظیم خواهد شد؛ به همراه عنوان صفحه. به علاوه تاریخچه‌ی مرور صفحات نیز در مرورگر به روز رسانی شده و امکان حرکت بین صفحات توسط دکمه‌های back و forward همانند قبل وجود خواهد داشت. همچنین اگر مرورگر جاری سایت، امکان استفاده از جاوا اسکریپت را نداشته باشد، به صورت خودکار به حالت بارگذاری کامل صفحه سوئیچ خواهد کرد.
سایت‌های بسیاری خودشان را با این الگو وفق داده‌اند. برای نمونه Twitter و Github از مفهوم pjax استفاده‌ی وسیعی دارند. برای نمونه، layout یا master page یک سایت را درنظر بگیرید. به ازای مرور هر صفحه، یکبار باید تمام قسمت‌های تکراری layout از سرور بارگذاری شوند. توسط pjax به سرور اعلام می‌کنیم، ما تنها نیاز به body صفحات را داریم و نه کل صفحه را. همچنین اگر مرورگر از جاوا اسکریپت استفاده نمی‌کند، لطفا کل صفحه را همانند گذشته بازگشت بده. به علاوه مسایل سمت کلاینت مانند تغییر آدرس مرورگر و تغییر عنوان صفحه نیز به صورت خودکار مدیریت شوند. این تکنیک را دقیقا در حین مرور مخزن‌های کد Github می‌توانید مشاهده کنید. فقط قسمتی که لیست فایل‌ها را ارائه می‌دهد، از سرور دریافت می‌گردد و نه کل صفحه.


بکارگیری pjax در ASP.NET MVC

مطابق توضیحاتی که ارائه شد، برای پیاده سازی سازی pjax نیاز به دو فایل layout داریم. یکی برای حالت ajax ایی و دیگری برای حالت بارگذاری کامل صفحه. حالت ajax ایی آن تنها از رندرکردن body پشتیبانی می‌کند؛ و نه ارائه تمام قسمت‌های صفحه مانند هدر، فوتر، منوها و غیره. بنابراین خواهیم داشت:

الف) تعریف فایل‌های layout سازگار با pjax
ابتدا یک فایل جدید را به نام _PjaxLayout.cshtml به پوشه‌ی Shared اضافه کنید؛ با این محتوا:
 <title>@ViewBag.Title</title>
@RenderBody()
سپس layout اصلی سایت را به نحو ذیل تغییر دهید
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="~/Content/Site.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
    <script src="~/Scripts/jquery.pjax.js"></script>

    <script type="text/javascript">
        $(function () {
            $(document).pjax('a[withpjax]', '#pjaxContainer', { timeout: 5000 });
        });
    </script>
</head>
    <body>
        <div>Main layout ...</div>
        <div id="pjaxContainer">
            @RenderBody()
        </div>
    </body>
</html>
در فایل PjaxLayout خبری از هدر و فوتر نیست و فقط یک عنوان و نمایش body را به همراه دارد.
فایل layout اصلی سایت همانند قبل است. فقط RenderBody آن داخل یک div با id مساوی pjaxContainer قرار گرفته و از آن در فراخوانی افزونه‌ی pjax استفاده شده‌است. همانطور که ملاحظه می‌کنید، مطابق تنظیمات ابتدای هدر layout، فقط لینک‌هایی که دارای ویژگی withpjax باشند، توسط pjax پردازش خواهند شد.

ب) تغییر فایل ViewStart برنامه
در فایل ViewStart، کار مقدار دهی layout پیش فرض صورت گرفته‌است. اکنون نیاز است این فایل را جهت معرفی layout دوم تعریف شده مخصوص pjax، اندکی ویرایش کنیم:
@{
    if (Request.Headers["X-PJAX"] != null)
    {
        Layout = "~/Views/Shared/_PjaxLayout.cshtml";
    }
    else
    {
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
}
افزونه‌ی pjax، هدری را به نام X-PJAX به سرور ارسال می‌کند. بر این اساس می‌توان تصمیم گرفت که آیا از layout اصلی (در صورتیکه مرورگر از جاوا اسکریپت پشتیبانی نمی‌کند و این هدر را ارسال نکرده‌است) یا از layout سبک‌تر pjax استفاده شود.

ج) آزمایش برنامه
using System.Web.Mvc;

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

        public ActionResult About()
        {
            return View();
        }
    }
}
یک کنترلر ساده را به نحو فوق با دو اکشن متد و دو View متناظر با آن ایجاد کنید.
سپس View متد Index را به نحو ذیل تغییر دهید:
 @{
ViewBag.Title = "Index";
}

<h2>Index</h2>

@Html.ActionLink(linkText: "About", actionName:"About", routeValues: null,
                         controllerName:"Home", htmlAttributes: new { withpjax = "with-pjax"})
در این View یک لینک معمولی به اکشن متد About اضافه شده‌است. فقط در ویژگی‌های html آن، یک ویژگی جدید به نام withpjax را نیز اضافه کرده‌ایم تا در صورت امکان و پشتیبانی مرورگر، از pjax استفاده شود.
اکنون اگر برنامه را اجرا کنید، چنین خروجی را در برگه‌ی network آن مشاهده خواهید کرد:



همانطور که ملاحظه می‌کنید، با کلیک بر روی لینک About، یک درخواست pjax ایی به سرور ارسال شده‌است؛ به همراه هدرهای ویژه آن. هنوز قسمت‌های اصلی layout سایت مشخص هستند (و مجددا از سرور درخواست نشده‌اند). آدرس صفحه عوض شده‌است. به علاوه قسمت body آن تنها تغییر کرده‌است.



این مثال را از اینجا نیز می‌توانید دریافت کنید
PajxMvcApp.zip


برای مطالعه بیشتر

A Faster Web With PJAX
Favour PJAX over dynamically loaded partial views
What is PJAX and why
Pjax.Mvc
Using pjax with ASP.Net MVC3
Getting started with PJAX with ASP.NET MVC
ASP.NET MVC with PAjax or PushState/ReplaceState and Ajax
نظرات مطالب
آشنایی با Window Function ها در SQL Server بخش اول
سلام
Syntax ارائه شده Over Clause در SQL Server 2008 شامل دو پارامتر است :
Ranking Window Functions 
< OVER_CLAUSE > :: =
    OVER ( [ PARTITION BY value_expression , ... [ n ] ]
           <ORDER BY_Clause> )

Aggregate Window Functions 
< OVER_CLAUSE > :: = 
    OVER ( [ PARTITION BY value_expression , ... [ n ] ] )
در SQL Server 2012 پارامتر <ROW or RANGE clause> به Syntax فوق افزوده شد. بنابراین، Query هایی که از Row یا Range استفاده کرده اند، در SQL Server 2008 با خطا مواجه می‌شوند.