نظرات مطالب
ASP.NET MVC #16
با سلام و تشکر واقعی از این مطالب مفید. 
اگه از روش اول برای ثبت  استثناها توسط Elmah استفاده شود، یعنی حذف یک سطر از فایل Global.asax.cs، فقط  استثناها  ثبت  می شود ، یا صفحه خطایی هم نمایش داده می شود؟
نظرات مطالب
استفاده از قابلیت پارتیشن بندی در آرشیو جداول بانک‌های اطلاعاتی SQL Server
سلام جناب رجبی مطلب بسیار خوبی بود مخصوصا که من واقعا بهش نیاز داشتم اما سوالاتی برام پیش اومده من دیتابیس بزرگی دارم که سه جدولش از همه بزرگته و یکیش 190 میلیون و دو تا جدول 43 میلیون رکوردی دارم و بقیه جداول زیر 5 میلیون هست
چون در شروع کار تجربه خوبی نداشتم و از حجم اطلاعات مطمئن نبودم روی دیتابیسم pk نذاشتم حالا که می‌خوام pk  و ایندکس گذاریش کنم برای هر جدول یک filegroup و در هر فایل گروپ یک فایل برای pk  و یک فایل برای index‌ها گذاشتم و تقریبا هر کدوم از اون جدوال بزرگ را به 30 تا جدول تقسیم کردم تا سرعت پرس و جو‌های زیادم بسیار کمتر بشه که به نظرم خوب نمی‌اومد (کار پرس و جو و ایجاد کوئری را دشوارتر می‌کنه) تا با راه حل شما اشنا شدم اما سوالم اینجاست اگر داده هام رو بر اساس تاریخ در datafile‌ها پارتیشن بندی کنم زمانی که بخوام مثلا اطلاعات تا قبل از سال 85 را ارشیو کنم تاثیری در زمان پرس و جوی من دارد و کلید‌ها و ایندکس‌ها وضعیتشون به چه شکل خواهد بود اگر از راه حل خودم استفاده نکنم چون همینجوریش حجم دیتابیسم 35 گیگه و با گذاشتن کلید و ایندکس چیزی بین 15 تا 20 گیگ هم اضافه میشه که خیلی بد هست.
الان که یه خورده رو کد‌ها کار کردم یه سوال برام پیش اومد مثالی میزنم من جدولی دارم که تاریخ را با نام  شرکت میگیره و یک کد گزارش میده حالا من این کد گزارش را با مثلا 10 هزار رکورد در جدول دوم ذخیره می‌کنم و و باز با همون کد گزارش 5000 رکورد را در جدول سوم ذخیره می‌کنم و به همین ترتیب برای تمام روز‌ها و برای کل شرکت‌ها این کد گزارش تولید و با حجم انبوهی از رکورد‌ها در جداول ذخیره میشن
حالا سوال اینه که چطور بر اساس تاریخ که در جدول فقط اول هست جداول دیگر را پارتیشن بندی کنم ؟  
مطالب
بررسی دقیق‌تر صفحات آبی ویندوز

حدود یک سال قبل کامپیوتری را که داشتم (اینتل پنتیوم 4) به یک AMD دوهسته‌ای ارتقاء دادم و هفته‌ی اول پس از ارتقاء، روزگار من سیاه شد! روزهای اول 2 بار کرش ویندوز و مشاهده صفحه آبی و روزهای بعد تا 7 بار این اتفاق تکرار می‌شد. حتی تا تعویض مادربرد جدید هم پیش رفتم ولی تاثیری نداشت. تست رم و غیره هم انجام شد، مشکلی نبود. خلاصه اینجا بود که از سر ناچاری به این فکر افتادم که آیا این پیغام‌های صفحه‌ی آبی ویندوز را می‌شود تفسیر کرد؟ مشکل دقیقا از کجاست؟ چون در این موارد به هر کسی که مراجعه کنید بر اساس تجربه قبلی یک نسخه برای شما خواهد پیچید. رمت خرابه! بایوست رو ارتقاء بده! (این مورد تاثیر داشت! ولی تعداد کرش‌ها صفر نشد) مادربردت مشکل داره و ...

تمام این‌ها بر اساس تجربیات قبلی این افراد است و ارزشمند. ولی آیا این جواب‌ها قانع کننده هستند؟ چرا باید رم را عوض کرد؟ از کجا فهمیدید مادربرد مشکل داره؟



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

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



اکنون بعد از هر کرش و صفحه آبی ویندوز یک فایل دامپ در دایرکتوری C:\WINDOWS\Minidump تشکیل می‌شود. برای آنالیز این فایلها به صورت زیر می‌شود عمل کرد:
ابتدا برنامه زیر را دانلود کنید:
Debugging Tools for Windows

پس از نصب، Debugging Tools for Windows را خواهید داشت که جهت دیباگ کردن سیستم و آنالیز فایلهای دامپ و غیره کاربرد دارد.

سپس مطالعه مقاله زیر در مورد نحوه استفاده از این ابزار بسیار مفید است:
http://support.microsoft.com/kb/315263

به صورت خلاصه :
یک فایل bat درست کنید با محتویات زیر و دقیقا به همین شکل:
c:\windbg\kd -y srv*c:\symbols*http://msdl.microsoft.com/download/symbols -i c:\windows\i386 -z %1


در این دستور سه مورد قابل ملاحظه است:
الف) مسیر فایل kd.exe که توسط پکیج Debugging Tools for Windows نصب می‌شود. (مطابق سیستم خودتان آنرا اصلاح کنید)
ب) مسیر c:\windows\i386 بدین معنا است که دایرکتوری i386 سی دی ویندوز را در این مسیر کپی کرده‌اید یا خواهید کرد (نیاز به یک ویندوز تر و تازه و نصب نشده خواهد بود).
ج) مسیر c:\symbols خودبخود ایجاد خواهد شد و فایلهای مربوطه از سایت مایکروسافت توسط برنامه kd.exe دانلود می‌شود (بنابراین باید دسترسی به اینترنت نیز داشت).

فرض کنید نام این فایل را test.bat گذاشته‌اید.
برای آنالیز فایل Mini102607-07.dmp در دایرکتوری مینی دامپ ویندوز (07 در اینجا یعنی هفتمین کرش روز مربوطه!) دستور زیر را در خط فرمان صادر کنید:
test.bat C:\WINDOWS\Minidump\Mini102607-07.dmp
پس از مدتی این برنامه کار آنالیز را تمام خواهد کرد و گزارشی را ارائه می‌دهد (یک فایل log متنی تشکیل خواهد شد).
نتیجه یک نمونه از این آنالیزهای سیستم من به صورت زیر بود:
BAD_POOL_CALLER (c2)
The current thread is making a bad pool request. Typically this is at a bad IRQL level or double freeing the same allocation, etc.
Arguments:
Arg1: 00000007, Attempt to free pool which was already freed
Arg2: 00000cd4, (reserved)
Arg3: 02060008, Memory contents of the pool block
Arg4: 88b4a118, Address of the block of pool being deallocated

Debugging Details:
------------------

POOL_ADDRESS: 88b4a118

FREED_POOL_TAG: TCPc

BUGCHECK_STR: 0xc2_7_TCPc

CUSTOMER_CRASH_COUNT: 4

DEFAULT_BUCKET_ID: COMMON_SYSTEM_FAULT

PROCESS_NAME: System

LAST_CONTROL_TRANSFER: from 8054a583 to 804f9deb

STACK_TEXT:
ba4f3874 8054a583 000000c2 00000007 00000cd4 nt!KeBugCheckEx+0x1b
ba4f38c4 b043d3ff 88b4a118 00000000 ba4f390c nt!ExFreePoolWithTag+0x2a3
ba4f38d4 b043cca3 883ae760 883ae7f4 883ae7f4 tcpip!TCPClose+0x16
ba4f390c b02f3161 8a74fe20 883ae760 b02f2a6d tcpip!TCPDispatch+0x101
WARNING: Stack unwind information not available. Following frames may be wrong.
ba4f3984 b03e2046 00000001 00000000 ba4f39d8 vsdatant+0x45161
ba4f39d8 b03e921c 00000008 ba4f3aac 00000000 ipnat!NatpRedirectQueryHandler+0x250
ba4f3a70 00000000 8837d8e8 0000000d 000005ee ipnat!NatpDirectPacket+0xd2

STACK_COMMAND: kb

FOLLOWUP_IP:
vsdatant+45161
b02f3161 ?? ???

SYMBOL_STACK_INDEX: 4

SYMBOL_NAME: vsdatant+45161

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: vsdatant

IMAGE_NAME: vsdatant.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 46e0766a

FAILURE_BUCKET_ID: 0xc2_7_TCPc_vsdatant+45161

BUCKET_ID: 0xc2_7_TCPc_vsdatant+45161

Followup: MachineOwner

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

خوب! تا اینجا مشخص شد که دلیل کرش، درایور vsdatant.sys است. با جستجو در اینترنت مشخص شد که این درایور مربوط به فایروال زون آلارم است! (همین عبارت بالا یا نام درایور ذکر شده را مستقیما در گوگل جستجو کنید)
پس از آن زون آلارم را با outpost firewall جایگزین کردم و تا الان کرشی حاصل نشده است (حتی یکبار از سال قبل تا به امروز). جدا زندگی من مختل شده بود. تصور کنید سیستم شما روزی 7 بار کرش کند!! و چه تصورات نامربوطی را نسبت به فروشنده سخت افزار در ذهن خود مرور کرده باشید!

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

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

مطالب
آشنایی با آزمایش واحد (unit testing) در دات نت، قسمت 5

ادامه آشنایی با NUnit

حالت‌های مختلف Assert :

NUnit framework حالت‌های مختلفی از دستور Assert را پشتیبانی می‌کند که در ادامه با آنها آشنا خواهیم شد.

کلاس Assertion :
این کلاس دارای متدهای زیر است:
public static void Assert(bool condition)
public static void Assert(string message, bool condition)

تنها در حالتی این بررسی موفقیت آمیز گزارش خواهد شد که condition مساوی true باشد
public static void AssertEquals(string message, object expected, object actual)
public static void AssertEquals(string message, float expected, float actual, float delta)
public static void AssertEquals(string message, double expected, double actual, double delta)
public static void AssertEquals(string message, int expected, int actual)
public static void AssertEquals(int expected, int actual)
public static void AssertEquals(object expected, object actual)
public static void AssertEquals(float expected, float actual, float delta)
public static void AssertEquals(double expected, double actual, double delta)

تنها در صورتی این بررسی به اثبات خواهد رسید که اشیاء actual و expected یکسان باشند. (دلتا در اینجا به عنوان تلرانس آزمایش درنظر گرفته می‌شود)

public static void AssertNotNull(string message, object anObject)
public static void AssertNotNull(object anObject)

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

public static void AssertNull(string message, object anObject)
public static void AssertNull(object anObject)

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

public static void AssertSame(string message, object expected, object actual)
public static void AssertSame(object expected, object actual)

تنها در صورتی این بررسی به اثبات خواهد رسید که اشیاء actual و expected یکسان باشند.

public static void Fail(string message)
public static void Fail()

همواره Fail خواهد شد. (در مورد کاربرد آن در قسمت بعد توضیح داده خواهد شد)


نکته:
در یک متد آزمایش واحد شما مجازید به هرتعدادی که لازم است از متدهای Assertion استفاده نمائید. در این حالت اگر تنها یکی از متدهای assertion با شکست روبرو شود، کل متد آزمایش واحد شما مردود گزارش شده و همچنین عبارات بعدی Assertion بررسی نخواهند شد. بنابراین توصیه می‌شود به ازای هر متد آزمایش واحد، تنها از یک Assertion استفاده نمائید.

مهم!
کلاس Assertion منسوخ شده است و توصیه می‌شود بجای آن از کلاس Assert استفاده گردد.

آشنایی با کلاس Assert :
این کلاس از متدهای زیر تشکیل شده است:

الف) بررسی حالت‌های تساوی

Assert.AreEqual( object expected, object actual );

جهت بررسی تساوی دو شیء مورد بررسی و شیء مورد انتظار بکار می‌رود.

Assert.AreNotEqual( object expected, object actual );

جهت بررسی عدم تساوی دو شیء مورد بررسی و شیء مورد انتظار بکار می‌رود.
برای مشاهده انواع و اقسام overload های آن‌ها می‌توانید به راهنمای NUnit که پس از نصب، در پوشه doc آن قرار می‌گیرد مراجعه نمائید.

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

Assert.AreSame( object expected, object actual );
Assert.AreNotSame( object expected, object actual );

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

Assert.Contains( object anObject, IList collection );

ب) بررسی حالت‌های شرطی:

Assert.IsTrue( bool condition );

تنها در حالتی این بررسی موفقیت آمیز گزارش خواهد شد که condition مساوی true باشد

Assert.IsFalse( bool condition);

تنها در حالتی این بررسی موفقیت آمیز گزارش خواهد شد که condition مساوی false باشد
Assert.IsNull( object anObject );

این بررسی تنها در صورتی موفقیت آمیز گزارش می‌شود که شیء مورد نظر نال باشد.
Assert.IsNotNull( object anObject );

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

Assert.IsNaN( double aDouble );

این بررسی تنها در صورتی موفقیت آمیز گزارش می‌شود که شیء مورد نظر عددی نباشد (اگر با جاوا اسکریپت کار کرده باشید حتما با isNan آشنا هستید، is not a numeric ).

Assert.IsEmpty( string aString );
Assert.IsEmpty( ICollection collection );

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

Assert.IsNotEmpty( string aString );
Assert.IsNotEmpty( ICollection collection );

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

ج) بررسی حالت‌های مقایسه‌ای

Assert.Greater( double arg1, double arg2 );
Assert.GreaterOrEqual( int arg1, int arg2 );
Assert.Less( int arg1, int arg2 );
Assert.LessOrEqual( int arg1, int arg2 );

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

د) بررسی نوع اشیاء

Assert.IsInstanceOfType( Type expected, object actual );
Assert.IsNotInstanceOfType( Type expected, object actual );
Assert.IsAssignableFrom( Type expected, object actual );
Assert.IsNotAssignableFrom( Type expected, object actual );

این توابع و Overload های آن‌ها امکان بررسی نوع شیء مورد نظر را میسر می‌سازند.

ه) متدهای کمکی
Assert.Fail();
Assert.Ignore();

در حالت استفاده از ignore ، آزمایش واحد شما در حین اجرا ندید گرفته خواهد شد. از متد fail برای طراحی یک متد assertion سفارشی می‌توان استفاده کرد. برای مثال:
طراحی متدی که بررسی کند آیا یک رشته مورد نظر حاوی عبارتی خاص می‌باشد یا خیر:

public void AssertStringContains( string expected, string actual,
string message )
{
if ( actual.IndexOf( expected ) < 0 )
Assert.Fail( message );
}

و) متدهای ویژه‌ی بررسی رشته‌ها

StringAssert.Contains( string expected, string actual );
StringAssert.StartsWith( string expected, string actual );
StringAssert.EndsWith( string expected, string actual );
StringAssert.AreEqualIgnoringCase( string expected, string actual );
StringAssert.IsMatch( string expected, string actual );

این متدها و انواع overload های آن‌ها جهت بررسی‌های ویژه رشته‌ها بکار می‌روند. برای مثال آیا رشته مورد نظر حاوی عبارتی خاص است؟ آیا با عبارتی خاص شروع می‌شود یا با عبارتی ویژه، پایان می‌پذیرد و امثال آن.

ز) بررسی فایل‌ها

FileAssert.AreEqual( Stream expected, Stream actual );
FileAssert.AreEqual( FileInfo expected, FileInfo actual );
FileAssert.AreEqual( string expected, string actual );

FileAssert.AreNotEqual( Stream expected, Stream actual );
FileAssert.AreNotEqual( FileInfo expected, FileInfo actual );
FileAssert.AreNotEqual( string expected, string actual );

این متدها جهت مقایسه دو فایل بکار می‌روند و ورودی‌های آن‌ها می‌تواند از نوع stream ، شیء FileInfo و یا مسیر فایل‌ها باشد.

ح) بررسی collections

CollectionAssert.AllItemsAreInstancesOfType( IEnumerable collection, Type expectedType );
CollectionAssert.AllItemsAreNotNull( IEnumerable collection );
CollectionAssert.AllItemsAreUnique( IEnumerable collection );
CollectionAssert.AreEqual( IEnumerable expected, IEnumerable actual );
CollectionAssert.AreEquivalent( IEnumerable expected, IEnumerable actual);
CollectionAssert.AreNotEqual( IEnumerable expected, IEnumerable actual );
CollectionAssert.AreNotEquivalent( IEnumerable expected,IEnumerable actual );
CollectionAssert.Contains( IEnumerable expected, object actual );
CollectionAssert.DoesNotContain( IEnumerable expected, object actual );
CollectionAssert.IsSubsetOf( IEnumerable subset, IEnumerable superset );
CollectionAssert.IsNotSubsetOf( IEnumerable subset, IEnumerable superset);
CollectionAssert.IsEmpty( IEnumerable collection );
CollectionAssert.IsNotEmpty( IEnumerable collection );

به صورت اختصاصی و ویژه نیز می‌توان بررسی مقایسه‌ای را بر روی اشیایی از نوع IEnumerable انجام داد. برای مثال آیا معادل هستند، آیا شیء مورد نظر نال نیست و امثال آن.

نکته: در تمامی overload های این توابع، آرگومان message نیز وجود دارد. از این آرگومان زمانیکه آزمایش با شکست مواجه شد، جهت ارائه اطلاعات بیشتری استفاده می‌گردد.

ادامه دارد...

مطالب
بررسی مقدمات کتابخانه‌ی JSON.NET
چرا JSON.NET؟
JSON.NET یک کتابخانه‌ی سورس باز کار با اشیاء JSON در دات نت است. تاریخچه‌ی آن به 8 سال قبل بر می‌گردد و توسط یک برنامه نویس نیوزیلندی به نام James Newton King تهیه شده‌است. اولین نگارش آن در سال 2006 ارائه شد؛ مقارن با زمانی که اولین استاندارد JSON نیز ارائه گردید.
این کتابخانه از آن زمان تا کنون، 6 میلیون بار دانلود شده‌است و به علت کیفیت بالای آن، این روزها پایه اصلی بسیاری از کتابخانه‌ها و فریم ورک‌های دات نتی می‌باشد؛ مانند RavenDB تا ASP.NET Web API و SignalR مایکروسافت و همچنین گوگل نیز از آن جهت تدارک کلاینت‌های کار با API خود استفاده می‌کنند.
هرچند دات نت برای نمونه در نگارش سوم آن جهت مصارف WCF کلاسی را به نام DataContractJsonSerializer ارائه کرد، اما کار کردن با آن محدود است به فرمت خاص WCF به همراه عدم انعطاف پذیری و سادگی کار با آن. به علاوه باید درنظر داشت که JSON.NET از دات نت 2 به بعد تا مونو، Win8 و ویندوز فون را نیز پشتیبانی می‌کند.

برای نصب آن نیز کافی است دستور ذیل را در کنسول پاورشل نیوگت اجرا کنید:
 PM> install-package Newtonsoft.Json

معماری JSON.NET

کتابخانه‌ی JSON.NET از سه قسمت عمده تشکیل شده‌است:
الف) JsonSerializer
ب) LINQ to JSON
ج) JSON Schema


الف) JsonSerializer
کار JsonSerializer تبدیل اشیاء دات نتی به JSON و برعکس است. مزیت مهم آن امکانات قابل توجه تنظیم عملکرد و خروجی آن می‌باشد که این تنظیمات را به شکل ویژگی‌های خواص نیز می‌توان اعمال نمود. به علاوه امکان سفارشی سازی هر کدام نیز توسط کلاسی به نام JsonConverter، پیش بینی شده‌است.
یک مثال:
 var roles = new List<string>
{
   "Admin",
   "User"
};
string json = JsonConvert.SerializeObject(roles, Formatting.Indented);
در اینجا نحوه‌ی استفاده از JSON.NET را جهت تبدیل یک شیء دات نتی، به معادل JSON آن مشاهده می‌کنید. اعمال تنظیم Formatting.Indented سبب خواهد شد تا خروجی آن دارای Indentation باشد. برای نمونه اگر در برنامه‌ی خود قصد دارید فرمت JSON تو در تویی را به نحو زیبا و خوانایی نمایش دهید یا چاپ کنید، همین تنظیم ساده کافی خواهد بود.
و یا در مثال ذیل استفاده از یک anonymous object را مشاهده می‌کنید:
 var jsonString = JsonConvert.SerializeObject(new
{
   Id =1,
   Name = "Test"
}, Formatting.Indented);
به صورت پیش فرض تنها خواص عمومی کلاس‌ها توسط JSON.NET تبدیل خواهند شد.


تنظیمات پیشرفته‌تر JSON.NET

مزیت مهم JSON.NET بر سایر کتابخانه‌ها‌ی موجود مشابه، قابلیت‌های سفارشی سازی قابل توجه آن است. در مثال ذیل نحوه‌ی معرفی JsonSerializerSettings را مشاهده می‌نمائید:
var jsonData = JsonConvert.SerializeObject(new
{
   Id = 1,
   Name = "Test",
   DateTime = DateTime.Now
}, new JsonSerializerSettings
{
   Formatting = Formatting.Indented,
   Converters =
   {
      new JavaScriptDateTimeConverter()
   }
});
در اینجا با استفاده از تنظیم JavaScriptDateTimeConverter، می‌توان خروجی DateTime استانداردی را به مصرف کنندگان جاوا اسکریپتی سمت کاربر ارائه داد؛ با خروجی ذیل:
 {
  "Id": 1,
  "Name": "Test",
  "DateTime": new Date(1409821985245)
}


نوشتن خروجی JSON در یک استریم

خروجی متد JsonConvert.SerializeObject یک رشته‌است که در صورت نیاز به سادگی توسط متد File.WriteAllText در یک فایل قابل ذخیره می‌باشد. اما برای رسیدن به حداکثر کارآیی و سرعت می‌توان از استریم‌ها نیز استفاده کرد:
using (var stream = File.CreateText(@"c:\output.json"))
{
    var jsonSerializer = new JsonSerializer
   {
      Formatting = Formatting.Indented
   };
   jsonSerializer.Serialize(stream, new
   {
     Id = 1,
     Name = "Test",
     DateTime = DateTime.Now
   });
}
کلاس JsonSerializer و متد Serialize آن یک استریم را نیز جهت نوشتن خروجی می‌پذیرند. برای مثال response.Output برنامه‌های وب نیز یک استریم است و در اینجا نوشتن مستقیم در استریم بسیار سریعتر است از تبدیل شیء به رشته و سپس ارائه خروجی آن؛ زیرا سربار تهیه رشته JSON از آن حذف می‌گردد و نهایتا GC کار کمتری را باید انجام دهد.


تبدیل JSON رشته‌ای به اشیاء دات نت

اگر رشته‌ی jsonData ایی را که پیشتر تولید کردیم، بخواهیم تبدیل به نمونه‌ای از شیء User ذیل کنیم:
public class User
{
   public int Id { set; get; }
   public string Name { set; get; }
   public DateTime DateTime { set; get; }
}
خواهیم داشت:
 var user = JsonConvert.DeserializeObject<User>(jsonData);
در اینجا از متد DeserializeObject به همراه مشخص سازی صریح نوع شیء نهایی استفاده شده‌است.
البته در اینجا با توجه به استفاده از JavaScriptDateTimeConverter برای تولید jsonData، نیاز است چنین تنظیمی را نیز در حالت DeserializeObject مشخص کنیم:
var user = JsonConvert.DeserializeObject<User>(jsonData, new JsonSerializerSettings
{
   Converters = {  new JavaScriptDateTimeConverter() }
});


مقدار دهی یک نمونه یا وهله‌ی از پیش موجود

متد JsonConvert.DeserializeObject یک شیء جدید را ایجاد می‌کند. اگر قصد دارید صرفا تعدادی از خواص یک وهله‌ی موجود، توسط JSON.NET مقدار دهی شوند از متد PopulateObject استفاده کنید:
 JsonConvert.PopulateObject(jsonData, user);


کاهش حجم JSON تولیدی

زمانیکه از متد JsonConvert.SerializeObject استفاده می‌کنیم، تمام خواص عمومی تبدیل به معادل JSON آن‌ها خواهند شد؛ حتی خواصی که مقدار ندارند. این خواص در خروجی JSON، با مقدار null مشخص می‌شوند. برای حذف این خواص از خروجی JSON نهایی تنها کافی است در تنظیمات JsonSerializerSettings، مقدار NullValueHandling = NullValueHandling.Ignore مشخص گردد.
var jsonData = JsonConvert.SerializeObject(object, new JsonSerializerSettings
{
   NullValueHandling = NullValueHandling.Ignore,
   Formatting = Formatting.Indented
});
مورد دیگری که سبب کاهش حجم خروجی نهایی خواهد شد، تنظیم DefaultValueHandling = DefaultValueHandling.Ignore است. در این حالت کلیه خواصی که دارای مقدار پیش فرض خودشان هستند، در خروجی JSON ظاهر نخواهند شد. مثلا مقدار پیش فرض خاصیت int مساوی صفر است. در این حالت کلیه خواص از نوع int که دارای مقدار صفر می‌باشند، در خروجی قرار نمی‌گیرند.
به علاوه حذف Formatting = Formatting.Indented نیز توصیه می‌گردد. در این حالت فشرده‌ترین خروجی ممکن حاصل خواهد شد.


مدیریت ارث بری توسط JSON.NET

در مثال ذیل کلاس کارمند و کلاس مدیر را که خود نیز در اصل یک کارمند می‌باشد، ملاحظه می‌کنید:
public class Employee
{
    public string Name { set; get; }
}

public class Manager : Employee
{
    public IList<Employee> Reports { set; get; }
}
در اینجا هر مدیر لیست کارمندانی را که به او گزارش می‌دهند نیز به همراه دارد. در ادامه نمونه‌ای از مقدار دهی این اشیاء ذکر شده‌اند:
 var employee = new Employee { Name = "User1" };
var manager1 = new Manager { Name = "User2" };
var manager2 = new Manager { Name = "User3" };
manager1.Reports = new[] { employee, manager2 };
manager2.Reports = new[] { employee };
با فراخوانی
 var list = JsonConvert.SerializeObject(manager1, Formatting.Indented);
یک چنین خروجی JSON ایی حاصل می‌شود:
{
  "Reports": [
    {
      "Name": "User1"
    },
    {
      "Reports": [
        {
          "Name": "User1"
        }
      ],
      "Name": "User3"
    }
  ],
  "Name": "User2"
}
این خروجی JSON جهت تبدیل به نمونه‌ی معادل دات نتی خود، برای مثال جهت رسیدن به manager1 در کدهای فوق، چندین مشکل را به همراه دارد:
- در اینجا مشخص نیست که این اشیاء، کارمند هستند یا مدیر. برای مثال مشخص نیست User2 چه نوعی دارد و باید به کدام شیء نگاشت شود.
- مشکل دوم در مورد کاربر User1 است که در دو قسمت تکرار شده‌است. این شیء JSON اگر به نمونه‌ی معادل دات نتی خود نگاشت شود، به دو وهله از User1 خواهیم رسید و نه یک وهله‌ی اصلی که سبب تولید این خروجی JSON شده‌است.

برای حل این دو مشکل، تغییرات ذیل را می‌توان به JSON.NET اعمال کرد:
var list = JsonConvert.SerializeObject(manager1, new JsonSerializerSettings
{
   Formatting = Formatting.Indented,
   TypeNameHandling = TypeNameHandling.Objects,
   PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
با این خروجی:
{
  "$id": "1",
  "$type": "JsonNetTests.Manager, JsonNetTests",
  "Reports": [
    {
      "$id": "2",
      "$type": "JsonNetTests.Employee, JsonNetTests",
      "Name": "User1"
    },
    {
      "$id": "3",
      "$type": "JsonNetTests.Manager, JsonNetTests",
      "Reports": [
        {
          "$ref": "2"
        }
      ],
      "Name": "User3"
    }
  ],
  "Name": "User2"
}
- با تنظیم TypeNameHandling = TypeNameHandling.Objects سبب خواهیم شد تا خاصیت اضافه‌ای به نام $type به خروجی JSON اضافه شود. این نوع، در حین فراخوانی متد JsonConvert.DeserializeObject جهت تشخیص صحیح نگاشت اشیاء بکار گرفته خواهد شد و اینبار مشخص است که کدام شیء، کارمند است و کدامیک مدیر.
- با تنظیم PreserveReferencesHandling = PreserveReferencesHandling.Objects شماره Id خودکاری نیز به خروجی JSON اضافه می‌گردد. اینبار اگر به گزارش دهنده‌ها با دقت نگاه کنیم، مقدار $ref=2 را خواهیم دید. این مورد سبب می‌شود تا در حین نگاشت نهایی، دو وهله متفاوت از شیء با Id=2 تولید نشود.

باید دقت داشت که در حین استفاده از JsonConvert.DeserializeObject نیز باید JsonSerializerSettings یاد شده، تنظیم شوند.


ویژگی‌های قابل تنظیم در JSON.NET

علاوه بر JsonSerializerSettings که از آن صحبت شد، در JSON.NET امکان تنظیم یک سری از ویژگی‌ها به ازای خواص مختلف نیز وجود دارند.
- برای نمونه ویژگی JsonIgnore معروفترین آن‌ها است:
public class User
{
   public int Id { set; get; }

   [JsonIgnore]
   public string Name { set; get; }

   public DateTime DateTime { set; get; }
}
JsonIgnore سبب می‌شود تا خاصیتی در خروجی نهایی JSON تولیدی حضور نداشته باشد و از آن صرفنظر شود.

- با استفاده از ویژگی JsonProperty اغلب مواردی را که پیشتر بحث کردیم مانند NullValueHandling، TypeNameHandling و غیره، می‌توان تنظیم نمود. همچنین گاهی از اوقات کتابخانه‌های جاوا اسکریپتی سمت کاربر، از اسامی خاصی که از روش‌های نامگذاری دات نتی پیروی نمی‌کنند، در طراحی خود استفاده می‌کنند. در اینجا می‌توان نام خاصیت نهایی را که قرار است رندر شود نیز صریحا مشخص کرد. برای مثال:
[JsonProperty(PropertyName = "m_name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { set; get; }
همچنین در اینجا امکان تنظیم Order نیز وجود دارد. برای مثال مشخص کنیم که خاصیت X در ابتدا قرار گیرد و پس از آن خاصیت Y رندر شود.

- استفاده از ویژگی JsonObject به همراه مقدار OptIn آن به این معنا است که از کلیه خواصی که دارای ویژگی JsonProperty نیستند، صرفنظر شود. حالت پیش فرض آن OptOut است؛ یعنی تمام خواص عمومی در خروجی JSON حضور خواهند داشت منهای مواردی که با JsonIgnore مزین شوند.
[JsonObject(MemberSerialization.OptIn)]
public class User
{
    public int Id { set; get; }

    [JsonProperty]
    public string Name { set; get; }
 
    public DateTime DateTime { set; get; }
}

- با استفاده از ویژگی JsonConverter می‌توان نحوه‌ی رندر شدن مقدار خاصیت را سفارشی سازی کرد. برای مثال:
[JsonConverter(typeof(JavaScriptDateTimeConverter))]
public DateTime DateTime { set; get; }


تهیه یک JsonConverter سفارشی

با استفاده از JsonConverterها می‌توان کنترل کاملی را بر روی اعمال serialization و deserialization مقادیر خواص اعمال کرد. مثال زیر را در نظر بگیرید:
public class HtmlColor
{
   public int Red { set; get; }
   public int Green { set; get; }
   public int Blue { set; get; }
}

var colorJson = JsonConvert.SerializeObject(new HtmlColor
{
  Red = 255,
  Green = 0,
  Blue = 0
}, Formatting.Indented);
در اینجا علاقمندیم، در حین عملیات serialization، بجای اینکه مقادیر اجزای رنگ تهیه شده به صورت int نمایش داده شوند، کل رنگ با فرمت hex رندر شوند. برای اینکار نیاز است یک JsonConverter سفارشی را تدارک دید:
    public class HtmlColorConverter : JsonConverter
    {

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(HtmlColor);
        }

        public override object ReadJson(JsonReader reader, Type objectType,
                                        object existingValue, JsonSerializer serializer)
        {
            throw new NotSupportedException();
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var color = value as HtmlColor;
            if (color == null)
                return;

            writer.WriteValue("#" + color.Red.ToString("X2")
                + color.Green.ToString("X2") + color.Blue.ToString("X2"));
        }
    }
کار با ارث بری از کلاس پایه JsonConverter شروع می‌شود. سپس باید تعدادی از متدهای این کلاس پایه را بازنویسی کرد. در متد CanConvert اعلام می‌کنیم که تنها اشیایی از نوع کلاس HtmlColor را قرار است پردازش کنیم. سپس در متد WriteJson منطق سفارشی خود را می‌توان پیاده سازی کرد.
از آنجائیکه این تبدیلگر صرفا قرار است برای حالت serialization استفاده شود، قسمت ReadJson آن پیاده سازی نشده‌است.

در آخر برای استفاده از آن خواهیم داشت:
var colorJson = JsonConvert.SerializeObject(new HtmlColor
{
  Red = 255,
  Green = 0,
  Blue = 0
},  new JsonSerializerSettings
    {
      Formatting = Formatting.Indented,
      Converters = { new HtmlColorConverter() }
    });   
مطالب
WebStorage: قسمت دوم
در این مقاله قصد داریم نحوه‌ی کدنویسی webstorage را با کتابخانه‌هایی که در مقاله قبل معرفی کردیم بررسی کنیم.
ابتدا روش ذخیره سازی و بازیابی متداول آن را بررسی میکنیم که تنها توسط دو تابع صورت می‌گیرد. مطلب زیر برگرفته از w3Schools است:
دسترسی به شیء webstorage به صورت زیر امکان پذیر است:
window.localStorage
window.sessionStorage
ولی بهتر است قبل از ذخیره و بازیابی، از پشتیبانی مرورگر از webstorage اطمینان حاصل نمایید:
if(typeof(Storage) !== "undefined") {
    // Code for localStorage/sessionStorage.
} else {
    // Sorry! No Web Storage support..
}
برای ذخیره سازی و سپس خواندن به شکل زیر عمل می‌کنیم:
localStorage.setItem("lastname", "Smith");

//========================
localStorage.getItem("lastname");
خواندن می‌تواند حتی به شکل زیر هم صورت بگیرد:
var a=localStorage.lastname;
استفاده از store.js برای مرورگرهایی که از webstorage پشتیبانی نمی‌کنند به شکل زیر است:
//ذخیره مقدار
store.set('username', 'marcus')

//بازیابی مقدار
store.get('username')

//حذف مقدار
store.remove('username')

//حذف تمامی مقادیر ذخیره شده
store.clear()

//ذخیره ساختار
store.set('user', { name: 'marcus', likes: 'javascript' })

//بازیابی ساختار به شکل قبلی
var user = store.get('user')
alert(user.name + ' likes ' + user.likes)

//تغییر مستقیم مقدار قبلی
store.getAll().user.name == 'marcus'

//بازخوانی تمام مقادیر ذخیر شده توسط یک حلقه
store.forEach(function(key, val) {
    console.log(key, '==', val)
})
همچنین بهتر هست از یک فلگ برای بررسی فعال بودن storage استفاده نمایید. به این دلیل که گاهی کاربرها از پنجره‌های private استفاده می‌کنند که ردگیری اطلاعات آن ممکن نیست و موجب خطا می‌شود.
<script src="store.min.js"></script>
<script>
    init()
    function init() {
        if (!store.enabled) {
            alert('Local storage is not supported by your browser. Please disable "Private Mode", or upgrade to a modern browser.')
            return
        }
        var user = store.get('user')
        // ... and so on ...
    }
</script>
در صورتیکه بخشی از داده‌ها را توسط localstorage ذخیره نمایید و بخواهید از طریق storage به آن دسترسی داشته باشید، خروجی string خواهد بود؛ صرف نظر از اینکه شما عدد، شیء یا آرایه‌ای را ذخیره کرده‌اید.
در صورتیکه ساختار JSON را ذخیره کرده باشید، می‌توانید رشته برگردانده شده را با json.stringify و json.parse بازیابی و به روز رسانی کنید.
در حالت cross browser تهیه‌ی یک sessionStorage امکان پذیر نیست. ولی می‌توان به روش ذیل و تعیین یک زمان انقضاء آن را محدود کرد:
var storeWithExpiration = {
// دریافت کلید و مقدار و زمان انقضا به میلی ثانیه
    set: function(key, val, exp) {
//ایجاد زمان فعلی جهت ثبت تاریخ ایجاد
        store.set(key, { val:val, exp:exp, time:new Date().getTime() })
    },
    get: function(key) {
        var info = store.get(key)
//در صورتی که کلید داده شده مقداری نداشته باشد نال را بر میگردانیم
        if (!info) { return null }
//تاریخ فعلی را منهای تاریخ ثبت شده کرده و در صورتی که
//از مقدار میلی ثاینه بیشتر باشد یعنی منقضی شده و نال بر میگرداند
        if (new Date().getTime() - info.time > info.exp) { return null }
        return info.val
    }
}

// استفاده عملی از کد بالا
// استفاده از تایمر جهت نمایش واکشی داده‌ها قبل از نقضا و بعد از انقضا
storeWithExpiration.set('foo', 'bar', 1000)
setTimeout(function() { console.log(storeWithExpiration.get('foo')) }, 500) // -> "bar"
setTimeout(function() { console.log(storeWithExpiration.get('foo')) }, 1500) // -> null

مورد بعدی استفاده از سورس  cross-storage  است. اگر به یاد داشته باشید گفتیم یکی از احتمالاتی که برای ما ایجاد مشکل می‌کند، ساب دومین هاست که ممکن است دسترسی ما به یک webstorage را از ساب دومین دیگر از ما بگیرد.
این کتابخانه به دو جز تقسیم شده است یکی هاب Hub و دیگری Client .
ابتدا نیاز است که هاب را آماده سازی و با ارائه یک الگو از آدرس وب، مجوز عملیات را دریافت کنیم. در صورتیکه این مرحله به فراموشی سپرده شود، انجام هر نوع عمل روی دیتاها در نظر گرفته نخواهد شد.
CrossStorageHub.init([
  {origin: /\.example.com$/,            allow: ['get']},
  {origin: /:\/\/(www\.)?example.com$/, allow: ['get', 'set', 'del']}
]);
حرف $ در انتهای عبارت باعث مشود که دامنه‌ها با دقت بیشتری در Regex بررسی شوند و دامنه زیر را معتبر اعلام کند:
valid.example.com
ولی دامنه زیر را نامعتبر اعلام می‌کند:
invalid.example.com.malicious.com
همچنین می‌توانید تنظیماتی را جهت هدر‌های CSP و CORS، نیز اعمال نمایید:
{
  'Access-Control-Allow-Origin':  '*',
  'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
  'Access-Control-Allow-Headers': 'X-Requested-With',
  'Content-Security-Policy':      "default-src 'unsafe-inline' *",
  'X-Content-Security-Policy':    "default-src 'unsafe-inline' *",
  'X-WebKit-CSP':                 "default-src 'unsafe-inline' *",
}
پس کار را بدین صورت آغاز می‌کنیم، یک فایل به نام hub.htm درست کنید و هاب را آماده سازید:
hub.htm
<script type="text/javascript" src="~/Scripts/cross-storage/hub.js"></script>
<script>
 CrossStorageHub.init([
      {origin: /.*localhost:300\d$/, allow: ['get', 'set', 'del']}
    ]);
  </script>
کد بالا فقط درخواست‌های هاست لوکال را از پورتی که ابتدای آن با 300 آغاز می‌شود، پاسخ می‌دهد و مابقی درخواست‌ها را رد می‌کند. متدهای ایجاد، ویرایش و حذف برای این آدرس معتبر اعلام شده است. 
در فایل دیگر که کلاینت شناخته می‌شود باید فایل hub معرفی شود تا تنظیمات هاب خوانده شود:
var storage = new CrossStorageClient('http://localhost:3000/example/hub.html');

var setKeys = function () {
      
return storage.set('key1', 'foo').then(function() {
return storage.set('key2', 'bar');
      });
};
در خط اول، فایل هاب معرفی شده و تنظیمات روی این صفحه اعمال می‌شود. سپس در خطوط بعدی داده‌ها ذخیره می‌شوند. از آنجا که با هر یکبار ذخیره، return صورت می‌گیرد و تنها اجازه‌ی ورود یک داده را داریم، برای حل این مشکل متد then پیاده سازی شده است. متغیر setKeys شامل یک آرایه از کلیدها خواهد بود.
نحوه‌ی ذخیره سازی بدین شکل هم طبق مستندات صحیح است:
storage.onConnect().then(function() {

  return storage.set('key', {foo: 'bar'});

}).then(function() {

  return storage.set('expiringKey', 'foobar', 10000);

});
در کد بالا ابتدا یک داده دائم ذخیره شده است و در کد بعد یک داده موقت که بعد از 10 ثانیه اعتبار خود را از دست می‌دهد.
برای خواندن داده‌های ذخیره شده به نحوه زیر عمل می‌کنیم:
storage.onConnect().then(function() {
  return storage.get('key1');
}).then(function(res) {
  return storage.get('key1', 'key2', 'key3');
}).then(function(res) {
  // ...
});
کد بالا نحوه‌ی خواندن مقادیر را به شکل‌های مختلفی نشان میدهد و مقدار بازگشتی آن‌ها یک آرایه از مقادیر است؛ مگر اینکه تنها یک مقدار برگشت داده شود. مقدار بازگشتی در تابع بعدی به عنوان یک آرگومان در دسترس است. در صورتی که خطایی رخ دهد، قابلیت هندل آن نیز وجود دارد:
storage.onConnect()
    .then(function() {
      return storage.get('key1', 'key2');
    })

.then(function(res) {
      console.log(res); // ['foo', 'bar']
    })['catch'](function(err) {
      console.log(err);
    });
برای باقی مسائل چون به دست آوردن لیست کلیدهای ذخیره شده، حذف کلید‌های مشخص شده، پاکسازی کامل داده‌ها و ... به مستندات رجوع کنید.
در اینجا جهت سازگاری با مرورگرهای قدیمی خط زیر را به صفحه اضافه کنید:
<script src="https://s3.amazonaws.com/es6-promises/promise-1.0.0.min.js"></script>
ذخیره‌ی اطلاعات به شکل یونیکد، فضایی دو برابر کدهای اسکی میبرد و با توجه به محدود بودن حجم webstorage به 5 مگابایت ممکن است با کمبود فضا مواجه شوید. در صورتیکه قصد فشرده سازی اطلاعات را دارید می‌توانید از کتابخانه lz-string استفاده کنید. ولی توجه به این نکته ضروری است که در صورت نیاز، عمل فشرده سازی را انجام دهید و همینطوری از آن استفاده نکنید.

IndexedDB API
آخرین موردی که بررسی می‌شود استفاده از IndexedDB API است که با استفاده از آن میتوان با webstorage همانند یک دیتابیس رفتار کرد و به سمت آن کوئری ارسال کرد. 
var request = indexedDB.open("library");

request.onupgradeneeded = function() {
  // The database did not previously exist, so create object stores and indexes.
  var db = request.result;
  var store = db.createObjectStore("books", {keyPath: "isbn"});
  var titleIndex = store.createIndex("by_title", "title", {unique: true});
  var authorIndex = store.createIndex("by_author", "author");

  // Populate with initial data.
  store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
  store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
  store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
};

request.onsuccess = function() {
  db = request.result;
};
کد بالا ابتدا به دیتابیس library متصل می‌شود و اگر وجود نداشته باشد، آن را می‌سازد. رویداد onupgradeneeded برای اولین بار اجرا شده و در آن می‌توانید به ایجاد جداول و اضافه کردن داده‌های اولیه بپردازید؛ یا اینکه از آن جهت به ارتقاء ورژن دیتابیس استفاده کنید. خصوصیت result، دیتابیس باز شده یا ایجاد شده را باز می‌گرداند. در خط بعدی جدولی با کلید کد ISBN کتاب تعریف شده است. در ادامه هم دو ستون اندیس شده برای عنوان کتاب و نویسنده معرفی شده است که عنوان کتاب را یکتا و بدون تکرار در نظر گرفته است. سپس در جدولی که متغیر store به آن اشاره می‌کند، با استفاده از متد put، رکوردها داخل آن درج می‌شوند. در صورتیکه کار با موفقیت انجام شود رویداد onSuccess فراخوانی می‌گردد.
برای انجام عملیات خواندن و نوشتن باید از تراکنش‌ها استفاده کرد:
var tx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});

tx.oncomplete = function() {
  // All requests have succeeded and the transaction has committed.
};
در خط  اول ابتدا یک خط تراکنشی بین ما و جدول books با مجوز خواندن و نوشتن باز می‌شود و در خط بعدی جدول books را در اختیار می‌گیریم و همانند کد قبلی به درج داده‌ها می‌پردازیم. در صورتیکه عملیات با موفقیت به اتمام برسد، متغیر تراکنش رویدادی به نام oncomplete فراخوانی می‌گردد. در صورتیکه قصد دارید تنها مجوز خواندن داشته باشید، عبارت readonly را به کار ببرید.
var tx = db.transaction("books", "readonly");
var store = tx.objectStore("books");
var index = store.index("by_author");

var request = index.openCursor(IDBKeyRange.only("Fred"));
request.onsuccess = function() {
  var cursor = request.result;
  if (cursor) {
    // Called for each matching record.
    report(cursor.value.isbn, cursor.value.title, cursor.value.author);
    cursor.continue();
  } else {
    // No more matching records.
    report(null);
  }
};
در دو خط اول مثل قبل، تراکنش را دریافت می‌کنیم و از آنجا که می‌خواهیم داده را بر اساس نام نویسنده واکشی کنیم، ستون اندیس شده نام نویسنده را دریافت کرده و با استفاده از متد opencursor درخواست خود را مبنی بر واکشی رکوردهایی که نام نویسنده fred است، ارسال میداریم. در صورتیکه عملیات با موفقیت انجام گردد و خطایی دریافت نکنیم رویداد onsuccess فراخوانی می‌گردد. در این رویداد با دو حالت برخورد خواهیم داشت؛ یا داده‌ها یافت می‌شوند و رکوردها برگشت داده می‌شوند یا هیچ رکوردی یافت نشده و مقدار نال برگشت خواهد خورد. با استفاده از cursor.continue می‌توان داده‌ها را به ترتیب واکشی کرده و مقادیر رکورد را با استفاده خصوصیت value  به سمت تابع report ارسال کرد.
کدهای بالا همه در مستندات معرفی شده وجود دارند و ما پیشتر توضیح ابتدایی در مورد آن دادیم و برای کسب اطلاعات بیشتر می‌توانید به همان مستندات معرفی شده رجوع کنید. برای idexedDB هم می‌توانید از این منابع +  +  +  استفاده کنید که خود w3c منبع فوق العاده‌تری است.

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

 شماره بازبینی Revision Number

شماره ساخت Build Number
شماره جزئی Minor Number
شماره اصلی Major Number
 2 719
5
2

اسمبلی بالا به ورژن یا نسخه‌ی 2.5.719.2 اشاره دارد که دو شماره‌ی اول (2.5) مثل تمامی برنامه‌ها به میزان تغییرات کارکردی یک اسمبلی اشاره دارد و عموم مردم هم نسخه یک نرم افزار را به همین دو عدد میشناسند. عدد سوم به این اشاره دارد که در شرکت، این ورژن از اسمبلی چندبار build شده است و شما باید به ازای هر Build این عدد را افزایش دهید. عدد آخری به این اشاره دارد که در طول روز انتشار، این چندمین Build بوده است. اگر در زمان ارائه‌ی این اسمبلی باگ مهمی در آن یافت شود، با هر بار Build آن در یک روز، باید این عدد افزایش یابد و برای روزهای آتی این مقدار مجددا آغاز می‌شود. مایکروسافت از این سیستم نسخه بندی استفاده می‌کند و بسیار توصیه می‌شود که توسعه دهندگان هم از این روش تبعیت کنند.

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

AssemblyFileVersion: این شماره نسخه در منابع اطلاعاتی Win32 ذخیره می‌گردد و کاربرد آن تنها جهت نمایش این اطلاعات است و CLR هیچ گونه ارجاع یا استفاده‌ای از آن ندارد. در حالت عادی، شما باید دو شماره اولی را جهت نمایش عمومی مشخص کنید. سپس با هر بار Build کردن، شماره‌های ساخت و بازبینی را هم به همان ترتیب افزایش می‌دهید. حالت ایده‌آل این است که ابزار AL یا CSC به طور خودکار با هر بار Build شدن، با توجه به زمان سیستم، به طور خودکار این دو شماره آخر را مشخص کنند ولی متاسفانه واقعیت این است که چنین کاری صورت نمی‌گیرد. این اعداد جهت نمایش و شناسایی اسمبلی برای اشکال زدایی مشکلات اسمبلی به کار می‌رود.

AssemblyInformationalVersion: این شماره نسخه هم در منابع اطلاعاتی Win32 ذخیره می‌گردد و تنها هدف اطلاعاتی دارد. مجددا اینکه CLR هیچ گونه اهمیتی به آن نمی‌دهد. این شماره نسخه به محصولی اشاره می‌کند که شامل این اسمبلی است.

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

AssemblyVersion: این شماره نسخه در جدول متادیتای AssemblyDef ذخیره می‌گردد. CLR از این شماره نسخه جهت اتصال نام قوی Strongly Named به اسمبلی استفاده می‌کند (این مورد در فصل سه کتاب  توضیح داده شده است). این شماره نسخه بسیار مهم بوده و به عنوان شناسه‌ی یکتا برای اسمبلی استفاده می‌شود.

موقعیکه شما شروع به توسعه‌ی یک اسمبلی می‌کنید، باید هر 4 شماره نسخه را مقداردهی کرده و تا زمانیکه توسعه‌ی نسخه بعدی آن اسمبلی را آغاز نکرده‌اید، نباید آن را تغییر دهید. علت اصلی آن این است که موقعیکه اسمبلی «الف» با یک نام قوی به اسمبلی «ب» ارجاع می‌کند، نسخه‌ی اسمبلی «ب» در ورودی جدول AssemblyRef  اسمبلی «الف» قرار گرفته است. این مورد باعث می‌شود زمانیکه CLR به بارگزاری اسمبلی «ب» احتیاج دارد، دقیقا می‌داند که چه نسخه‌ای با اسمبلی «الف» ساخته و تست شده است . البته این احتمال وجود دارد که CLR نسخه‌ای متفاوت از اسمبلی را با Binding Redirect بار کند که ادامه‌ی این مباحث در فصل سوم دنبال می‌شود.

مطالب
ترفندهای یونیکد برای زبان‌های راست به چپ
آشنایی با RLE

الگوریتم پردازش دوطرفه‌ی یونیکد، جهت و سمت نمایش متن را بر اساس خواص جهتی هر حرف مشخص می‌کند. در این حالت اگر متن مورد نمایش، انگلیسی و یا فارسی خالص باشند به خوبی عمل می‌کند؛ اما اگر ترکیب این دو را در یک  رشته داشته باشیم، نیاز است نحوه‌ی جهت گیری و نمایش حروف را به Unicode bidirectional algorithm معرفی کنیم. این نوع مشکلات را فارسی زبان‌ها در حین نمایش ترکیبی از متن فارسی و انگلیسی در Tooltips، برنامه‌های نمایش زیرنویس‌های فیلم‌ها، برنامه‌های گزارشگیری و امثال آن به وفور مشاهده می‌کنند.
راه حل استاندارد یونیکد آن، استفاده از حروف نامرئی یونیکد است که جهت نمایشی متن جاری را بازنویسی می‌کنند:
U+202A:   LEFT-TO-RIGHT EMBEDDING (LRE)
U+202B:   RIGHT-TO-LEFT EMBEDDING (RLE)
U+202D:   LEFT-TO-RIGHT OVERRIDE (LRO)
U+202E:   RIGHT-TO-LEFT OVERRIDE (RLO)
U+202C:   POP DIRECTIONAL FORMATTING (PDF)
برای مثال حرف یونیکد نامرئی U202B به این معنا است: «از این لحظه به بعد تا اطلاع ثانوی، متن نمایش داده شده راست به چپ است؛ صرفنظر از خواص جهتی حروف مورد استفاده».
این تا اطلاع ثانوی یا POP نیز توسط حرف U202C مشخص شده و به پایان می‌رسد. به عبارتی یونیکد شبیه به یک پشته یا Stack عمل می‌کند.


مثال اول
عبارت «متن فارسی به همراه جمله‌ی this is a test انگلیسی» را در نظر بگیرید. اکنون فرض کنید می‌خواهیم از آن جهت ارائه یک فایل readme مخصوص GitHub با فرمت mark down یا md استفاده کنیم:


همانطور که ملاحظه می‌کنید، جمله معکوس شده‌است. برای رفع این مشکل می‌توان از کاراکتر نامرئی یونیکد 202b استفاده کرد. البته در mark down امکان تعریف ساده‌تر این کاراکتر به صورت ذیل نیز پیش بینی شده‌است:
 &#x202b;



مثال دوم

اغلب نمایشگرهای چپ به راست متون نیز در حالت پیش فرض، عبارت مثال اول را معکوس نمایش می‌دهند:


اگر از notepad استفاده کنید، به صورت توکار امکان افزودن RLE را به ابتدای جمله دارد:


مثال سوم

در زبان‌های دات نتی نیز جهت نمایش صحیح متون ترکیبی، می‌توان حرف RLE را به صورت ذیل به ابتدای یک جمله اضافه کرد:
public const char RightToLeftEmbedding = (char)0x202B;
این مورد خصوصا در ابزارهای گزارشگیری یا کار با API ویندوز می‌تواند مفید باشد.



تشخیص راست به چپ بودن متن

در محیط وب جهت نمایش صحیح یک متن نیز می‌توان به مرورگرها کمک کرد. تعریف dir=rtl تفاوتی با قرار دادن RLE در ابتدای یک متن ندارد. در این حالت نیاز است بدانیم حروف RTL در چه بازه‌ای از شماره حروف یونیکد قرار می‌گیرند:
  Right-to-left Unicode blocks for modern scripts are:

 Consecutive range of the main letters:
 U+0590 to U+05FF - Hebrew
 U+0600 to U+06FF - Arabic
 U+0700 to U+074F - Syriac
 U+0750 to U+077F - Arabic Supplement
 U+0780 to U+07BF - Thaana
 U+07C0 to U+07FF - N'Ko
 U+0800 to U+083F - Samaritan

 Arabic Extended:
 U+08A0 to U+08FF - Arabic Extended-A

 Consecutive presentation forms:
 U+FB1D to U+FB4F - Hebrew presentation forms
 U+FB50 to U+FDFF - Arabic presentation forms A

 More Arabic presentation forms:
 U+FE70 to U+FEFF - Arabic presentation forms B
که یک نمونه‌ی ساده شده‌ی این بازه‌ها، به صورت ذیل است:
private static readonly Regex _matchArabicHebrew =
new Regex(@"[\u0600-\u06FF,\u0590-\u05FF]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

  public static bool ContainsRtlFarsi(this string txt)
  {
       return !string.IsNullOrEmpty(txt) && _matchArabicHebrew.IsMatch(txt);
  }
و حالت پیشرفته‌تر آن‌را که سایت توئیتر برای ارائه‌ی یک جعبه متنی به صورت خودکار راست به چپ شونده، مورد استفاده قرار می‌دهد، در اینجا می‌توانید مطالعه کنید:
RTLText.module.js


نمایش صحیح عبارات ممیز دار در یک گزارش راست به چپ


استاندارد یونیکد یک سری کاراکتر را «کاراکتر ضعیف» معرفی کرده‌است. برای مثال کاراکتر اسلش بکار رفته در یک تاریخ هم از این دست است. بنابراین اگر در یک گزارش تولیدی، شماره کد ممیز دار و یا یک تاریخ را معکوس مشاهده می‌کنید به این علت است که یک «نویسه ضعیف» مثل اسلش نمی‌تواند جهت را تغییر دهد؛ مگر اینکه از یک «نویسه قوی» برای دستکاری آن استفاده شود (مانند RLE و POP که در ابتدای بحث معرفی شدند).
یک مطلب تکمیلی در این مورد: «iTextSharp و نمایش صحیح تاریخ در متنی راست به چپ»
این اصول در تمام محیط‌هایی که از یونیکد پشتیبانی می‌کنند صادق است و تفاوتی نمی‌کند که ویندوز باشد یا Adobe reader و یا یک ابزار گزارشگیری که اصلا برای محیط‌های راست به چپ طراحی نشده‌است.


کار با اعراب در متون راست به چپ

در یونیکد یک حرف می‌تواند از یک یا چند code point تشکیل شود. در حالت FormC، هر حرف، با اعراب آن یک code point را تشکیل می‌دهند. در حالت FormD، حرف با اعراب آن دو code point را تشکیل خواهند داد. به همین جهت نیاز است رشته را تبدیل به حالت D کرد تا بتوان اعراب آن‌را مجزای از حروف پایه، حذف نمود.
البته اعراب در اینجا به اعراب عربی ختم نمی‌شود. یک سری حروف اروپایی مانند  "ä" ،"ö" و "ü" را نیز شامل می‌شود.
یک مطلب تکمیلی در این مورد:  «حذف اعراب از حروف و کلمات»
نظرات مطالب
راه اندازی StimulSoft Report در ASP.NET MVC
در مورد خطا، اگر فایل گزارش و دیتای آن موجود باشد شاید بتوان بررسی کرد.
در مورد متد SynchronizeBusinessObjects  و یا  Synchronize - برای RegData - هم برای ایجاد ساختار دیتای ارسالی به گزارش در قسمت Dictionary استفاده می‌شود و اجباری به استفاده از آن نیست. می‌توان بر اساس پارامتر maxLevel  تعداد لایه‌های نمایش را مقداردهی کرد.
در مثال بالا به صورت زیر نتایج خروجی آن را با هم مقایسه کنید:
report.RegBusinessObject("CustomerServiceReport", data);

report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportNoSync.mrt"));

report.Dictionary.SynchronizeBusinessObjects();
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync.mrt"));

report.Dictionary.SynchronizeBusinessObjects(0);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync0.mrt"));

report.Dictionary.SynchronizeBusinessObjects(1);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync1.mrt"));

report.Dictionary.SynchronizeBusinessObjects(2);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync2.mrt"));

NoSync

Sync

 Sync0

Sync1

Sync2