کتابخانه fuzzyDropdown
کتابخانه jquery.xmlTweet
افزونه OneNote Clipper 2.0
مجله MSDN شماره March 2015 منتشر شد
JQuery Plugins #2
فضای نام
(function( $ ){ $.fn.tooltip = function( options ) { // این }; $.fn.tooltipShow = function( ) { // تعریف }; $.fn.tooltipHide = function( ) { // بد است }; $.fn.tooltipUpdate = function( content ) { // ! }; })( jQuery );
(function( $ ){ var methods = { init : function( options ) { // این }, show : function( ) { // تعریف }, hide : function( ) { // خوب است }, update : function( content ) { // ! } }; $.fn.tooltip = function( method ) { // منطق تابع را از اینجا صدا زده ایم if ( methods[method] ) { return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { return methods.init.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' ); } }; })( jQuery );
// تابع init صدا زده میشود $('div').tooltip();
// تابع update با پارامتر صدا زده میشود $('div').tooltip('update', 'This is the new tooltip content!');
(function( $ ){ var methods = { init : function( options ) { return this.each(function(){ $(window).bind('resize.tooltip', methods.reposition); }); }, destroy : function( ) { return this.each(function(){ $(window).unbind('.tooltip'); }) }, reposition : function( ) { // ... }, show : function( ) { // ... }, hide : function( ) { // ... }, update : function( content ) { // ... } }; $.fn.tooltip = function( method ) { if ( methods[method] ) { return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { return methods.init.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' ); } }; })( jQuery );
$('#fun').tooltip(); // مدتی بعد... $('#fun').tooltip('destroy');
شناسه ها و استفاده از Let
Basic Literal
جدول بالا کاملا واضح است و برنامه نویسان دات نت نظیر #C با انواع داده ای بالا آشنایی دارند. فقط در مورد گزینه آخر unit در فصلهای بعدی توضیح خواهم داد.
Arithmetic Operators (عملگرهای محاسباتی)
Simple String (کار با نوع داده رشته ای)
بعد از بررسی موارد بالا حالا به معرفی شناسهها میپردازم. شناسهها در #F راهی هستند برای اینکه شما به مقادیر نام اختصاص دهید. برای اختصاص نام به مقادیر کافیست از کلمه کلیدی let به همراه یک نام و علامت = و یک عبارت استفاده کنید. چیزی شبیه به تعریف متغیر در سایر زبانها نظیر #C. دلیل اینکه در #F به جای واژه متغیر از شناسه استفاده میشود این است که شما میتوانید به یک شناسه تابعی را نیز اختصاص دهید و مقدار شناسهها دیگر قابل تغییر نیست. در #F کلمه متغیر یک واژه نادرست است چون زمانی که شما یه یک متغیر مقدار اختصاص میدهید، مقدار اون متغیر دیگه قایل تغییر نیست. برای همین اکثر برنامه نویسان #F به جای استفاده از واژه متغیر از واژه مقدار یا شناسه استفاه میکنند. برای همین از واژه متغیر برای نام گذاری استفاده نمیشود. (البته در #F در بعضی مواقع ما شناسهها رو دوباره تعریف میکنیم که چیزی شبیه به استفاده از متغیر هاست ولی با اندکی تفاوت. در این فصل تمرکز ما بر روی شناسه هایی است که مقدارشان تغییر نمیکند ولی در فصل برنامه نویسی دستوری به تفصیل در این باره توضیح داده شده است)
let x = 42
let myAdd = fun x y -> x + y
let raisePowerTwo x = x ** 2.0
نام گذاری شناسه ها
برای نام گذاری شناسهها نام انتخابی یا باید با Underscore شروع شود یا با حروف. بعد از آن میتونید از اعداد هم استفاده کنید.(نظیر سایر زبانهای برنامه نویسی)
#F از unicode هم پشتیبانی میکنه یعنی میتونید متغیری به صورت زیر رو تعریف کنید.
let مسعود = ""
let ``more? `` = true
let ``class`` = "style"
محدوده تعریف شناسه ها
به دلیل اینکه در #F از {} به عنوان شروع و اتمام محدوده استفاده نمیشود دونستن و شناختن محدوده توابع بسیار مهم و ضروری است. چون اگر از شناسه ای که در یک محدوده در دسترس نباشد استفاده کنید با خطای کامپایلر متوقف خواهید شد.
همون بحث متغیرهای محلی و سراسری (در سایر زبان ها) در این جا نیز صادق است یعنی در #F شناسههای سراسری و محلی خواهیم داشت. تمام شناسه ها، چه اون هایی که در توابع استفاده میشوند و چه اونهایی که به مقادیر اشاره میکنند محدودشون از نقطه ای که تعریف میشوند تا جایی که اتمام استفاده از اونهاست تعریف شده است. برای مثال اگر یک شناسه رو در بالای فایل تعریف کنید که یک مقدار دارد تا پایان SourceFile قابل استفاده است.( به دلیل نبود مفهوم کلاس از واژه sourceFile استفاده کردم). هم چنین شناسه هایی که در توابع تعریف میشوند فقط در همون توابع قابل استفاده هستند.
حالا سوال این است که با نبودن {} چگونه محدوده خود توابع مشخص میشود؟
در #F با استفاده از فضای خای یا space محدوده شناسهها و توابع رو مشخص میکنیم. برای روشن شدن مطلب به مثال زیر دقت کنید.
let test a b = let dif = b - a let mid = dif / 2 mid + a printfn "(test 5 11) = %i" (test 5 11) printfn "(test 11 5) = %i" (test 11 5)
نکته مهم: به جای استفاده از فضای خالی(space) نمیتونید از TAB استفاده کنید.
LIGHTWEIGHT SYNTAX یا VERBOSE SYNTAX
در #F دو نوع سبک کد نویسی وجود دارد. یکی lightweight و دیگری Verbose. البته اکثر برنامه نویسان از سبک lightweight که به صورت پیش فرض در #F تعبیه شده است استفاده میکنند ولی آشنایی با سبک verbose نیز به عنوان برنامه نویس #F ضروری است. ما نیز به تبعیت از سایرین از سبک lightweight استفاده خواهیم کرد ولی یک فصل به عنوان مطالب تکمیلی اختصاص دادم که تفاوت این دو سبک را در طی چندین مثال بیان میکند.
همان طور که قبلا بیان شد #F بر اساس زبان OCaml پیاده سازی شده است. زبان OCaml مانند #F، یک زبان LIGHTWEIGHT SYNTAX نیست. LIGHTWEIGHT SYNTAX بدین معنی است محدوده شناسهها بر اساس فضای خالی بین اونها مشخص میشود نه با ;. (البته استفاده از ; به صورت اختیاری است)
بازنویسی مثال بالا
let halfWay a b = let dif = b - a in let mid = dif / 2 in mid + a
#light "off"
-
تنها دریافت رکوردهای مورد نیاز
string city = "New York"; List<School> schools = db.Schools.ToList(); List<School> newYorkSchools = schools.Where(s => s.City == city).ToList();
در کد بالا ابتدا کلیه ردیفهای جدول از دیتابیس به حافظه منتقل میشود و سپس برروی آنها کوئری مورد نظر اعمال میگردد که بشدت میتواند برای یک برنامه - خصوصا برنامه وب - بهدلیل دریافت کلیهی ردیفهای جدول بسیار مخرب باشد. کوئری فوق را میتوان به صورت زیر اصلاح کرد:
List<School> newYorkSchools = db.Schools.Where(s => s.City == city).ToList(); یا IQueryable<School> schools = db.Schools; List<School> newYorkSchools = schools.Where(s => s.City == city).ToList();
-
حداقل رفت و برگشت به دیتابیس
کد زیر را در نظر بگیرید:
string city = "New York"; List<School> schools = db.Schools.Where(s => s.City == city).ToList(); var sb = new StringBuilder(); foreach(var school in schools) { sb.Append(school.Name); sb.Append(": "); sb.Append(school.Pupils.Count); sb.Append(Environment.NewLine); }
هدف تکه کد بالا این است که تعداد دانش آموزان مدرسههای واقع در شهر New York را بدست آورد.
توجه داشته باشید:
- یک مدرسه میتواند چندین دانش آموز داشته باشد (وجود رابطه یک به چند)
- LazyLoading فعال است
- تعداد مدرسههای شهر نیویورک 200 عدد میباشد
اگر کوئری بالا را بهوسیلهی یک پروفایلر بررسی نمایید، متوجه خواهید شد 1 + 200 رفت و برگشت به دیتابیس صورت گرفته است که به "N+1 select problem" معروف است. 1 مرتبه جهت دریافت لیست مدرسههای شهر نیویورک و 200 مرتبه جهت دریافت تعداد دانش آموزان هر مدرسه.
بدلیل فعال بودن Lazy Loading، زمانیکه موجودیتی فراخوانی میشود، سایر موجودیتهای وابسته به آن، زمانی از دیتابیس فراخوانی خواهند شد که به آنها دسترسی پیدا کنید. در حلقهی foreach هم به ازای هر مدرسه (200 مدرسه) شهر نیویورک یک رفت و برگشت انجام میشود.
اما راه حل در این مورد خاص استفاده از Eager Loading است. خط دوم کد را بصورت زیر تغییر دهید:
List<School> schools = db.Schools .Where(s => s.City == city) .Include(x => x.Pupils) .ToList();
حال با یک رفت و برگشت، همراه هر مدرسه اطلاعات مربوط به دانش آموزان وابستهی آن نیز در دسترس خواهد بود.
-
تنها استفاده از ستونهای مورد نیاز
فرض کنید قصد دارید نام و نام خانوادگی دانش آموزان یک مدرسه را بدست آورید.
int schoolId = 1; List<Pupil> pupils = db.Pupils .Where(p => p.SchoolId == schoolId) .ToList(); foreach(var pupil in pupils) { textBox_Output.Text += pupil.FirstName + " " + pupil.LastName; textBox_Output.Text += Environment.NewLine; }
- انتقال اطلاعات بلا استفاده که ممکن است باعث کاهش کارآیی Sql Server I/O و شبکه و اشغال حافظهی کلاینت گردد.
- کاهش کارآیی ایندکس گذاری. فرض کنید برروی جدول دانش آموزان ایندکسی شامل 2 ستون نام و نام خانوادگی تعریف کردهاید. با انتخاب تمام ستونهای جدول توسط خط دوم (select * from...) به کارآیی ایندکس گذاری برروی این جدول آسیب زدهاید. توضیح بیشتر در اینجا مطرح شده است.
اما راه حل:
var pupils = db.Pupils .Where(p => p.SchoolId == schoolId) .Select(x => new { x.FirstName, x.LastName }) .ToList();
-
عدم تطابق نوع ستون با نوع خصیصه مدل
فرض کنید نوع ستون جدول دانش آموزان (VARCHAR(20 است و خصیصه کدپستی مدل دانش آموز مانند زیر تعریف شده است:
public string PostalZipCode { get; set; }
انتخاب نوع داده و تطابق نوع داده مدل با ستون جدول دارای اهمیت زیادی است و در صورت عدم رعایت، باعث کاهش کارآیی شدید میگردد. در کد زیر قصد دارید لیست نام و نام خانوادگی دانش آموزانی را که کدپستی آنها 90210 میباشد، بدست بیاورید.
string zipCode = "90210"; var pupils = db.Pupils .Where(p => p.PostalZipCode == zipCode) .Select(x => new {x.FirstName, x.LastName}) .ToList();
هنگامیکه کوئری بالا را اجرا نمایید، زمان زیادی جهت اجرای آن صرف خواهد شد. در صورتی که از یک پروفایلر استفاده نمایید، میتوانید عملیات پرهزینه را شناسایی نمایید و اقدام به کاهش هزینهها کنید.
همانطور که در شکل بالا مشخص است عملیات index scan از سایر عملیاتها پرهزینهتر است. حال به بررسی علت بهوجود آمدن این عملیات پرهزینه خواهیم پرداخت.
Index Scan زمانی رخ میدهد که اس کیو ال سرور مجبور است هر صفحهی از ایندکس را بخواند و شرایط را (کدپستی برابر 90210) اعمال نماید و نتیجه را برگرداند. Index Scan بسیار هزینه بر است، چون اس کیو ال سرور، کل ایندکس را بررسی مینماید. نقطهی مقابل و بهینهی آن، Index Seek است که اس کیو ال سرور به صفحهی مورد نظر ایندکسی که به شرایط نزدیکتر است، منتقل میگردد.
خب چرا اس کیو ال سرور Index Scan را بجای Index Seek انتخاب کرده است؟!
اشکالی در قسمت سمت چپ شکل بالا که به رنگ قرمز نمایش داده شده است، وجود دارد:
Type conversion: Seek Plan for CONVERT_IMPLICIT(nvarchar(20), [Extent1].[PostalZipCode],0)=[@p__linq__0]
پارامتر کوئری تولید شدهی توسط EF از نوع NVARCHAR است و تبدیل نوع NVARCHAR پارامتر کدپستی، که محدودهی اطلاعات بیشتری (Unicode Strings) را نسبت به نوع VARCHAR ستون دارد، بهدلیل از دست رفتن اطلاعات امکان پذیر نیست. بههمین جهت برای مقایسهی پارامتر کدپستی با ستون VARCHAR ، اس کیو ال سرور باید هر ردیف ایندکس را از VARCHAR به NVARCHAR تبدیل نماید که منجر به Index Scan میشود. اما راه حل بسیار ساده این است که فقط نوع خصیصه را با ستون جدول یکسان کنید.
[Column(TypeName = "varchar")] public string PostalZipCode { get; set; }
من بلدم با set identity_insert table_name on/off کاری کنم که خودم دستی مقداری را برای خصیصه identity لحاظ کنم. ولی متاسفانه نتونستم مقدار یک ستون با خصیصه Identity رو بروز رسانی (یا همون update) کنم. لطفا بهم بگید که اصلا این کار ممکنه یا من بلد نیستم. البته براساس query زیر بمن SQL Server گفته که نمیشه این ستون را update کرد که ظاهرا هم همین طور(ستون id همانطور که در پیام آمده از نوع identity هست)
update t set id = new_id from (select id, row_number() over(order by id) new_id from #temp)t --Cannot update identity column 'id'.
اصلا اجازه بدین یه جور دیگه سوال رو مطرح کنم من نیاز دارم تمام مقادیر identity رو بروز رسانی کنم تا کاملا پشت سر هم و متوالی بشن این کار را میتونم با یک تابع row_number و یک derived table انجام بدم (اگر بذارن!) همانطور که قبلا نشان دادم، یا با روش زیر این کار را بکنم که البته اجرا نمیشه به این دلیل که در یک جدول نمیشه دو identity property داشت. با فرض اجرا شدن دستور select into باز هم در دستور update با مشکل بر میخوردیم (چون نمیشه ستون id را بروز رسانی کرد)
select id, identity(int, 1,1) new_id into #temptable from #temp order by id asc /* cannot add identity column, using the SELECT INTO statement, to table '#temptable', which already has column 'id' that inherits the identity property. */ update t set id = new_id from #temp t join #temptable d on t.id = d.id;
declare @t table(id int) insert into @t select id from #temp delete from #temp set identity_insert #temp on insert #temp (id) select row_number() over(order by id) from @t set identity_insert #temp off
من قصد ندارم صورت مساله نقد و بررسی بشه و اصولی بودن یا صحیح بودنش مورد ارزیابی قرار بگیره فقط برام این یک سوال شده.
مساله عمومی که راجب این ستون وجود داره استفاده کردن از Gapهای حاصل شده در این ستون برای درجهای بعدی است. که query آن نیز بسیار ساده و در دسترس است.
آیا شما میدانید که چگونه این مشکل با sequence ای که در نسخه 2012 معرفی شده است حل میشود؟
A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
سؤال: first chance exception چیست؟
وقتی استثنایی در یک برنامه رخ میدهد، به آن یک first chance exception میگویند. این اولین شانسی است که سیستم به شما میدهد تا استثنای رخ داده را مدیریت کنید. اگر کدهای برنامه یا ابزاری (یک try/catch یا دیباگر) این اولین شانس را ندید بگیرند، یک second chance exception رخ میدهد. اینجا است که برنامه به احتمال زیاد خاتمه خواهد یافت.
مشاهدهی پیامهای A first chance exception در پنجرهی output ویژوال استودیو به این معنا است که استثنایی رخ داده، اما توسط یک استثناءگردان مدیریت شدهاست. بنابراین در اکثر موارد، موضوع خاصی نیست و میتوان از آن صرفنظر کرد.
سؤال: چگونه میتوان منشاء اصلی پیام رخدادن یک first chance exception را یافت؟
ویژوال استودیو در پنجرهی output، مدام پیام رخدادن first chance exception را نمایش میدهد؛ اما واقعا کدام قطعه از کدهای برنامه سبب بروز آن شدهاند؟ به صورت پیش فرض صفحهی نمایش استثناءها در VS.NET زمانی نمایان میشود که استثنای رخ داده، مدیریت نشده باشد. برای فعال سازی نمایش استثناهای مدیریت شده باید تنظیمات ذیل را اعمال کرد:
- به منوی Debug | Exceptions مراجعه کنید.
- گره Common Language Runtime Exceptions را باز کنید.
- سپس گروه System آنرا نیز باز کنید.
- در اینجا بر اساس نوع استثنایی که در پنجرهی output نمایش داده میشود، آن استثناء را یافته و Thrown آنرا انتخاب کنید.
اینبار اگر برنامه را اجرا کنید، دقیقا محلی که سبب بروز استثنای ArgumentOutOfRangeException شده در VS.NET گزارش داده خواهد شد.