اشتراکها
طراحی Exception messages مناسب
پاسخ به پرسشها
چطور باید برای سایت dntips.ir پیشنهاد و نظر ارسال کرد؟
این پیشنهادات بر اساس تگهای وارد شده توسط کاربران سایت است ...
مطالب
داستانی از Unicode
یکی از مباحثی که به نظرم هر دانشجوی رشته کامپیوتر، فناوری اطلاعات و علاقمند به این حوزه باید بداند بحث کاراکترهاست؛ جدا از اینکه همه ما در مورد وجود ascii یا UTF-8 و ... و توضیحات مختصر آن اطلاع داریم ولی عدهای از دوستان مثل من هنوز اطلاعات پایهایتر و جامعتری در این باره نداریم؛ در این مقاله که برداشتی از وب سایت smashing magazine و W3 است به این مبحث میپردازیم.
کامپیوترها تنها با اعداد سر و کار دارند نه با حروف؛ پس این بسیار مهم هست که همه کامپیوترها بر روی یک سری اعداد مشخص به عنوان نمایندهای از حروف به توافق برسند. این توافق یکسان بین همه کامپیوترها بسیار مهم هست و باید طبق یک استاندارد مشترک استفاده شود تا در همه سیستمها قابل استفاده و انتقال باشد؛ برای همین در سال 1960 اتحادیه استاندارهای آمریکا، یک سیستم رمزگذاری 7 بیتی را ایجاد کرد؛ به نام American Standard Code for Information Interchange یا کد استاندارد سازی شده آمریکایی برای تبادل اطلاعات یا همان ASCII. این هفت بیت به ما اجازه میداد تا 128 حرف را کدگذاری کنیم. این مقدار برای حروف کوچک و بزرگ انگلیسی و هم چنین حروف لاتین، همراه با کدگذاری ارقام و یک سری علائم نگارشی و کاراکترهایی از قبیل space ، tab و موارد مشابه و نهایتا کلیدهای کنترلی کافی بود. در سال 1968 این استاندارد توسط رییس جمهور وقت آمریکا لیندون جانسون به رسمیت شناخته شده و همه سیستمهای کامپیوتری ملزم به رعایت و استفاده از این استاندارد شدند.
برای لیست کردن و دیدن این کدها و نمادهای حرفیشان میتوان با یک زبان برنامه نویسی یا اسکریپتی آنها را لیست کرد. کد زیر نمونهای از کد نوشته شده در جاوااسکریپت است.
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<128; i++) document.writeln ((i%32?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
بیایید با یک برنامه علائم را در این 15 استاندارد بررسی کنیم. تکه کدی که من در اینجا نوشتم یک لیست را که در آن اعداد یک تا 16 لیست شده است، نشان میدهد که با انتخاب هر کدام، کدها را از 0 تا 255 بر اساس هر استاندارد به ترتیب نمایش میدهد. این کار توسط تعیین استاندارد در تگ متا رخ میدهد.
در زمان بارگذاری، استانداردها با کد زیر به لیست اضافه میشوند.در مرحله بعد لیستی که postback را در آن فعال کردهایم، کد زیر را اجرا میکند. در این کد ابتدا charset انتخاب شده ایجاد شده و سپس یکی یکی کدها را به کاراکتر تبدیل میکنیم و رشته نهایی را درج میکنیم: ( دانلود فایلهای زیر )
private String ISO = "ISO-8859-"; protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { for (int i = 1; i < 16; i++) { ListItem item = new ListItem(); item.Text = ISO + i.ToString(); item.Value = i.ToString(); DropDownList1.Items.Add(item); } ShowCodes(1); } } protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { if (DropDownList1.SelectedItem != null) { int value = int.Parse(DropDownList1.SelectedValue); ShowCodes(value); } } private void ShowCodes(int value) { Response.Charset = ISO + value; string s = ""; for (int i = 0; i < 256; i++) { char ch = (char)i; s += i + "-" + ch; s += "<br/>";//br tag } Label1.Text = s; }
تقریبا سال 1990 بود که بسیاری از اسناد به همین شیوهها نوشته و ذخیره شد. ولی باز برای بسیاری از زبانها، حتی داشتن یکی دو حرف بیشتر مشکلاتی را به همراه داشت. مثلا حروف بعضی زبانها مثل چینی و ژاپنی که 256 عدد، پاسخگو نبود و با آمدن شبکهای چون اینترنت و بحث بین المللی شدن و انتقال اطلاعات، این مشکل بزرگتر از آنچه بود، شد.
یونیکد نجات بخش
اواخر سال 1980 بود که پیشنهاد یک استاندارد جدید داده شد و در آن به هر حرف و یا نماد در هر زبانی یک عدد یکتا نسبت داده میشد و باید بیشتر از 256 عدد میبود که آن را یونیکد نامیدند. در حال حاضر یونیکد نسخه 601 شامل 110 هزار کد می شود. 128 تای آن همانند اسکی است. از 128 تا 255 مربوط به علائم و علامتهاست که بیشتر آنها از استاندارد ISO-8859-1 وام گرفته شدهاند. از 256 به بعد هم بسیاری از علائم تلفظی و ... وجود دارد و از کد 880 زبان یونایی آغاز شده و پس از آن زبانهای سیریلیک، عبری، عربی و الی آخر ادامه مییابند. برای نشان دادن یک کد یونیکد به شکل هگزادسیمال U+0048 نوشته میشود و برای تبدیل آن به دسیمال 4*16+8=72 استفاده میشود. به هر کد یونیکد، کد پوینت code point گفته میشود.
در ویکی پدیای فارسی، یونیکد اینگونه توضیح داده شده است: "نقش یونیکد در پردازش متن این است که به جای یک تصویر برای هر نویسه یک کد منحصر به فرد ارایه میکند. به عبارت دیگر، یونیکد یک نویسه را به صورت مجازی ارایه میکند و کار ساخت تصویر (شامل اندازه، شکل، قلم، یا سبک) نویسه را به عهده نرمافزار دیگری مانند مرورگر وب یا واژهپرداز میگذارد. "
یونیکد از 8 بیت یا 16 بیت استفاده نمیکند و با توجه به اینکه دقیقا 110 ،116 کد را حمایت میکند به 21 بیت نیاز دارد. هر چند که کامپیوترها امروزه از معمارهای 32 بیتی و 64 بیتی استفاده میکنند، این سوال پیش میآید که ما چرا نمیتوانیم کاراکترها را بر اساس این 32 بیت و 64 بیت قرار بدهیم؟ پاسخ این سوال ایناست که چنین کاری امکان پذیر است و بسیاری از نرم افزارهای نوشته شده در زبان سی و سی ++ از wide character حمایت میکنند. این مورد یک کاراکتر 32 بیتی به نام wchar_t است که نوعی داده char توسعه یافته هشت بیتی است و بسیاری از مرورگرهای امروزی از آن بهره مند هستند و تا 4 بیلیون کاراکتر را حمایت میکنند.
شکل زیر دسته بندی از انواع زبانهای تحت حمایت خود را در نسخه 5.1 یونیکد نشان میدهد:
کد زیر در جاوااسکریپت کاراکترهای یونیکد را در مرز معینی که برایش مشخص کردهایم نشان میدهد:
<html> <body> <style type="text/css">p {float: left; padding: 0 15px; margin: 0;}</style> <script type="text/javascript"> for (var i=0; i<2096; i++) document.writeln ((i%256?'':'<p>') + i + ': ' + String.fromCharCode (i) + '<br>'); </script> </body> </html>
CSS & Unicode
یکی از جذابترین خصوصیات در css، خصوصیت Unicode-range است. شما میتوانید برای هر کاراکتر یا حتی رنج خاصی از کاراکترها، فونت خاصی را اعمال کنید. به دو نمونه زیر دقت کنید:
/* cyrillic */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB; } /* latin-ext */ @font-face { font-style: normal; src: local('Roboto Regular'), local('Roboto-Regular'), url(http://fonts.gstatic.com/s/roboto/v14/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; }
به 65536 کد اول یونیکد Basic Multilingual Plan یا به اختصار BMP میگویند و شامل همه کاراکترهای رایجی است که مورد استفاده قرار میگیرند. همچنین یونیکد شامل یک فضای بسیار بزرگ خالی است که به شما اجازه توسعه دادن آن را تا میلیونها کد میدهد. به کاراکترهایی که در این موقعیت قرار میگیرند supplementary characters یا کاراکترهای مکمل گویند. برای اطلاعات بیشتر میتوانید به سایت رسمی یونیکد مراجعه کنید. در اینجا هم مباحث آموزشی خوبی برای یونیکد دارد، هر چند کاملتر آن در سایت رسمی برای نسخههای مختلف یونیکد وجود دارد.
UTF-8 نجات بخش میشود
بسیاری از مشکلات ما حل شد. همه حروف را داریم و مرورگرها نیز همه حروف را میشناسند؛ ولی برای ما دو مشکل ایجاد کرده است:
- بسیاری از نرم افزارها و پروتکلها هنوز 8 بیتی کار میکنند.
- اگر یک متن انگلیسی ارسال کنید، 8 بیت هم کافی است ولی در این حالت 32 بیت جابجا میشود؛ یعنی 4 برابر و در ارسال و دریافت و پهنای باند برایمان مشکل ایجاد میکند.
برای حل این مشکل استاندارهای زیادی چون USC-2 یا UTF-16 ایجاد شدند ولی در سالهای اخیر برنده رقابت، UTF-8 بود که مخفف عبارت Universal Character Set Transformation Format 8 bit میباشد. این کدگذاری بسیار هوشمندانه عمل میکند. موقعی که شما کاراکتری را وارد میکنید که کدش بین 0 تا 255 است، 8 بیت به آن اختصاص میدهد و اگر در محدودهای است که بتوان دو بایت را به آن اختصاص داد، دوبایت و اگر بیشتر بود، سه بایت و اگر باز بیشتر بود 4 بایت به آن اختصاص میدهد. پس با توجه به محدوده کد، تعداد بایتها مشخص میشوند. بنابراین یک متن نوشته شده انگلیسی که مثلا از کدهای بین 0تا 128 استفاده میکند و فرمت ذخیره آن UTF-8 باشد به ازای هر کارکتر یک بایت ذخیره میکند.
مقایسهای بین نسخههای مختلف :
همانطور که میبینید UTF-8 برای کاراکترهای اسکی، از یک بایت و برای دیگر حروف از دوبایت و برای بقیه BMPها از سه بایت استفاده میکند و در صورتی که کاراکتری در ناحیه مکمل supplementary باشد، از چهار بایت استفاده خواهد کرد. UTF-16 از دو بایت برای نمایش کاراکترهای BMP و از 4 بایت برای نمایش کاراکترهای مکمل استفاده میکند و در UTF-32 از 4 بایت برای همه کاراکترها یا کد پوینتها استفاده میشود.
درحال حاضر، باتوجه به خرده نداشتن مقادیر پولی در ایران، عموما از نوعهای int و bigint برای ذخیره سازی این مقادیر استفاده میشود؛ اما در آینده با احتمال حذف تعدادی از صفرها، نیاز به ثبت خردهها هم ضروری خواهد بود و در اینجا این سؤال مهم مطرح میشود که نوع دادهای مناسب برای انجام اینکار چیست؟ برای نمونه در SQL Server، نوعهای دادهای decimal، money، smallmoney و امثال آن وجود دارند که در این مطلب، تفاوتهای مهم آنها و روش صحیح انتخاب نوع دادهای مناسب مخصوص اینکار را بررسی خواهیم کرد.
مشکل مهم نوع دادهای int جهت ذخیره سازی مقادیر پولی
فرض کنید جدول سادهای را با دو فیلد Id و Price دارید که نوع مبلغ آنرا با توجه به عدم داشتن خرده در واحد پولی، int انتخاب کردهاید:
اگر در این جدول فقط 7 رکورد زیر را ثبت کنیم:
به نظر شما خروجی کوئری سادهی زیر که جهت نمایش جمع مبالغ وارد شده تهیه شده، چیست؟
خروجی آن فقط استثنای زیر است!
عنوان میکند که جمع آن از بازهی اعداد صحیح خارج شدهاست و در سیستمی که نوع مبالغ آنرا int انتخاب کردهاید، دیر یا زود به این مشکل خواهید رسید. فقط کافی است کاربران، یکسالی با آن برنامه کار کنند!
برای حل این مشکل میتوان به صورت موقت، نوع دادهای را به bigint تبدیل کرد و مجددا جمع رکوردها را محاسبه کرد:
یک روش دیگر مواجه شدن با این مساله، عدم انتخاب نوع int برای فیلد Price، از ابتدای کار است.
از نوع دادهای float برای ذخیره سازی مقادیر پولی استفاده نکنید!
هیچگاه نباید از نوع دادهی float برای ذخیره سازی مقادیر پولی استفاده کرد؛ از این جهت که این نوع اعداد، به صورت تقریبی از یک مقدار decimal و به صورت باینری در SQL Server ذخیره میشوند. به همین جهت به محض ذخیره شدن، با عددی غیر دقیق مواجه خواهیم بود. همچنین مقایسهی دقیق این نوع اعداد هم مشکلات خاصی را به همراه دارد.
SQL Server چگونه مقادیر پولی money و small money را ذخیره میکند؟
SQL Server برای کار با مقادیر پولی، دو نوع MONEY و SMALLMONEY را ارائه میدهد که شبیه به نوعهای BIGINT و INT، نیاز به 8 و 4 بایت برای ذخیره سازی دارند. در عمل نوع MONEY شبیه به نوع DECIMAL(19,4) و نوع SMALLMONEY همانند DECIMAL(10,4) رفتار میکند. یعنی نوع MONEY میتواند تا 15 رقم دسیمال پیش از ممیز و 4 رقم اعشار را ذخیره کند و نوع SMALLMONEY تنها میتواند 6 رقم دسیمال و 4 رقم اعشاری را ذخیره کند.
اما ... هرچند نوع دادهی MONEY و DECIMAL(19,4) به ظاهر یکی هستند، اما به نحو متفاوتی بر روی دیسک سخت ذخیره میشوند. برای نمونه فرض کنید که قصد داریم عدد 4,513.19 را یکبار به صورت MONEY و بار دیگر به صورت SMALLMONEY ذخیره کنیم که در نهایت به جدول زیر میرسیم:
همانطور که مشاهده میکنید، نوعهای MONEY و SMALLMONEY، دقیقا همانند BIGINT هشت بایتی و INT، چهار بایتی ذخیره میشوند و عملا در پشت صحنهی SQL Server، اعداد صحیح هستند. اما نوع DECIMAL(19,4) که هرچند شبیه به MONEY عمل میکند، 9 بایتی است.
الگوریتم انتخاب نوع دادهی مناسب ذخیره سازی مقادیر پولی
در فلوچارت زیر که از کتاب «Donald Knuth’s "The Art of Computer Programming – Volume 1".» انتخاب شده، روش مواجه شدن با انواع و اقسام نوعهای دادهای عددی را به خوبی مشخص میکند که آیا عدد در حال ذخیره شدن، خرده دارد یا خیر؟ آیا از 922,337,203,685,477.5807 کوچکتر است یا خیر و امثال آن که در تصمیمگیری نهایی مؤثر هستند:
اعدادی را که در این نمودار مشاهده میکنید، در جدول زیر بهتر توضیح داده شدهاند. به عبارتی چه تفاوتی بین نوع Money و Decimal(19,4) مشابه وجود دارد:
تفاوت مهم نوع Money و Decimal(19,4)، در دقت آنها است
تا اینجا به نظر آنچنان تفاوتی بین نوع Money و Decimal(19,4) وجود ندارد و نوع money اتفاقا یک بایت را کمتر اشغال میکند و کوچکتر است. اما تفاوت اصلی را با مثال زیر بهتر میتوان توضیح داد:
در اینجا جدولی تهیه شده که دو ستون اصلی Mon1 و Dec1 را دارد و مابقی ستونهای آن، محاسباتی هستند:
همانطور که مشاهده میکنید، با ضرب دو عدد دسیمال، مقادیر پیش و پس از ممیز، یعنی precision و scale تغییر کردهاند، اما در مورد money چنین چیزی رخ نداده و ثابت است. برای مثال زمانیکه با یک عدد DECIMAL(4,2) کار میکنیم، اگر آنرا ضربدر همین عدد کنیم، به یک عدد DECIMAL(8,4) خواهیم رسید که البته حداکثر precision ممکن آن در SQL Server عدد 38 است، اما یک چنین تغییری در حین ضرب اعداد از نوع money رخ نمیدهد.
موضوع دقت را با مثال زیر بهتر میتوان بررسی کرد:
فرض کنید جدولی را داریم با دو فیلد از نوع Money و مشابه آن یعنی decimal(19,4) به صورت فوق. اگر رکوردهای زیر را به آن اضافه کنیم:
و سپس سعی کنیم که جمع اعداد وارد شده را محاسبه کنیم:
به نتیجهی زیر میرسیم:
همانطور که مشخص است در حین محاسباتی مانند جمع و منها و محاسبهی sum، تفاوتی بین این نوعها نیست. اما اگر سعی در تقسیم آنها کنیم:
به خروجی زیر میرسیم:
نتیجهی واقعی 0,00009 است که پس از گرد شدن، به 0.0001 مقدار دسیمال میرسیم، اما این دقت در نوع money از دست رفتهاست.
نکتهی مهمی که در اینجا قابل مشاهدهاست، محدود نبودن نتیجهی حاصل، به دقت اعشارها در عدد decimal تعریف شده و scale تعریف شدهی اولیهی آن است. نمونهی دیگر آنرا در مثال زیر میتوانید مشاهده کنید که هرچند عدد دسیمال تعریف شده، فقط 2 رقم اعشاری دارد، اما در حین تقسیم، از این مساله صرفنظر شده و خروجی آن محدود به 2 رقم اعشار نیست؛ برخلاف نوع money که حداکثر 4 رقم ثابت اعشاری را بیشتر نمیتواند داشته باشد:
نتیجهگیری
برای ذخیره سازی مقادیر پولی در SQL Server، اگر سیستم شما OLTP-like است و با اعدادی مانند 1000.24 کار میکنید و حداکثر میخواهید جمع و منهای آنها را محاسبه کنید، انتخاب نوع MONEY و یا SMALLMONEY بسیار مناسب است؛ اما اگر سیستم شما OLAP-like است و در آن اعمال ضرب و تقسیم زیاد رخ میدهد، فقط از نوع Decimal استفاده کنید.
مشکل مهم نوع دادهای int جهت ذخیره سازی مقادیر پولی
فرض کنید جدول سادهای را با دو فیلد Id و Price دارید که نوع مبلغ آنرا با توجه به عدم داشتن خرده در واحد پولی، int انتخاب کردهاید:
CREATE TABLE [Test1]( [Id] [int] IDENTITY(1,1) NOT NULL, [Price] [int] NOT NULL, CONSTRAINT [PK_Test1] PRIMARY KEY CLUSTERED ( [Id] ASC ));
Insert into Test1 values (1000000000),(1000000000),(1000000000),(1000000000),(1000000000),(1000000000),(1000000000)
select sum(price) from Test1
Arithmetic overflow error converting expression to data type int.
برای حل این مشکل میتوان به صورت موقت، نوع دادهای را به bigint تبدیل کرد و مجددا جمع رکوردها را محاسبه کرد:
select sum(cast(price as bigint)) from Test1
از نوع دادهای float برای ذخیره سازی مقادیر پولی استفاده نکنید!
هیچگاه نباید از نوع دادهی float برای ذخیره سازی مقادیر پولی استفاده کرد؛ از این جهت که این نوع اعداد، به صورت تقریبی از یک مقدار decimal و به صورت باینری در SQL Server ذخیره میشوند. به همین جهت به محض ذخیره شدن، با عددی غیر دقیق مواجه خواهیم بود. همچنین مقایسهی دقیق این نوع اعداد هم مشکلات خاصی را به همراه دارد.
DECLARE @f AS FLOAT = '29545428.0211111'; SELECT CAST(@f AS NUMERIC(28, 14)) AS value;
SQL Server چگونه مقادیر پولی money و small money را ذخیره میکند؟
SQL Server برای کار با مقادیر پولی، دو نوع MONEY و SMALLMONEY را ارائه میدهد که شبیه به نوعهای BIGINT و INT، نیاز به 8 و 4 بایت برای ذخیره سازی دارند. در عمل نوع MONEY شبیه به نوع DECIMAL(19,4) و نوع SMALLMONEY همانند DECIMAL(10,4) رفتار میکند. یعنی نوع MONEY میتواند تا 15 رقم دسیمال پیش از ممیز و 4 رقم اعشار را ذخیره کند و نوع SMALLMONEY تنها میتواند 6 رقم دسیمال و 4 رقم اعشاری را ذخیره کند.
اما ... هرچند نوع دادهی MONEY و DECIMAL(19,4) به ظاهر یکی هستند، اما به نحو متفاوتی بر روی دیسک سخت ذخیره میشوند. برای نمونه فرض کنید که قصد داریم عدد 4,513.19 را یکبار به صورت MONEY و بار دیگر به صورت SMALLMONEY ذخیره کنیم که در نهایت به جدول زیر میرسیم:
همانطور که مشاهده میکنید، نوعهای MONEY و SMALLMONEY، دقیقا همانند BIGINT هشت بایتی و INT، چهار بایتی ذخیره میشوند و عملا در پشت صحنهی SQL Server، اعداد صحیح هستند. اما نوع DECIMAL(19,4) که هرچند شبیه به MONEY عمل میکند، 9 بایتی است.
الگوریتم انتخاب نوع دادهی مناسب ذخیره سازی مقادیر پولی
در فلوچارت زیر که از کتاب «Donald Knuth’s "The Art of Computer Programming – Volume 1".» انتخاب شده، روش مواجه شدن با انواع و اقسام نوعهای دادهای عددی را به خوبی مشخص میکند که آیا عدد در حال ذخیره شدن، خرده دارد یا خیر؟ آیا از 922,337,203,685,477.5807 کوچکتر است یا خیر و امثال آن که در تصمیمگیری نهایی مؤثر هستند:
اعدادی را که در این نمودار مشاهده میکنید، در جدول زیر بهتر توضیح داده شدهاند. به عبارتی چه تفاوتی بین نوع Money و Decimal(19,4) مشابه وجود دارد:
تفاوت مهم نوع Money و Decimal(19,4)، در دقت آنها است
تا اینجا به نظر آنچنان تفاوتی بین نوع Money و Decimal(19,4) وجود ندارد و نوع money اتفاقا یک بایت را کمتر اشغال میکند و کوچکتر است. اما تفاوت اصلی را با مثال زیر بهتر میتوان توضیح داد:
CREATE TABLE MoneyTest ( Mon1 money, Mon2 AS Mon1*Mon1, Mon3 AS Mon1*Mon1*Mon1, Dec1 decimal(19,4), Dec2 AS Dec1*Dec1, Dec3 AS Dec1*Dec1*Dec1, MonDec AS Mon1*Dec1, DecMon AS Dec1*Mon1);
همانطور که مشاهده میکنید، با ضرب دو عدد دسیمال، مقادیر پیش و پس از ممیز، یعنی precision و scale تغییر کردهاند، اما در مورد money چنین چیزی رخ نداده و ثابت است. برای مثال زمانیکه با یک عدد DECIMAL(4,2) کار میکنیم، اگر آنرا ضربدر همین عدد کنیم، به یک عدد DECIMAL(8,4) خواهیم رسید که البته حداکثر precision ممکن آن در SQL Server عدد 38 است، اما یک چنین تغییری در حین ضرب اعداد از نوع money رخ نمیدهد.
موضوع دقت را با مثال زیر بهتر میتوان بررسی کرد:
CREATE TABLE [MoneyTest]( [Id] [int] IDENTITY(1,1) NOT NULL, decimalMoney decimal(19,4), moneyMoney money CONSTRAINT [PK_MoneyTest] PRIMARY KEY CLUSTERED ( [Id] ASC ));
INSERT INTO MoneyTest VALUES (12321423442.3456,12321423442.3456), (1111111.1919,1111111.1919)
SELECT * FROM MoneyTest SELECT SUM(decimalMoney) AS [sumDecimal], SUM(moneyMoney) AS [sumMoney] FROM MoneyTest
همانطور که مشخص است در حین محاسباتی مانند جمع و منها و محاسبهی sum، تفاوتی بین این نوعها نیست. اما اگر سعی در تقسیم آنها کنیم:
DECLARE @moneyPer money, @decimalPer decimal(19,4) SET @moneyPer = (SELECT moneyMoney FROM MoneyTest WHERE id = 2)/((SELECT moneyMoney FROM MoneyTest WHERE id = 1)) SET @decimalPer = (SELECT decimalMoney FROM MoneyTest WHERE id = 2)/((SELECT decimalMoney FROM MoneyTest WHERE id = 1)) SELECT @moneyPer AS[moneyPer], @decimalPer AS [decimalPer];
نتیجهی واقعی 0,00009 است که پس از گرد شدن، به 0.0001 مقدار دسیمال میرسیم، اما این دقت در نوع money از دست رفتهاست.
نکتهی مهمی که در اینجا قابل مشاهدهاست، محدود نبودن نتیجهی حاصل، به دقت اعشارها در عدد decimal تعریف شده و scale تعریف شدهی اولیهی آن است. نمونهی دیگر آنرا در مثال زیر میتوانید مشاهده کنید که هرچند عدد دسیمال تعریف شده، فقط 2 رقم اعشاری دارد، اما در حین تقسیم، از این مساله صرفنظر شده و خروجی آن محدود به 2 رقم اعشار نیست؛ برخلاف نوع money که حداکثر 4 رقم ثابت اعشاری را بیشتر نمیتواند داشته باشد:
DECLARE @M MONEY = 1234, @D DECIMAL(6,2) = 1234 SELECT @M/$1000000 AS [MONEY] , @D/$1000000 AS [DECIMAL]
نتیجهگیری
برای ذخیره سازی مقادیر پولی در SQL Server، اگر سیستم شما OLTP-like است و با اعدادی مانند 1000.24 کار میکنید و حداکثر میخواهید جمع و منهای آنها را محاسبه کنید، انتخاب نوع MONEY و یا SMALLMONEY بسیار مناسب است؛ اما اگر سیستم شما OLAP-like است و در آن اعمال ضرب و تقسیم زیاد رخ میدهد، فقط از نوع Decimal استفاده کنید.
DECLARE @dOne DECIMAL(19,4) = 1, @dThree DECIMAL(19,4) = 3, @mOne MONEY = 1, @mThree MONEY = 3 SELECT (@dOne/@dThree) * @dThree AS DecimalResult, (@mOne/@mThree) * @mThree AS MoneyResult
نظرات مطالب
تاریخ شمسی برای blogger !
قالبهای غیر رسمی هم اگر سلکتورهای بلاگر را حفظ کرده باشند، بله کار میکند.
برای مثال اگر یک قالب غیر رسمی در تگ h2 کلاس استاندارد date-header را حفظ کرده، بله کار میکند.
در کل اگر میخواهید تست کنید که این روش روی سایت شما هم جواب میده یا نه، اسکریپت گریس مانکی زیر را نصب کنید:
https://www.dntips.ir/2008/12/blog-post_19.html
برای مثال اگر یک قالب غیر رسمی در تگ h2 کلاس استاندارد date-header را حفظ کرده، بله کار میکند.
در کل اگر میخواهید تست کنید که این روش روی سایت شما هم جواب میده یا نه، اسکریپت گریس مانکی زیر را نصب کنید:
https://www.dntips.ir/2008/12/blog-post_19.html
پاسخ به بازخوردهای پروژهها
فونت نستعلیق
- نیاز هست سورس iText رو پچ کرد (غیر رسمی است).
- پشتیبانی رسمی از GUSB در سال 2014 به مجموعه iText اضافه خواهد شد. نظر نویسنده اصلی iText در این مورد:
- پشتیبانی رسمی از GUSB در سال 2014 به مجموعه iText اضافه خواهد شد. نظر نویسنده اصلی iText در این مورد:
It's indeed not supported yet. It's on our roadmap for next year (2014). – Bruno Lowagie
مایکروسافت در تاریخ 20 دسامبر 2013 پیش نمایش نسخه جدید ASP.NET Identity را معرفی کرد. تمرکز اصلی در این انتشار، رفع مشکلات نسخه 1.0 بود. امکانات جدیدی هم مانند Account Confirmation و Password Reset اضافه شده اند.
دانلود این انتشار
و برای گرفتن یک وهله از UserManager:
ASP.NET Identity را میتوانید در قالب یک پکیج NuGet دریافت کنید. در پنجره Manage NuGet Packages میتوانید پکیجهای Preview را لیست کرده و گزینه مورد نظر را
نصب کنید. برای نصب پکیجهای pre-release توسط Package Manager Console از فرامین زیر استفاده کنید.
- Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.0.0-alpha1 -Pre
- Install-Package Microsoft.AspNet.Identity.Core -Version 2.0.0-alpha1 -Pre
- Install-Package Microsoft.AspNet.Identity.OWIN -Version 2.0.0-alpha1 -Pre
دقت کنید که حتما از گزینه "Include Prerelease" استفاده میکنید. برای اطلاعات بیشتر درباره نصب پکیجهای Pre-release لطفا به این لینک و یا این لینک مراجعه کنید.
در ادامه لیست امکانات جدید و مشکلات رفع شده را میخوانید.
Account Confirmation
سیستم ASP.NET Identity حالا از Account Confirmation پشتیبانی میکند. این یک سناریوی بسیار رایج است. در اکثر وب سایتهای امروزی پس از ثبت نام، حتما باید ایمیل خود را تایید کنید. پیش از تایید ثبت نام قادر به انجام هیچ کاری در وب سایت نخواهید بود، یعنی نمیتوانید Login کنید. این روش مفید است، چرا که از ایجاد حسابهای کاربری نامعتبر (bogus) جلوگیری میکند. همچنین این روش برای برقراری ارتباط با کاربران هم بسیار کارآمد است. از آدرسهای ایمیل کاربران میتوانید در وب سایتهای فروم، شبکههای اجتماعی، تجارت آنلاین و بانکداری برای اطلاع رسانی و دیگر موارد استفاده کنید.
نکته: برای ارسال ایمیل باید تنظیمات SMTP را پیکربندی کنید. مثلا میتوانید از سرویسهای ایمیل محبوبی مانند SendGrid استفاده کنید، که با Windows Azure براحتی یکپارچه میشود و از طرف توسعه دهنده اپلیکیشن هم نیاز به پیکربندی ندارد.
در مثال زیر نیاز دارید تا یک سرویس ایمیل برای ارسال ایمیلها پیکربندی کنید. همچنین کاربران پیش از تایید ایمیل شان قادر به بازنشانی کلمه عبور نیستند.
Password Reset
این هم یک سناریوی رایج و استاندارد است. کاربران در صورتی که کلمه عبورشان را فراموش کنند، میتوانند از این قابلیت برای بازنشانی آن استفاده کنند. کلمه عبور جدیدی بصورت خودکار تولید شده و برای آنها ارسال میشود. کاربران با استفاده از این رمز عبور جدید میتوانند وارد سایت شوند و سپس آن را تغییر دهند.
Security Token Provider
هنگامی که کاربران کلمه عبورشان را تغییر میدهند، یا اطلاعات امنیتی خود را بروز رسانی میکنند (مثلا حذف کردن لاگینهای خارجی مثل فیسبوک، گوگل و غیره) باید شناسه امنیتی (security token) کاربر را بازتولید کنیم و مقدار قبلی را Invalidate یا بی اعتبار سازیم. این کار بمنظور حصول اطمینان از بی اعتبار بودن تمام شناسههای قبلی است که توسط کلمه عبور پیشین تولید شده بودند. این قابلیت، یک لایه امنیتی بیشتر برای اپلیکیشن شما فراهم میکند. چرا که وقتی کاربری کلمه عبورش را تغییر بدهد از همه جا logged-out میشود. یعنی از تمام مرورگرهایی که برای استفاده از اپلیکیشن استفاده کرده خارج خواهد شد.
برای پیکربندی تنظیمات این قابلیت میتوانید از فایل Startup.Auth.cs استفاده کنید. میتوانید مشخص کنید که میان افزار OWIN cookie هر چند وقت یکبار باید شناسه امنیتی کاربران را بررسی کند. به لیست زیر دقت کنید.
// Enable the application to use a cookie to store information for the signed in user // and to use a cookie to temporarily store information about a user logging in with a third party login provider // Configure the sign in cookie app.UseCookieAuthentication(newCookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = newPathString("/Account/Login"), Provider = newCookieAuthenticationProvider { OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromSeconds(5), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } });
امکان سفارشی کردن کلیدهای اصلی Users و Roles
در نسخه 1.0 نوع فیلدهای کلید اصلی در جداول Users و Roles از نوع رشته (string) بود. این بدین معنا است که وقتی از Entity Framework و Sql Server برای ذخیره دادههای ASP.NET Identity استفاده میکنیم دادههای این فیلدها بعنوان nvarchar ذخیره میشوند. درباره این پیاده سازی پیش فرض در فروم هایی مانند سایت StackOverflow بسیار بحث شده است. و در آخر با در نظر گرفتن تمام بازخورد ها، تصمیم گرفته شد یک نقطه توسعه پذیری (extensibility) اضافه شود که توسط آن بتوان نوع فیلدهای اصلی را مشخص کرد. مثلا شاید بخواهید کلیدهای اصلی جداول Users و Roles از نوع int باشند. این نقطه توسعه پذیری مخصوصا هنگام مهاجرت دادههای قبلی بسیار مفید است، مثلا ممکن است دیتابیس قبلی فیلدهای UserId را با فرمت GUID ذخیره کرده باشد.
اگر نوع فیلدهای کلید اصلی را تغییر دهید، باید کلاسهای مورد نیاز برای Claims و Logins را هم اضافه کنید تا کلید اصلی معتبری دریافت کنند. قطعه کد زیر نمونه ای از نحوه استفاده این قابلیت برای تعریف کلیدهای int را نشان میدهد.
de Snippet publicclassApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim> { } publicclassCustomRole : IdentityRole<int, CustomUserRole> { public CustomRole() { } public CustomRole(string name) { Name = name; } } publicclassCustomUserRole : IdentityUserRole<int> { } publicclassCustomUserClaim : IdentityUserClaim<int> { } publicclassCustomUserLogin : IdentityUserLogin<int> { } publicclassApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim> { }
پشتیبانی از IQueryable روی Users و Roles
کلاسهای UserStore و RoleStore حالا از IQueryable پشتیبانی میکنند، بنابراین میتوانید براحتی لیست کاربران و نقشها را کوئری کنید.
بعنوان مثال قطعه کد زیر دریافت لیست کاربران را نشان میدهد. از همین روش برای دریافت لیست نقشها از RoleManager میتوانید استفاده کنید.
// // GET: /Users/ public async Task<ActionResult> Index() { return View(await UserManager.Users.ToListAsync()); }
پشتیبانی از عملیات Delete از طریق UserManager
در نسخه 1.0 اگر قصد حذف یک کاربر را داشتید، نمیتوانستید این کار را از طریق UserManager انجام دهید. اما حالا میتوانید مانند قطعه کد زیر عمل کنید.
var user = await UserManager.FindByIdAsync(id); if (user == null) { return HttpNotFound(); } var result = await UserManager.DeleteAsync(user);
میان افزار UserManagerFactory
شما میتوانید با استفاده از یک پیاده سازی Factory، وهله ای از UserManager را از OWIN context دریافت کنید. این الگو مشابه چیزی است که برای گرفتن AuthenticationManager در OWIN context استفاده میکنیم. این الگو همچنین روش توصیه شده برای گرفتن یک نمونه از UserManager به ازای هر درخواست در اپلیکیشن است.
قطعه کد زیر نحوه پیکربندی این میان افزار در فایل StartupAuth.cs را نشان میدهد.
// Configure the UserManager app.UseUserManagerFactory(newUserManagerOptions<ApplicationUserManager>() { DataProtectionProvider = app.GetDataProtectionProvider(), Provider = newUserManagerProvider<ApplicationUserManager>() { OnCreate = ApplicationUserManager.Create } });
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
میان افزار DbContextFactory
سیستم ASP.NET Identity از Entity Framework برای ذخیره داده هایش در Sql Server استفاده میکند. بدین منظور، ASP.NET Identity کلاس ApplicationDbContext را رفرنس میکند. میان افزار DbContextFactory به ازای هر درخواست در اپلیکیشن یک وهله از ApplicationDbContext را به شما تحویل میدهد.
می توانید پیکربندی لازم را در StartupAuth.cs انجام دهید.
app.UseDbContextFactory(ApplicationDbContext.Create);
Samples
امکانات جدید را میتوانید در پروژه https://aspnet.codeplex.com پیدا کنید. لطفا به پوشه Identity در سورس کد مراجعه کنید. برای اطلاعاتی درباره نحوه اجرای پروژه هم فایل readme را بخوانید.
برای مستندات ASP.NET Identity 1.0 هم به http://www.asp.net/identity سر بزنید. هنوز مستنداتی برای نسخه 2.0 منتشر نشده، اما بزودی با انتشار نسخه نهایی مستندات و مثالهای جدیدی به سایت اضافه خواهند شد.
Known Issues
در کنار قابلیتهای جدیدی مانند Account Confirmation و Password Reset، دو خاصیت جدید به کلاس IdentityUser اضافه شده اند: 'Email' و 'IsConfirmed'. این تغییرات الگوی دیتابیسی که توسط ASP.NET Identity 1.0 ساخته شده است را تغییر میدهد. بروز رسانی پکیجها از نسخه 1.0 به 2.0 باعث میشود که اپلیکیشن شما دیگر قادر به دسترسی به دیتابیس عضویت نباشد، چرا که مدل دیتابیس تغییر کرده. برای بروز رسانی الگوی دیتابیس میتوانید از Code First Migrations استفاده کنید.
نکته: نسخه جدید به EntityFramework 6.1.0-alpha1 وابستگی دارد، که در همین تاریخ (20 دسامبر 2013) پیش نمایش شد. http://blogs.msdn.com/b/adonet/archive/2013/12/20/ef-6-1-alpha-1-available.aspx
EntityFramework 6.1.0-alpha1 بروز رسانی هایی دارد که سناریوی مهاجرت در ASP.NET Identity را تسهیل میکند، به همین دلیل از نسخه جدید EF استفاده شده. تیم ASP.NET هنوز باگهای زیادی را باید رفع کند و قابلیتهای جدیدی را هم باید پیاده سازی کند. بنابراین پیش از نسخه نهایی RTM شاهد پیش نمایشهای دیگری هم خواهیم بود که در ماههای آتی منتشر میشوند.
برای اطلاعات بیشتر درباره آینده ASP.NET Identity به لینک زیر سری بزنید.