‫داستان بی‌سوادی ما – ۵: SQL Server Profiler‫

یکی از ابزارهایی که هم برای خطایابی خیلی استفاده می‌شود و هم برای پیدا کردن مشکلات سرعت Queryهای دیتابیسی، ابزار SQL Server Profiler است. من هم از این ابزار خیلی استفاده کرده و می‌کنم. اما مشکل من با این ابزار حجم زیاد log تولیدی آن است. گاهی اوقات برای دیدن یک Query خاص باید چند صد رکورد را بالا و پایین کنم تا به رکورد مورد نظرم برسم. هر چند که این مشکل خیلی آزار دهنده بود ولی چون چاره‌ی دیگری نداشتم مجبور بودم همچنان از Profiler استفاده کنم.

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

نمی‌دانم این بی‌سوادی ناقابل تا حالا چند صد ساعت وقتم را تلف کرده است. اما به هر حال نحوه‌ی فیلتر کردن اطلاعات بسیار ساده بوده و کافی بود تا از طریق برگه‌ی Event Selection، اطلاعات را بر حسب «نام کاربری» و… فیلتر کنم. برای کسب اطلاعات بیشتر به اصل مطلب در وبلاگ وحید مراجعه کنید.

این مطلب جزیی از سری نوشته‌های «داستان بی‌سوادی ما» است.

پروژه‌های جاوایی در دات‌نت

تعدادی زیادی از پروژه‌های کد باز معروف ‭.Net‬ از روی پروژه‌های موفق Java ساخته شده‌اند. یکی از قشنگ‌ترین چیزهایی که در این تقلید رعایت شده، یکسان بودن API هر دو است. یعنی مثلاً اگر پروژه‌ی جاوایی JavaProjABC وجود داشته باشد که شامل سه کلاس C1 و C2 و C3 بوده و کلاس C2 شامل متودی به اسم m1 باشد که دو پارامتر string دریافت می‌کند، پروژه‌ی دات‌نتی آن که احتمالاً NProjABC یا ProjABC.Net نام دارد هم دقیقاً همین کلاس‌ها و همین متودها را دارد. علاوه بر این رفتار کلاس‌ها و متودها هم یکی است. مثلاً اگر متود m1 در پروژه‌ی جاوایی دو پارامتر دریافتی را در یک فایل متنی نوشته و زیر آن یک خط می‌کشد، متود m1 پروژه‌ی دات‌نتی هم دقیقاً همین کار را می‌کند.

خوبی این موضوع این است که کدهای نوشته شده برای استفاده از یک پروژه‌ی جاوایی یا دات‌نتی اصطلاحاً قابل حمل بوده و به راحتی قابل تبدیل به platform دیگر است. خوبی دیگر آن، یکی بودن شکل و شمایل configuration است. مثلاً کدهای مورد نیاز برای configuration پروژه‌ی جاوایی log4j بسیار شبیه به configuration نسخه‌ی دات‌نتی یعنی log4net است. خوبی دیگری هم هست که خیلی هم قابل استفاده است. وقتی که کار کردن با یکی را بلد باشید کار کردن با آن یکی را هم خود به خود یاد گرفته‌اید و اگر مثالی یا کتابی روی یکی از پروژه‌ها موجود باشد، در آن پروژه‌ی دیگر هم می‌توان از آن استفاده کرد.

این مورد آخر جدیداً خیلی برای من به درد بخور بود. یکی از APIهای پر استفاده‌ی جستجو در NHibernate عبارت است از ICriteria. من به مشکلی در استفاده از آن برخورده بودم که نمی‌دانستم چطور باید آن را حل کنم. در مورد NHibernate و ICriteria جستجو کردم و چیز خاصی پیدا نکردم. اما خوشبختانه چون NHibernate پورت پروژه‌ی معروف جاوایی Hibernate به دات‌نت است توانستم از روی یک سوال و جواب Hibernate جاوایی به جوابم برسم.

Paging problem with Named SQL queries

Named SQL query in NHibernate is an easy way to apply paging through native SQL. SetFirstResult and SetMaxResults helps a lot here. But the problem is when query is get a bit complicated or for some other reasons that I don’t know, paging does not works on pages after first page. In this case a wrong sql query is generated.

From given answers I didn’t realized what’s the root cause and if there is any solutions or not. But because I was in emergency I used my work-around for it.

My work-around is adding paging elements directly in the query and pass “first result” and “max results” to the query as parameters instead of using SetFirstResult and SetMaxResults. Consider following example:





default-access="field.camelcase"
namespace="nhtest"
assembly="nhtest">



SELECT TOP (:maxResults) * FROM (

select

*

,ROW_NUMBER() OVER(ORDER BY (:orderBy)) as _sort_row from

Toy

)as query WHERE query._sort_row > :firstResult
ORDER BY query._sort_row

]]>





This code is inspired from script that NHibernate itself produces while correct paging. Parametrization “top” and “order” sections was not very easy. I got helped from here and here. Notice parametrized “order” is not necessary for paging. It was my own requirement.

از رنجی که می‌بریم

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

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

یکی دیگر از مشکلات ما تخصصی بودن مشاغل در آمریکاست. در آنجا آنقدر کارهای متنوع گوناگونی وجود دارد هر کسی می‌تواند صرفاً در یک موضوع خاص تخصص پیدا کرده و هیچ وقت هم بیکار نماند. مثلاً آنجا یک نفر می‌تواند تمام عمرش را DBA باشد، طراح صفحات UI باشد، PHP کار باشد یا… در حالی که ما در ایران معمولاً مجبور هستیم تلفیقی از چند فناوری را به طور هم‌زمان به کار ببریم. اشکال این داستان آنجاست که برنامه‌نویس آمریکایی صرفاً یک موضوع خاص مثل Silverlight یا NHibernate را دنبال کرده و کاملاً در آن مهارت پیدا کرده و آن موضوع را بدون هیچ عیب و نقصی انجام می‌دهد. در حالی ما در ایران هیچ وقت چنین فرصتی را پیدا نکرده و همیشه از فناوری‌ها و روش‌ها به صورت دست و پا شکسته استفاده می‌کنیم.

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

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

Partial Class

همیشه فکر می‌کردم امکان Partial Class در مورد کلاس‌هایی که خودم می‌نویسم چیز بی‌استفاده‌ای است. تا این که دیروز شرایطی پیش آمد که فقط Partial Class می‌توانست به من کمک کند.

من یک کلاس حجیم، بیش از ۷۰۰ خط داشتم که کار کردن با آن سخت شده بود. خصوصاً این که چند نفر به طور هم‌زمان روی آن کار می‌کردند و همیشه احتمال بروز conflict موقع check-in وجود داشت. علاوه بر این عملکرد متودهای آن را می‌شد به ۲ یا ۳ بخش مستقل تقسیم‌بندی کرد و بهتر بود هر کدام را در جای مستقلی نگه می‌داشتم. بنابراین فکر کردم که تقسیم این کلاس به دو یا چند کلاس دیگر خیلی بهتر است.

این کلاس از نوع Generic است و بیشتر اعضایش از نوع static تعریف شده‌اند. اول کار این طور به نظرم رسید که از Extension Method استفاده کرده و بخش‌های دوم و سوم را به یک کلاس static دیگر برده و عملکرد آنها را از طریق Extension Method مورد استفاده قرار دهم. با این که Extension Methodها با کلاس‌های Generic مشکلی نداشتند، اما چون می‌خواستم از داخل Extension Methodها، متودهای کلاس اصلی را که از نوع static هم بودن را صدا بزنم دچار خطای زیر می‌شدم:

Member ‘SomeMethod()’ cannot be accessed with an instance reference; qualify it with a type name instead

علت خطا همان طور که مشخص است این است که من اجازه ندارم از داخل Extension Methodها، متودهای static را صدا بزنم. ظاهراً دلیل آن این است که با Extension Methodها مشابه متودهای غیر static برخورد می‌شود و فراخوانی متودهای static از داخل آنها ممنوع است.

در اینجا بود که استفاده از Partial Classها به دردم خورد: کلاس مورد نظرم را به چند بخش کوتاه‌تر که از لحاظ عملکرد به هم شبیه بودند تقسیم کردم. این طوری اقلاً حجم فایل کلاس کوتاه‌تر شده و کار تیمی با آن راحت‌تر شد. به مثال MSDN دقت کنید:

public partial class Employee
{
public void DoWork()
{
}
}

public partial class Employee
{
public void GoToLunch()
{
}
}

‫‫‫‫داستان بی‌سوادی ما – ۴: Visual Studio Debugging

یکی از جاهایی که کار می‌کردم دو تا Solution خیلی بزرگ داشتیم. اولی که ۶۴ تا پروژه‌ی ویژوال استودیو بود که همگی تبدیل به dll می‌شدند و دومی که یک پروژه‌ی وب حجیم بود با چیزی بالای ۶۰۰ صفحه aspx. بعضی وقت‌ها که ایرادی پیش می‌آمد مجبور می‌شدم Solution اول را روی کامپیوتر خودم Build کنم، dllهایش را به Solution دوم اضافه کنم، Solution دوم را در حالت Debug بالا بیاورم، آن صفحه‌ای را که مشکل را داشت را باز کنم، روی آن خطی که dllهای Solution اول را صدا می‌زد Break Point بگذارم و هر وقت که اجرای برنامه به آن خط می‌رسید آنقدر F11 می‌زدم و از کلاس‌ها و متودهای مختلف رد می‌شدم تا به آنجایی که تازه می‌خواستم وجود یا عدم وجود ایراد را بررسی کنم برسم. این پروسه خودش در بهترین حالت ۲۰ دقیقه طول می‌کشید تازه بعضی وقت‌ها علت خطا را پیدا کرده و درست می‌کردم و می‌خواستم دوباره آن را امتحان کنم، این خودش یعنی ۲۰ دقیقه‌ی دیگر. حالا خودتان حساب کنید بعضی روزها که می‌افتادیم روی دنده‌ی Debug، چقدر باید وقتمان تلف می‌شد.

چند بار سعی کرده بودم این پروسه را با فراخوانی dllها در یک برنامه‌ی ویندوزی آزمایشی، حذف موقتی بعضی صفحات کم استفاده‌ی Solution دوم، استفاده از Unit Test، استفاده از logging و… کوتاه‌تر و بهینه‌تر کنم اما چندان موفق نبودم. چون به هر صورت بعضی چیزها را فقط در خود پروژه‌ی وب می‌توانستم Debug کنم. متاسفانه این بعضی چیزها خیلی کم تعداد نبودند و همیشه روی اعصاب من و بقیه راه می‌رفتند تا آنجا که بعضی وقت‌ها درخواست‌های Debug یک بخش را کلاً بی‌خیال شده و آن بخش را مجدداً بازنویسی می‌کردم یا این که ایرادهایی را که می‌دانستم قطعاً وجود دارند را مدام پشت گوش می‌انداختم چون واقعاً اعصاب و وقت دیباگ همچون پروژه‌ی حجیمی را نداشتم.

البته تا اینجای کار فقط یک حالت مشکل را شنیدید. حالت دیگر آن وقتی است که امکان استفاده از Break Point را ندارید و اصلاً نمی‌توانید Debug کنید. مثل وقتی که یک عملیاتی از قبیل Data Binding یا به روز رسانی اطلاعات از طریق کدهای Markup یا Declarative صفحات aspx انجام می‌شود.

این مشکلات چند سال است که گریبان‌گیر من است و من دیگر وجود آن را قبول کرده بودم تا این چند وقت پیش با یک ور رفتن کوچولو با StackOverflow فهمیدم که چقدر تا حالا وقتم بیهوده تلف می‌شده است. چون می‌توانستم به راحتی آب خوردن از گزینه Attach to Process ویژوال استودیو برای این کار استفاده کنم بدون آن که این همه وقتم تلف شود.

روش کار بدین صورت است که اول آن Solution وبی را باز می‌کنید و بدون حالت Debug به اجرا در می‌آورید. بعد Solution حاوی dllها را باز کرده و مطمئن شوید که به غیر از فایل‌های dll، فایل‌های pdb را هم دارید. البته اگر ندارید نگران نباشید. این فایل‌ها با یک بار Build به طور خودکار ساخته می‌شوند. قدم بعدی انتخاب گزینه‌ی Attach to Process از منوی Debug ویژوال استودیویی است که Solution حاوی dllها در آن باز است. حالا از فهرست پیش رو، process مربوط به ASP.NET Web Development Server را که معمولاً اسمش با WebDev شروع می‌شود را انتخاب نمایید. در این لحظه ویژوال استودیو خود به خود به حالت Debug می‌رود. شما می‌توانید هر جا که خواستید Break Point بگذارید. اگر به آن صفحه‌ای که نهایتاً آن کد را اجرا می‌کند بروید، می‌بینید که ویژوال استودیوی حاوی dll در محل Break Point انتخابی شما آماده دریافت F10 و F11 است. به همین سادگی! این کار حتی در مورد کدهایی هم که به طور مستقیم از markup صدا زده می‌شوند نیز قابل انجام است.

به عنوان سورپریز این قضیه را داشته باشید که می‌توانید عین همین کار را بدون اجرا کردن پروژه‌ی وبی از طریق ویژوال استودیوی اول هم انجام دهید البته به شرطی که همان پروژه را در IIS کامپیوترتان داشته باشید. روش کار به این صورت است که در بخش Attach to Process به جای Web Development Server از process مربوط به IIS که اسمش در ویندوز دو هزار و هشت w3wp است استفاده کنید.

سورپریز دومی هم وجود دارد. می‌توانید به جای استفاده از IIS کامپیوتر خودتان از IIS دیگر کامپیوترها هم استفاده کنید. یعنی می‌توانید از طریق ویژوال استودیوی کامپیوتر خودتان Web Applicationهایی که در IIS یک کامپیوتر دیگر نصب هستند را Debug کنید. اسم این کار Remote Debugging است.

‫داستان بی‌سوادی ما – ۳: ASP.NET Membership

یکی از امکانات ASP.NET مجموعه Membership و امکانات جانبی آن است. این مجموعه به برنامه‌های ASP.NET امکان تعریف کاربر، نقش و کنترل دسترسی بر اساس آنها را می‌دهد. برای این که بتوان از این امکانات استفاده کرد بایستی موارد مورد نیاز ASP.NET applications services در بانک اطلاعاتی مورد استفاده برنامه نصب گردد. این کار به طور معمول با کمک ابزار aspnet_regsql.exe انجام می‌شود.

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

 C:WindowsMicrosoft.NETFrameworkv4.0.30319
من می‌خواستم انجام کار را خودکار کنم به همین دلیل آن اسکریپت‌ها را از طریق کد باز کرده، اسم بانک اطلاعاتی خودم را به جای اسم پیش‌فرض آن می‌گذاشتم و دست آخر با هزار زحمت آن از طریق کدهای C#‎ در داخل دیتابیس اجرا می‌کردم. این کارها واقعاً وقت‌گیر و پردردسر بودند. چون انجام کامل آن و دیباگ آن حدود ۳ روز طول می‌کشید. علاوه بر آن وقتی از شرکتی به شرکتی دیگر نقل مکان می‌کردم دیگر نمی‌توانستم از آن کدهای قبلی استفاده کنم و باید آنها را دوباره از اول می‌نوشتم.

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

SqlServices.Install("databaseName", SqlFeatures.All, "connectionString");
این کلاس با ارزش در فضای نام System.Web.Management قرار دارد. جهت کسب اطلاع بیشتر به این لینک مراجعه کنید.

توجه: این نوشته در قالب سری نوشته‌های «بی‌سوادی ما» می‌باشد.

‫داستان بی‌سوادی ما – ۲

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

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

یکی از راه‌های جلوگیری از این احساس حماقت‌ها این است که در حوزه‌ی فناوری‌های مورد استفاده خود حسابی خبره شده و هیچ کتاب، خبر یا وبلاگی را از دست ندهیم. اما متاسفانه این راه اقلاً برای من امکان‌پذیر نیست. چون من مثل یک برنامه‌نویس آمریکایی نیستم که فقط در یک محدوده‌ی خاص فعالیت کنم. من به طور هم‌زمان باید با مجموعه‌ای قابل توجهی از فناوری‌های هر چند به طور سطحی کار کنم. از جمله: C#‎، ASP.NET، XML، XSLT، JavaScript، MS SQL Server، NHibernate، Castle ActiveRecord، Network Programming، WPF، TFS، SVN، ‌Build Automation، Agile، Scrum، Ajax، UML، Visual Studio، Unit Test، LINQ، Windows و…

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

Access NHibernate.ISession from Castle ActiveRecord

If you are a Castle ActiveRecord user you may currently encountered situations that it is needed to do some specific operation that is possible just via NHibernate or even just need to direct access to NHibernate’s ISession. For example if you want to use LINQ-to-NHibernate or run SQL query with Castle ActiveRecord, there is no way other than direct access to ISession.

To access NHibernate’s ISession you must utilize ActiveRecordMediator.Execute() or ActiveRecordBase.Execute(). This method accepts a NHibernateDelegate delegate. Then through this delegate you have access to NHibernate’s ISession. Consider following code that uses LINQ-to-NHibernate through Castle ActiveRecord:

[ActiveRecord(Lazy = true)]
public class Car: ActiveRecordBase
{
public static List GetCars()
{
return (List)Execute(
delegate(ISession session, object instance)
{
//here you have direct access to ISession

var q = from car in session.Linq()
select car;

return q.ToList();
}, null);
}
}

For more examples go to this, this and this link.

BelongsTo and cascading

BelongsTo is a attribute in Castle ActiveRecord that is used to mark an association as many-to-one. For example consider following class diagram. There is an association between Student and Teacher named ArtStudent. This is a many-to-one association so you must add BelongsTo attribute to Student class as showed in following code snippet.

[ActiveRecord(Lazy = true)]
public class Teacher : ActiveRecordBase
{
    //other properties
}

[ActiveRecord(Lazy = true)]
public class Student : ActiveRecordBase
{
    [BelongsTo("ArtTeacher_ID", Cascade = CascadeEnum.None)]
    public virtual Teacher ArtTeacher { set; get; }

    //other properties
}

As this is an one-way association from Student only, Teacher does not know anything about student so it not necessary to add any association or property to Teacher.

Using Cascade field of BelongsTo attribute you can tell ActiveRecord what to do if any modifications occurs to Student. These modification can be SaveUpdate and/or Delete. Please take attention that means modifications just for Student not Teacher. For example if you have had set cascade mode to CascadeEnum.Delete, whenever the Student is deleted, its associated Teacher will be deleted too, but if a Teacher is deleted, no Student will be deleted.

More documentation about BelongsTo attribute:

1. BelongsToAttribute API Document (Trunk)
2.  BelongsToAttribute API Document (RC1)
3. BelongsToAttribute Class
4. BelongsToAttribute Members
5. CascadeEnum Enumeration