چرا باید از ابزارهای Object relational Mapper یا به اختصار ORM استفاده کرد؟ در اینجا سخن در مورد ORM خاصی نیست. هدف تبلیغ یک محصول ویژه هم نمیباشد و یک بحث کلی مد نظر است.
کار ابزارهای ORM خواندن ساختار دیتابیس شما بوده و سپس ایجاد کلاسهایی بر اساس این ساختار ، برقراری ارتباط بین اشیاء ایجاد شده و جداول، ویووها، رویههای ذخیره شده و غیره میباشد. همچنین این ابزارها امکان تعریف روابط one-to-one, one-to-many, many-to-one, و many-to-many بین اشیاء را نیز بر اساس ساختار دیتابیس شما فراهم میکنند.
در ادامه به فواید استفاده از ORM ها خواهیم پرداخت:
الف) یک ابزار ORM زمان تحویل پروژه را کاهش میدهد
اولین و مهمترین دلیلی که بر اساس آن در یک پروژه، استفاده از ORM حائز اهمیت میشود، بحث بالا بردن سرعت برنامه نویسی و کاهش زمان تحویل پروژه به مشتری است. این کاهش زمان بسته به نوع پروژه بین 20 تا 50 درصد میتواند خود را بروز دهد.
بدیهی است ابزارهای ORM کار شگفت انگیزی را قرار نیست انجام دهند و شما میتوانید تمام آن عملیات را دستی هم به پایان رسانید؛ اما اجازه دهید یک مثال کوتاه را با هم مرور کنیم.
برای پیاده سازی یک برنامه متداول با حدود 15 تا 20 جدول، حدودا به 30 شیء برای مدل سازی سیستم نیاز خواهد بود و برنامه نویسی این مجموعه بین 5000 تا 10000 سطر کد را به خود اختصاص خواهد داد. بدیهی است برنامه نویسی و آزمایش این سیستم چندین هفته یا ماه به طول خواهد انجامید.
اما با استفاده از یک ORM ، عمده وقت شما به طراحی سیستم و ایجاد ارتباطات بین اشیاء و دیتابیس در طی یک تا دو روز صرف خواهد شد. ایجاد کد بر اساس این مجموعه و با کمک ابزارهای ORM ، آنی است و با چند کلیک صورت میگیرد.
ب) یک ابزار ORM کدی با طراحی بهتر را تولید میکند
ممکن است شما بگوئید که کد نویسی من بینظیر است و از من بهتر کسی را نمیتوانید پیدا کنید! به تمامی زوایای کار خود مسلطم و نیازی هم به اینگونه ابزارها ندارم!
عدهای از شما به طور قطع اینگونهاید؛ اما نه همه. در یک تیم متوسط، همه نوع برنامه نویس با سطوح مختلفی را میتوانید پیدا کنید و تمامی آنها برنامه نویسها و یا طراحهای آنچنان قابلی هم نیستند. بنابراین امکان رسیدن به کدهایی که مطابق اصول دقیق برنامه نویسی شیء گرا نیستند و در آنها الگوهای طراحی به خوبی رعایت نشده، بسیار محتمل است. همچنین در یک تیم زمانیکه از یک الگوی یکسان پیروی نمیشود، نتایج نهایی بسیار ناهماهنگ خواهند بود.
در مقابل استفاده از ORM های طراحی شده توسط برنامه نویسهای قابل (senior (architect level) engineers) ، کدهایی را بر اساس الگوهای استاندارد و پذیرفته شدهی شیءگرا تولید میکنند و همواره یک روند کاری مشخص و هماهنگ را در یک مجموعه به ارمغان خواهند آورد.
ج) نیازی نیست تا حتما یک متخصص دات نت فریم ورک باشید تا از یک ORM استفاده کنید
قسمت دسترسی به دادهها یکی از اجزای کلیدی کارآیی برنامه شما است. اگر طراحی و پیاده سازی آن ضعیف باشد، کل برنامه را زیر سؤال خواهد برد. برای طراحی و پیاده سازی دستی این قسمت از کار باید به قسمتهای بسیاری از مجموعهی دات نت فریم ورک مسلط بود. اما هنگام استفاده از یک ORM مهمترین موردی را که باید به آن تمرکز نمائید بحث طراحی منطقی کار است و ایجاد روابط بین اشیاء و دیتابیس و امثال آن. مابقی موارد توسط ORM انجام خواهد شد و همچنین میتوان مطمئن بود که پیاده سازی خودکار انجام شده این قسمتها، بر اساس الگوهای طراحی شیءگرا است.
د) هنگام استفاده از یک ابزار ORM ، مدت زمان آزمایش برنامه نیز کاهش مییابد
بدیهی است اگر قسمت دسترسی به دادهها را خودتان طراحی و پیاده سازی کرده باشید، زمان قابل توجهی را نیز باید به بررسی و آزمایش صحت عملکرد آن بپردازید و الزامی هم ندارد که این پیاده سازی مطابق بهترین تجربیات کاری موجود بوده باشد. اما هنگام استفاده از کدهای تولید شده توسط یک ابزار ORM میتوان مطمئن بود که کدهای تولیدی آن که بر اساس یک سری الگوی ویژه تولید میشوند، کاملا آزمایش شده هستند و همچنین صدها و یا هزارها نفر در دنیا هم اکنون دارند از این پایه در پروژههای موفق خود استفاده میکنند و همچنین بازخوردهای خود را نیز به تیم برنامه نویسی آن ابزار ORM ارائه میدهند و این مجموعه مرتبا در حال بهبود و به روز شدن است.
ه) استفاده از یک ابزار ORM ، کار برنامه نویسی شما را سادهتر میکند
توضیح این قسمت نیاز به ذکر یک مثال دارد. لطفا به مثال زیر دقت بفرمائید:
try {
Employees objInfo = new Employees();
EmployeesFactory objFactory = new EmployeesFactory();
objInfo.EmployeeID = EmployeeID;
objFactory.Load(objInfo);
// code here to use the "objInfo" object
}
catch(Exception ex) {
// code here to handle the exception
}
به نظر شما کار کردن با یک یا چند شیء تولید شده که نمایانگر ساختار دیتابیس شما هستند و با استفاده از اینترفیس عمومی آنها میتوان تمامی اعمال بارگذاری، درج و حذف و غیره را انجام داد، سادهتر است یا کار کردن با کوهی از دستورات ADO.Net ؟
برداشتی آزاد از Five Reasons for using an ORM Tool
من در یکی از پروژهها از Kendo UI Treeview
استفاده کردم و قصد داشتم قابلیت تغییر نام را به گرهها بدهم. به همین جهت پس از جستجو به x-editable
برخوردم. این کتابخانهی جاواسکریپتی در ابتدا برای قالبهای بوت استراپ
طراحی شده بود که در حال حاضر اینگونه نیست و به راحتی در هر پروژهای که
فقط جی کوئری صدا زده شده باشد، قابل اجرا است و نسخهی مخصوص Angular آن هم در این آدرس
قرار دارد. همچنین این قابلیت اختیاری و پیش فرض را دارد که به طور
خودکار اطلاعات تغییر یافته را به سمت url ایی که شما تعیین میکنید، ارسال کند.
برای همین نیازی به استفاده جداگانه از jQuery Ajax برای ارسال اطلاعات نیست
و البته در انتهای مقاله هم هنگام استفاده از درخت کندو به مشکلی برخوردم
که آن را هم بررسی میکنیم.
وارد کردن کتابخانه ها
این کتابخانه شامل دو فایل css و JS میباشد که بسته به محیطی که در آن کار میکنید متفاوت هستند. در این صفحه شما میتوانید برای 4 محیط Jquery ,JqueryUI , Bootstrap2 و Bootsrap3 بستهی مخصوصش را یا به صورت دانلود فایلها یا از طریق CDN دریافت نمایید. در اینجا هم یک دمو از قابلیتهای آن قابل مشاهده است.
برای شروع، کتابخانهی مورد نظر خود را دریافت و آنها را به صفحهی خود اضافه نمایید. در صورتیکه از Bootstrap استفاده میکنید، ابتدا فایلهای زیر را اضافه کنید:
وارد کردن کتابخانه ها
این کتابخانه شامل دو فایل css و JS میباشد که بسته به محیطی که در آن کار میکنید متفاوت هستند. در این صفحه شما میتوانید برای 4 محیط Jquery ,JqueryUI , Bootstrap2 و Bootsrap3 بستهی مخصوصش را یا به صورت دانلود فایلها یا از طریق CDN دریافت نمایید. در اینجا هم یک دمو از قابلیتهای آن قابل مشاهده است.
برای شروع، کتابخانهی مورد نظر خود را دریافت و آنها را به صفحهی خود اضافه نمایید. در صورتیکه از Bootstrap استفاده میکنید، ابتدا فایلهای زیر را اضافه کنید:
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"> <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
اولین حالتیکه میتوانید با این کتابخانه کار کنید، استفاده از خاصیت *-data است. نمونه زیر را در نظر بگیرید:
<a href="#" id="favsite" data-type="text" data-pk="1" data-url="@Url.Action(MVC.Categories.EditCategory())" data-title="Enter your favorite site">dotnettips.info</a>
$(document).ready(function () { $('#favsite').editable(); });
$(document).ready(function () { $.fn.editable.defaults.mode = 'inline'; $('#favsite').editable(); });
حالت بعدی که میتوان استفاده کرد به شکل زیر است:
<a href="#" id="favsite" >dotnettips.info</a>
$.fn.editable.defaults.mode = 'inline'; $(document).ready(function () { $('#favsite').editable({ type: 'text', pk: 1, url: '@Url.Action(MVC.Categories.EditCategory())', title: 'Enter your favorite site' }); });
خوبی این روش این است که میتوان اطلاعات بیشتری چون رویدادها را به آن پاس داد. تا الان با نحوهی انتساب آن به اشیاء آشنا شدیم. اجازه دهید تا با خصوصیات آن آشنا شویم.
AjaxOptions | همانطور
که متوجه شدید به طور خودکار اطلاعات ویرایش شده، به سمت آدرس داده شده،
به شیوه Post ارسال میگردند. در صورتیکه قصد دست بردن
در نوع درخواست را دارید، میتوانید از این ویژگی استفاده کنید: ajaxOptions: { type: 'put', dataType: 'json' } |
Anim | این
ویژگی که تنها در حالت inline پاسخ میدهد، میتواند زمان بسته شدن
x-editable را تغییر دهد که به طور پیش فرض با false مقداردهی شده است. جهت
تغییر زمان بسته شدن، کد زیر را وارد نمایید: anim:'false' //or anim: { duration: 2000 } |
autotext | در انتهای جدول آمده است. |
defaultValue | در
صورتیکه عنصر مورد نظر محتوایی نداشته باشد و این خصوصیت را
مقداردهی کنید، موقع ویرایش، این عبارت تعیین شده نمایش مییابد. در مثال
بالا باید متن تگ a را حذف کرده تا نتیجه را ببینید: (البته فیلد value نباید مقداری داشته باشد) defaultValue: 'default val' //or defaultValue: undefined //or defaultValue: null |
disabled | false کردن این ویژگی باعث غیرفعال شدن x-editable بر روی کنترل جاری میگردد. |
display | خاصیت
display یا مقدار بولین false را دریافت میکند، یا نال، یا یک تابع
callback را میتوان به آن پاس داد. این خصوصت زمانی صدا زده میشود که
اطلاعات به سمت آدرس سرور رفته و با موفقیت بازگشت داده میشوند (در صورتی
که این ویژگی غیرفعال باشد، بلافاصه بعد از تایید کاربر، از اطلاعات وارد شده
صدا زده میشود) و سپس متن جدید عنصر تغییر مییابد. حال اگر این خاصیت نال
که مقدار پیش فرض آن است باشد، متن تغییر مییابد. ولی اگر false باشد، متن سابق
باقی خواهد ماند و در صورتیکه تابعی به آن پاس داده باشید، طبق تابع شما
عمل خواهد کرد. پارامترهایی که تابع شما میتواند داشته باشد به شرح زیر است: value : مقدار جدید response : پاسخ سرور ( در صورتی که ارسال از طریق Ajax صورت گرفته باشد) و در صورتیکه عنصر شما checlklist یا select باشد که حاوی منبعی از مقادیر هست، مقادیرشان در قالب یک آرایه با نام sourceData بازگشت خواهد خورد. برای دسترسی به آیتمهای انتخابی هم از کد زیر استفاده میکنیم: $.fn.editableutils.itemsByValue(value, sourceData) |
emptyclass | معرفی یک کلاس css برای موقعیکه عنصر خالی است. |
emptytext | در صورتی خالی بودن عنصر، این متن را برای عنصر نمایش بده. |
highlight | بعد از به روز رسانی متن عنصر، آن را با این رنگ highlight خواهد کرد و کد رنگی باید در مبنای هگز باشد. مقدار پیش فرض آن false است. |
mode | دو
حالت نمایشی دارد که پیش فرض آن popup است و با باز کردن یک پنجره، مقدار
جدید را دریافت میکند. مورد بعدی inline است که به جای باز کردن پنجره،
متن عنصر را به حالت ویرایش تغییر میدهد. |
name | نام فیلدی که مقدارش تغییر میکند. |
onblur | زمانی که کاربر فوکوس را از ویرایشگر میگیرد، ویرایشگر چه پاسخی باید به آن بدهد، باز بماند؟ ignore ، بسته شود؟ cancel و یا مقدار داده شده را تایید کند؟submit |
params | پارامترهای درخواست ایجکسی که کنترل در حالت پیش فرض ارسال میکند؛ شامل Pk که آن را با id
رکورد پر میکنیم. name نام فیلدی که تغییر یافته است و value که مقدار جدید
است. در صورتیکه دوست دارید اطلاعات اضافیتری نیز ارسال شوند،
میتوانید از این خاصیت استفاده کنید و پارامترها را در قالب Object به آن
پاس کنید. ولی اگر بخواهید در کل همهی پارامترها را رونویسی کنید باید یک
تابع را به آن پاس کنید: params: function(params) { //در این حالت پارامترهای پیش فرض ارسال نشده و تنها پارامترهای معرفی شده در این تابع ارسال میشوند params.a = 1; return params; } |
pk | کلید
اصلی رکورد شما در دیتابیس یا هان id است. در صورتی که از کلیدهای ترکیبی
استفاده میکنید، نگران نباشید فکر آن را هم کرده اند.//کلید عدد pk:1, //کلید رشته ای pk:'dp123' //کلید ترکیبی pk:{id: 1, lang: 'en'} //معرفی یک تابع به آن و بازگشت |
Placement | این ویژگی فقط به درد حالت Popup میخورد که پنجره را کجای عنصر نمایش دهد و شامل چهار مقدار left,right,top,bottom میشود. |
saveonchange | زمانی
که مقدار جدید، برابر مقدار فعلی باشد و این خاصیت false باشد، هیچ تغییری رخ
نخواهد داد. ولی اگر برابر true باشد ،مقدار جدید اسال و جایگزین مقدار فعلی
خواهد شد. مقدار پیش فرض آن false است. |
selector |
با استفاده از این خصوصیت در عنصر انتخابی به دنبال عناصری که در selector
تعیین شده میگردد و حالت ویرایش را روی آنها فعال میکند. در این حالت استفاده از خصوصیات emptytext و autotext در ابتدای امر ممکن نیست و بعد از اولین کلیک قابل استفاده هستند. نکته بعدی اینکه شما باید کلاسهای زیر را دستی اضافه کنید. کلاس editable-click برای همه کنترلها وکلاس editable-empty به کنترلهای بدون مقدار و برای مقداردهی کنترلهای بدون مقدار میتوان از خاصیت ''=data-value استفاده کرد. <div id="user"> <!-- empty --> <a href="#" data-name="username" data-type="text" data-value="" title="Username">Empty</a> <!-- non-empty --> <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a> </div> <script> $('#user').editable({ selector: 'a', url: '/post', pk: 1 }); </script> |
send | سه
مقدار auto,always و never را دریافت میکند. موقعی که شما آن را روی auto
تنظیم کنید؛ در صورتی مقادیر به سمت سرور ارسال میشوند که دو خاصیت url و
pk تعریف شده باشند. در غیر این صورت ویرایش فقط در حالت محلی و روی سیستم
کاربر رخ خواهد داد. |
showbuttons | در
صورتیکه با false مقداردهی شود، تایید فرم به طور خودکار انجام میگیرد و
اگر با یکی از مقادیر left یا Bottom پر شود، دکمهها را در آن قسمت نشان
میدهد. |
success | اطلاعات به سمت سرور
رفته و با موفقیت با کد 200 بازگشت داده شدهاند. در مستندات نوشته است، هر
کد وضعیتی غیر از 200 بازگشت داده شود، به سمت خاصیت error هدایت میشو.د ولی
آن طور که من با httpresponsemessage
تست کردم، چنین چیزی را مشاهده نکردم و مجددا success صدا زده شد. پس بهتر
هست دادهای را که به سمت کنترل برگشت میدهید، خودتان کنترل کنید. به خصوص
اگر انتقال اطلاعات صحیح باشد. ولی اگر در دیتابیس، تغییر با خطا روبرو گردد
بهتر است نتیجهی آن ارسال شده و از تغییر مقدار فعلی ممانعت به عمل آورید. success: function(response, newValue) { if(!response.success) return response.msg; } |
toggle | اگر قصد دارید که باز و بسته کردن ویرایشگر را بر عهدهی مثلا یک دکمهی روی صفحه بگذارید، این خصوصیت به شما کمک میکند:$('#edit-button').click(function(e) { e.stopPropagation(); $('#favsite').editable('toggle'); }); |
type | نوع
ویرایشی را که قرار است انجام گیرد، مشخص میکند. text برای متن، date برای
تاریخ، textarea جهت متون طولانیتر نسبت به text و بسیاری از موارد دیگر |
unsavedclass | این
کلاس موقعی اعمال میگردد که اطلاعاتی را ویرایش کردهاید، ولی اطلاعاتی
به سمت سرور ارسال نشده است. مثلا pk مقداردهی نشده یا send=never قرار
داید و یا اینکه به صورت محلی ذخیره میکنید و میخواهید در آخر همهی اطلاعات را
ارسال کنید. این خاصیت به طور پیش فرض با کلاس editable-unsaved مقداردهی شده که میتوانید با نال کردن، از شرش خلاص شوید. |
url | این
خاصیت با آدرس سمت سرور پر میشود. ولی میتوان به آن یک تابع هم پاس کرد که
این تابع جایگزین درخواست ایجکسی خودش خواهد شد و برای بازگشت دادن نتیجهی این درخواست به سمت تابعهای callback خودش میتوانید یک deferred object را برگشت دهید: url: function(params) { var d = new $.Deferred; if(params.value === 'abc') { //returning error via deferred object return d.reject('error message'); } else { //async saving data in js model someModel.asyncSaveMethod({ ..., success: function(){ d.resolve(); } }); return d.promise(); } } |
validate | مقدار
پیش فرض آن نال است و میتوان به آن یک تابع را جهت اعتبارسنجی سمت کلاینت پاس
داد. به عنوان آرگومان، مقدار جدیدی را ارسال کرده و در آن به اعتبارسنجی
میپردازید. در صورتی که مقدار، نامعتبر باشد، میتوانید یک پیام خطا از نوع
رشتهای را برگردانید. در صورتی که از نسخهی 1.5.1 به بعد استفاده میکنید، دریافت یک object با مقادیر زیر نیز ممکن شده است: newValue: مقدار جدید و جایگزین مقدار غیر معتبر. msg : پیام خطا. به کدهای زیر در سه حالت نگاه کنید: validate: function (value) { if ($.trim(value) == '') { //در تمامی نسخههای یک پیام متنی باز میگردد return 'This field is required'; //1.5.1 //یک مقدار جدید برگشت میدهد که بلافاصله آن را // تایید میکند و متن عنصر به روز میشود return { newValue: 'validated' }; //متن جدید ار ارسال کرده و پیام هشدار را نشان میدهد //ولی تایید نمیکند و منتظر تایید کاربر است return { newValue: 'validated', |
value | مقدار پیش فرضی که در ویرایشگر نشان میدهد و اگر مقداردهی نشود، از متن عنصر استفاده میکند. |
autotext | سه مقدار دارد auto (پیش فرض)،always و never. موقعیکه عنصر شما متنی نداشته باشد و روی auto تنظیم شده باشد، مقدار value را به عنوان متن عنصر نمایش میدهد. always کاری ندارد که عنصر شما متن دارد یا خالی است؛ مقدار value به آن انتساب داده خواهد شد. never هیچگاه. |
در قسمت بعدی که قسمت پایانی است مطالب را ادامه میدهیم.
مطالب
خواندنیهای 3 خرداد
- شیرپوینت 2007 و سایت جدید آن (که بر اساس خود MOSS2007 و سیلورلایت است)
- راهنمای اس کیوال سرور 2008 ، BOL - May 2008
قبل از اینکه به httpmoduleها بپردازیم، اجازه بدید کمی در در مورد httphandler اطلاعات کسب کنیم. httphandler ویژگی است که از asp.net به بعد ایجاد شد و در asp کلاسیک خبری از آن نیست.
یک httphandler کامپوننتی است که از طریق اینترفیس System.Web.IHttpHandler پیاده سازی میشود و به پردازش درخواستهای رسیده از httprequest رسیدگی میکند.
فرض کنید کاربری درخواست صفحه default.aspx را کرده است و سرور هم پاسخ آن را میدهد. در واقع پردازش اینکه چه پاسخی باید به کاربر یا کلاینت ارسال شود بر عهده این کامپوننت میباشد. برای وب سرویس هم موضوع به همین صورت است؛ هر نوع درخواست HTTP از این طریق انجام میشود.
حال به سراغ httpmodule میرویم. httpmoduleها اسمبلی یا ماژولهایی هستن که بر سر راه هر درخواست کاربر از سرور قرار گرفته و قبل از اینکه درخواست شما به httphandler برسد، اول از فیلتر اینها رد میشود. در واقع موقعی که شما درخواست صفحه default.aspx را میکنید، درخواست شما به موتور asp.net ارسال میشود و از میان فیلترهایی رد میشود تا به دست httphandler برای پردازش خروجی برسد. برای همین اگر گاهی به جای گفتن asp.net engine عبارت asp.net pipeline هم میگویند همین هست؛ چون درخواست شما از بین بخشهای زیادی میگذرد تا به httphandler برسد که httpmodule یکی از آن بخش هاست. با هر درخواستی که سرور ارسال میشود، httpmoduleها صدا زده میشوند و به برنامه نویس امکان بررسی اطلاعات درخواستی و پردازش درخواستها را در ورودی و خروجی، میدهد و شما میتوانید هر عملی را که نیاز دارید انجام دهید. تعدادی از این ماژولهای آماده، همان stateها و Authentication میباشند.
تصویر زیر نحوهی ارسال و بازگشت یک درخواست را به سمت httphandler نشان میدهد
برنامه نویس هم میتواند با استفاده از اینترفیسهای IHttpModule و IHttpHandler در درخواستها دخالت نماید.
برای شروع یک کلاس ایجاد کنید که اینترفیس IHttpModule را پیاده سازی میکند. شما دو متد را باید در این کلاس بنویسید؛ یکی Init و دیگر Dispose. همانطور که مطلع هستید، اولی در ابتدای ایجاد شیء و دیگر موقع از دست رفتن شی صدا زده میشود.
متد Init یک آرگومان از نوع httpapplication دارد که مانند رسم نامگذاری متغیرها، بیشتر به اسم context یا app نام گذاری میشوند:
public void Init(HttpApplication app) { app.BeginRequest += new EventHandler(OnBeginRequest); } public void Dispose(){ }
BeginRequest | این رویداد اولین رویدادی است که اجرا میشود، هر نوع عملی که میخواهید در ابتدای ارسال درخواست انجام دهید، باید در این قسمت قرار بگیرد؛ مثلا قرار دادن یک بنر بالای صفحه |
AuthenticateRequest | خود دانت از یک سیستم امنیتی توکار بهره مند است و اگر میخواهید در مورد آن خصوصی سازی انجام بدهید، این رویداد میتواند کمکتان کند |
AuthorizeRequest | بعد از رویداد بالا، این رویداد برای شناسایی انجام میشود. مثلا دسترسی ها؛ دسترسی به قسمت هایی خاصی از منابع به او داده شود و قسمت هایی بعضی از منابع از او گرفته شود. |
ResolveRequestCache | این رویداد برای کش کردن اطلاعات استفاده میشود. خود دانت تمامی این رویدادها را به صورت تو کار فراهم آورده است؛ ولی اگر باز خصوصی سازی خاصی مد نظر شماست میتوانید در این قسمتها، تغییراتی را اعمال کنید. مثلا ایجاد file caching به جای memory cache و ... |
AcquireRequestState | این قسمت برای مدیریت state میباشد مثلا مدیریت session ها |
PreRequestHandlerExecute | این رویداد قبل از httphandler اجرا میشود. |
PostRequestHandlerExecute | این رویداد بعد از httphandler اجرا میشود. |
ReleaseRequestState | این رویداد برای این صدا زده میشود که به شما بگوید عملیات درخواست پایان یافته است و باید stateهای ایجاد شده را release یا رها کنید. |
UpdateRequestCache | برای خصوصی سازی output cache بکار میرود. |
EndRequest | عملیات درخواست پایان یافته است. در صورتیکه قصد نوشتن دیباگری در طی تمامی عملیات دارید، میتواند به شما کمک کند. |
PreSendRequestHeaders | این رویداد قبل از ارسال طلاعات هدر هست. اگر قصد اضافه کردن اطلاعاتی به هدر دارید، این رویداد را به کار ببرید. |
PreSendRequestContent | این رویداد موقعی صدا زده میشود که متد response.flush فراخوانی شود.، اگر میخواهید به محتوا چیزی اضافه کنید، از اینجا کمک بگیرید. |
Error | این رویداد موقعی رخ میدهد که یک استثنای مدیریت نشده رخ بدهد. برای نوشتن سیستم خطایابی خصوصی از این قسمت عمل کنید. |
Disposed | این رویداد موقعی صدا زده میشود که درخواست، بنا به هر دلیلی پایان یافته است. برای عملیات پاکسازی و .. میشود از آن استفاده کرد. مثلا یک جور rollback برای کارهای انجام گرفته. |
کد زیر را در نظر بگیرید:
کد زیر یک رویداد را تعریف کرده و سپس خود httpapplication را به عنوان sender استفاده میکند.
در اینجا قصد داریم یکی از صفحات را در خروجی تغییر دهیم. آدرس تایپ شده همان باشد ولی صفحهی درخواست شده، صفحهی دیگری است. این کار موقعی بیشتر کاربردی است که آدرس یک صفحه تغییر کرده و کاربر آدرس قبلی را وارد میکند. حالا یا از طریق بوک مارک یا از طریق یک لینک، در یک جای دیگر و شما میخواهید او را به صفحهای جدید انتقال دهید، ولی در نوار آدرس، همان آدرس قبلی باقی بماند. همچنین کار دیگری که قرار است انجام بگیرد محاسبه مدت زمان رسیدگی به درخواست را محاسبه کند ، برای همین در رویداد BeginRequest زمان شروع درخواست را ذخیره و در رویداد EndRequest با به دست آوردن اختلاف زمان فعلی با زمان شروع به مدت زمان مربوطه پی خواهیم برد.
با استفاده از app.Context.Request.RawUrl آدرس اصلی و درخواست شده را یافته و در صورتی که شامل نام صفحه مربوطه بود، با نام صفحهی جدید جابجا میکنیم تا اطلاعات به صفحهی جدید پاس شوند ولی در نوار آدرس، هنوز آدرس قبلی یا درخواست شده، قابل مشاهده است.
در خط ["app.Context.Items["start که یک کلاس ارث بری شده از اینترفیس IDictionary است، بر اساس کلید، داده شما را ذخیره و در مواقع لزوم در هر رویداد به شما باز میگرداند.
public class UrlPath : IHttpModule { public void Init(HttpApplication app) { app.BeginRequest+=new EventHandler(_BeginRequest); app.EndRequest+=new EventHandler(_EndRequest); } public void Dispose() { } void _BeginRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication) sender; app.Context.Items["start"] = DateTime.Now; if (app.Context.Request.RawUrl.ToLower().Contains("tours_list.aspx")) { app.Context.RewritePath(app.Context.Request.RawUrl.ToLower().Replace("tours_list.aspx","tours_cat.aspx")); } } void _EndRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; string log = (DateTime.Now - DateTime.Parse(app.Context.Items["start"].ToString())).ToString(); Debugger.Log(0,"duration","request took " + log+Environment.NewLine); } }
<httpModules> <add name="UrlPath" type="UrlPath"/> </httpModules>
اگر کلاس شما داخل یک namespace قرار دارد، در قسمت type حتما قبل از نام کلاس، آن را تعریف کنید namspace.ClassName
حالا دیگر کلاس UrlPath به عنوان یک httpmodule به سیستم معرفی شده است. تگ httpmodule را بین تگ <system.web> قرار داده ایم.
در ادامه پروژه را start بزنید تا نتیجه کار را ببینید:
اگر IIS شما، هم نسخهی IIS من باشد، نباید تفاوتی مشاهده کنید و میبینید که درخواستها هیچ تغییری نکردند؛ چرا که اصلا httpmodule اجرا نشده است. در واقع در نسخههای قدیمی IIS یعنی 6 به قبل، این تعریف درست است ولی از نسخهی 7 به بعد IIS، روش دیگری برای تعریف را قبول دارد و باید تگ httpmodule، بین دو تگ <syste.webserver> قرار بگیرد و نام تگ httpmodule به module تغییر پیدا کند.
پس کد فوق به این صورت تغییر میکند:
<system.webServer> <modules> <add name="UrlRewrite" type="UrlRewrite"/> </modules> </system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
HTTP Error 500.22 - Internal Server Error
در کل استفاده از این ماژول به شما کمک میکند تمامی اطلاعات ارسالی به سیستم را قبل از رسیدن به قسمت پردازش بررسی نمایید و هر نوع تغییری را که میخواهید اعمال کنید و لازم نیست این تغییرات را روی هر بخش، جداگانه انجام دهید یا یک کلاس بنویسید که هر بار در یک جا صدا بزنید و خیلی از موارد دیگر
Global.asax و HttpModule
اگر با global.asax کار کرده باشید حتما میپرسید که الان چه تفاوتی با httpmodule دارد؟ در فایل global هم همینها را دارید و دقیقا همین مزایا مهیاست؛ در واقع global.asax یک پیاده سازی از httpapplication هست.
کلاسهای httpmodule نام دیگری هم دارند به اسم Portable global .asax به معنی یک فایل global.asax قابل حمل یا پرتابل. دلیل این نام گذاری این هست که شما موقعی که یک کد را در فایل global مینویسید، برای همیشه آن کد متعلق به همان پروژه هست و قابل انتقال به یک پروژه دیگر نیست ولی شما میتوانید httpmoduleها را در قالب یک پروژه به هر پروژه ای که دوست دارید رفرنس کنید و کد شما قابلیت استفاده مجدد و Reuse پیدا میکند و هم اینکه در صورت نیاز میتوانید آنها را در قالب یک dll منتشر کنید.
چطوری میشه موقع Stop کردن IIS و یا stop شدن پروژه به هر دلیلی (قبل از Stop کامل) متدی call شود که آخرین اطلاعاتی که در رم و در jobها اجرا شده رو در دیتابیس ذخیره کرد؟
مثلا تعداد کاربران آنلاین رو در یک متغیر static ذخیره میکنم که وقتی پروژه به هر دلیلی Stop شد میخوام آخرین اطلاعات متغیر استاتیک رو در دیتابیس ذخیره کنم.
البته در یک وظیفه (job) در پایان هر روز کل تعداد بازدیدها رو به تاریخ همان روز در دیتابیس ذخیره میکنم ولی میخواستم اگر به هر دلیلی پروژه Stop شد آخرین تعداد رو تا لحظه Stop شدن در دیتابیس ذخیره کنم.
برای حل این مشکل چه راهکاری پیشنهاد میکنید؟
مثلا تعداد کاربران آنلاین رو در یک متغیر static ذخیره میکنم که وقتی پروژه به هر دلیلی Stop شد میخوام آخرین اطلاعات متغیر استاتیک رو در دیتابیس ذخیره کنم.
البته در یک وظیفه (job) در پایان هر روز کل تعداد بازدیدها رو به تاریخ همان روز در دیتابیس ذخیره میکنم ولی میخواستم اگر به هر دلیلی پروژه Stop شد آخرین تعداد رو تا لحظه Stop شدن در دیتابیس ذخیره کنم.
برای حل این مشکل چه راهکاری پیشنهاد میکنید؟
البته در استفاده از رویه ذخیره شده سمت sql این امکان وجود دارد که رمزعبور توسط SQL Profiler رصد شود!
و کلا پسورد هش نشده به دیتابیس پاس می شود.
جا داره منم از زحمات و مطالب شما تشکر و سپاس گذاری کنم.
و کلا پسورد هش نشده به دیتابیس پاس می شود.
جا داره منم از زحمات و مطالب شما تشکر و سپاس گذاری کنم.
عموما هنگام تهیه یک مستند یا گزارش، هرچقدر حجم نهایی کمتر باشد، توزیع آن سادهتر خواهد بود. در اینجا اینطور به نظر میرسد که اگر مثلا از تصاویری با فرمت jpg یا png استفاده کنیم، کمترین حجم نهایی را میتوان بدست آورد. اما حین استفاده از iTextSharp شما با استفاده از تصاویری با فرمت BMP بهترین نتیجه را خواهید گرفت: کمترین حجم و بهترین کیفیت! البته یک نکتهی ریز دارد که باید رعایت شود:
using (var pdfDoc = new Document(PageSize.A4))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("tpn.pdf", FileMode.Create));
pdfWriter.SetPdfVersion(new PdfName("1.5"));
pdfWriter.CompressionLevel = PdfStream.BEST_COMPRESSION;
//...
}
در اینجا pdf version و همچنین compression level باید تنظیم شوند. پس از آن فشرده سازی تصاویر BMP به صورت خودکار حین تهیه فایل نهایی انجام خواهد شد.
1- ذخیره سازی اطلاعات در حالت offline به کمک IndexedDB :
2- Sync کردن اطلاعات ذخیره شده با سرور بخش اول:
3- Sync کردن اطلاعات ذخیره شده با سرور بخش دوم:
از سال 1970 تا به حال سیستمهای مدیریت پایگاه داده عملیاتی – ODBMS - مختلفی ایجاد شدهاند. بعضی از آنها به مرور زمان از بین رفتهاند و برخی قدرتمندتر شدهاند. در دهههای اخیر بین سیستمهای مدیریت پایگاه داده عملیاتی، محصولات شرکتهای اوراکل، مایکروسافت، IBM و SAP از بقیه موفقتر بودهاند. اما مسلما در این بین بهترین سیستم مدیریت پایگاه داده، محصول شرکت اوراکل بوده است و سخن گزافی نیست که بگوییم محصول شرکت اوراکل در دهههای اخیر در بین محصولات دیگر شرکتها پادشاهی میکرده است .
تا حدود 4 سال پیش بین کیفیت oracle db و sql server اختلاف فاحشی وجود داشت. چه از نظر سرعت و چه از نظر دیگر امکانات، اوراکل کاملا برتر از رقیب خود بود. در نسخهی sql server 2012، امکانات قابل توجهی به محصول شرکت مایکروسافت افزوده شد. از مهمترین این امکانات میتوان به ویژگی AlwaysOn و ColumnStore Indexها اشاره کرد. امکانات این نسخه باعث شد که اختلاف بین oracle db و sql server تا حدی کاهش یابد. مایکروسافت سرانجام در نسخهی sql server 2014 خود تغییرات اساسی بوجود آورد. مهمترین این تغییرات ایجاد موتور درونی In-Memory OLTP میباشد که برای تراکنشهای درون حافظه بهینه شده است. با استفاده از امکانات این نسخه میتوان بدون نیاز به دوباره نویسی محصولات، سرعت اجرای کوئریهای آنها را به طور متوسط ده برابر کرد. در شکل ذیل ساختار جدید sql server مشاهده میشود.
شرکت بوین که یک شرکت مشهور ارائه خدمات آنلاین و پیش بینی بازیهای ورزشی است و در هر لحظه، کاربران آنلاین بسیاری در وب سایت شرکت، کوئری اجرا میکنند، از قابلیتهای جدید اس کیو ال سرور 2014 استفاده کرده است و با استفاده از این قابلیتها توانسته سرعت اجرای پرس و جوهای مشتریانش را از 15 هزار پرس و جو در ثانیه به 250 هزار پرس و جو در ثانیه برساند. در نتیجه کارایی سرور این شرکت 16 برابر شده است.
در تحقیقی دیگر، یک محقق، با استفاده از قابلیتهای جدید اس کیو ال سرور 2014 توانسته است دو رکورد جدید را از اجرای کوئریهای انبار داده ای برای حجمهای 3 ترابایت و 10 ترابایت و نوع پارتیشن بندی نشده به ثبت برساند و رکوردهای قبلی را که متعلق به اوراکل بوده، بشکند. این محقق توانسته 404005 کوئری نسبتا سنگین انبار دادهای را در پایگاه دادهای با 10 ترابایت اطلاعات، در یک ساعت اجرا کند و رکورد قبلی را که متعلق به اوراکل و برابر 377594 کوئری با همین شرایط بوده، بشکند. همچنین هزینهی اجرای کوئریهای سرور اس کیو ال مذکور برابر 2.04 دلار در هر ساعت اجرای کوئری بوده است. به این معنی است که کمتر از نصف هزینهی مشابه در رکورد ثبت شدهی اوراکل که برابر 4.65 دلار در ساعت اجرای کوئری بوده است، هزینه داشته است.
شرکت گارتنر معمولا خلاصهی نتیجهی بررسیهای خود را در نمودارهایی خاص به نام مربع جادویی گارتنر ارائه میکند. در این نمودار، قابلیتهای اجرایی که بیانگر کیفیت فعلی محصول هستند، در محور عمودی نمایش داده میشوند و از پایین به بالا زیاد میشوند. یعنی هر چه محصولی بالاتر باشد، در حال حاضر کیفیت بهتری دارد. محور افقی نمودار بیانگر بصیرت و آینده نگری محصول میباشد و از چپ به راست زیاد میشود. به این ترتیب رهبران یک حوزهی خاص، در ربع بالا و سمت راست مربع جای میگیرند.
حال که با نحوهی تفسیر مربع جادویی گارتنر آشنا شدیم، به بررسی نمودارهای مربوط به سیستمهای مدیریت پایگاه داده عملیاتی در سه سال اخیر میپردازیم.
در شکل ذیل میبینیم که در سال 2013 و پس از ارائهی نسخهی sql server 2012 توسط مایکروسافت، اوراکل همچنان پیشتاز است و شرکتهای مایکروسافت، آی بی ام و SAP پس از آن قرار گرفتهاند. البته در این سال شرکت مایکروسافت فاصلهی زیاد قبلی خود را با اوراکل، کم کرده است.
در سال 2014، شرکت مایکروسافت از نظر آینده نگری و بصیرت، از اوراکل پیشی گرفته ولی هنوز در قابلیتهای اجرایی عقبتر از اوراکل قرار دارد.
اما چند روز پیش در تاریخ 12 اکتبر 2015، شرکت گارتنر گزارشی ارائه کرد که خیلی از فعالان آی تی را شگفت زده کرد. این گزارش در حال حاضر در وب سایت شرکت گارتنر قابل دسترسی است؛ ولی معمولا گارتنر پس از مدتی آن را از حالت رایگان به پولی تغییر میدهد.
لینک موقت گزارش
در گزارش سال 2015 و پس از ارائهی نسخهی sql server 2014 و کاربردی شدن و تست قابلیتهای آن در عمل توسط شرکتهای مختلف، بالاخره طلسم چند ده سالهی اوراکل شکسته شده و اگرچه اوراکل نسبت به سال قبل رشد داشته است، ولی sql server مایکروسافت توانسته، هم در قابلیت اجرای فعلی و هم در بصیرت و آینده نگری بالاتر از محصول شرکت اوراکل بایستد. بنابراین عملا دوران پادشاهی مطلق اوراکل در حوزهی پایگاههای دادهی عملیاتی به سر رسیده است.
- شرکت اوراکل بر خلاف تصور خیلی از افراد، همانند شرکتهای مایکروسافت، آی بی ام و ... محصولات گسترده و مختلفی دارد و این بررسی و نتایج تنها در حوزهی سیستمهای مدیریت پایگاه داده عملیاتی بود.
- بالاتر بودن sql server مایکروسافت از اوراکل در سال 2015 به این معنا نیست که اوراکل نمیتواند به جایگاه قبلی خود برگردد؛ بلکه شاید در سالهای آینده این رتبه بندی باز هم تغییر کند. در واقع این گزارش به این معنا است که فاصلهی زیاد قدیم بین sql server و oracle db از بین رفته و در حال حاضر این دو به رقیب سر سختی برای یکدیگر تبدیل شدهاند.
- وجود رقابت نزدیک بین شرکتهای بزرگ باعث میشود که این شرکتها حداکثر تلاش خود را برای بهتر کردن محصولات خود انجام بدهند و برندگان اصلی این وضعیت، استفاده کنندگان از این محصولات هستند.
- بنده به عنوان نگارندهی این پست شخصا با هر دو محصول oracle db و sql server کار میکنم و تلاش کردم که این پست بی طرفانه باشد؛ پس لطفا متعصبانه قضاوت نکنید.
تا حدود 4 سال پیش بین کیفیت oracle db و sql server اختلاف فاحشی وجود داشت. چه از نظر سرعت و چه از نظر دیگر امکانات، اوراکل کاملا برتر از رقیب خود بود. در نسخهی sql server 2012، امکانات قابل توجهی به محصول شرکت مایکروسافت افزوده شد. از مهمترین این امکانات میتوان به ویژگی AlwaysOn و ColumnStore Indexها اشاره کرد. امکانات این نسخه باعث شد که اختلاف بین oracle db و sql server تا حدی کاهش یابد. مایکروسافت سرانجام در نسخهی sql server 2014 خود تغییرات اساسی بوجود آورد. مهمترین این تغییرات ایجاد موتور درونی In-Memory OLTP میباشد که برای تراکنشهای درون حافظه بهینه شده است. با استفاده از امکانات این نسخه میتوان بدون نیاز به دوباره نویسی محصولات، سرعت اجرای کوئریهای آنها را به طور متوسط ده برابر کرد. در شکل ذیل ساختار جدید sql server مشاهده میشود.
شرکت بوین که یک شرکت مشهور ارائه خدمات آنلاین و پیش بینی بازیهای ورزشی است و در هر لحظه، کاربران آنلاین بسیاری در وب سایت شرکت، کوئری اجرا میکنند، از قابلیتهای جدید اس کیو ال سرور 2014 استفاده کرده است و با استفاده از این قابلیتها توانسته سرعت اجرای پرس و جوهای مشتریانش را از 15 هزار پرس و جو در ثانیه به 250 هزار پرس و جو در ثانیه برساند. در نتیجه کارایی سرور این شرکت 16 برابر شده است.
در تحقیقی دیگر، یک محقق، با استفاده از قابلیتهای جدید اس کیو ال سرور 2014 توانسته است دو رکورد جدید را از اجرای کوئریهای انبار داده ای برای حجمهای 3 ترابایت و 10 ترابایت و نوع پارتیشن بندی نشده به ثبت برساند و رکوردهای قبلی را که متعلق به اوراکل بوده، بشکند. این محقق توانسته 404005 کوئری نسبتا سنگین انبار دادهای را در پایگاه دادهای با 10 ترابایت اطلاعات، در یک ساعت اجرا کند و رکورد قبلی را که متعلق به اوراکل و برابر 377594 کوئری با همین شرایط بوده، بشکند. همچنین هزینهی اجرای کوئریهای سرور اس کیو ال مذکور برابر 2.04 دلار در هر ساعت اجرای کوئری بوده است. به این معنی است که کمتر از نصف هزینهی مشابه در رکورد ثبت شدهی اوراکل که برابر 4.65 دلار در ساعت اجرای کوئری بوده است، هزینه داشته است.
در واقع اگر بخواهیم سیستمهای مدیریت پایگاه داده عملیاتی را رتبه بندی کنیم، به جز سرعت، باید عوامل مختلفی را در نظر بگیریم که چنین کاری نیاز به همکاری گروهی بزرگ دارد. خوشبختانه چنین گروههایی وجود دارند و آن قدر معتبر هستند که اکثر شرکتهای بزرگ به آمارهای آنها استناد میکنند. در فناوریهای مربوط به آی تی، برای رسیدن به معتبرترین نتایج باید به گزارشهای ارائه شدهی شرکت گارتنر رجوع کنیم. گارتنر، شرکت پژوهشی و مشاورهی آمریکایی است، که در زمینهی ارائه خدمات برونسپاری، تحقیق و پژوهش و مشاوره فناوری اطلاعات فعالیت مینماید. این شرکت در سال 1979 راهاندازی شد و در سال 2014 بیش از 6500 نفر کارمند داشته که در 85 کشور بودهاند. در این بین حدود 1500 نفر از آنها در بخش تحقیق و توسعه فعالیت داشتهاند. همچنین در این سال، درآمد شرکت گارتنر که عمدتا از طریق مشاوره دادن به شرکتهای مختلف بوده، بیش از 2 میلیارد دلار در سال 2014 بوده است.
شرکت گارتنر معمولا خلاصهی نتیجهی بررسیهای خود را در نمودارهایی خاص به نام مربع جادویی گارتنر ارائه میکند. در این نمودار، قابلیتهای اجرایی که بیانگر کیفیت فعلی محصول هستند، در محور عمودی نمایش داده میشوند و از پایین به بالا زیاد میشوند. یعنی هر چه محصولی بالاتر باشد، در حال حاضر کیفیت بهتری دارد. محور افقی نمودار بیانگر بصیرت و آینده نگری محصول میباشد و از چپ به راست زیاد میشود. به این ترتیب رهبران یک حوزهی خاص، در ربع بالا و سمت راست مربع جای میگیرند.
حال که با نحوهی تفسیر مربع جادویی گارتنر آشنا شدیم، به بررسی نمودارهای مربوط به سیستمهای مدیریت پایگاه داده عملیاتی در سه سال اخیر میپردازیم.
در شکل ذیل میبینیم که در سال 2013 و پس از ارائهی نسخهی sql server 2012 توسط مایکروسافت، اوراکل همچنان پیشتاز است و شرکتهای مایکروسافت، آی بی ام و SAP پس از آن قرار گرفتهاند. البته در این سال شرکت مایکروسافت فاصلهی زیاد قبلی خود را با اوراکل، کم کرده است.
در سال 2014، شرکت مایکروسافت از نظر آینده نگری و بصیرت، از اوراکل پیشی گرفته ولی هنوز در قابلیتهای اجرایی عقبتر از اوراکل قرار دارد.
اما چند روز پیش در تاریخ 12 اکتبر 2015، شرکت گارتنر گزارشی ارائه کرد که خیلی از فعالان آی تی را شگفت زده کرد. این گزارش در حال حاضر در وب سایت شرکت گارتنر قابل دسترسی است؛ ولی معمولا گارتنر پس از مدتی آن را از حالت رایگان به پولی تغییر میدهد.
لینک موقت گزارش
در گزارش سال 2015 و پس از ارائهی نسخهی sql server 2014 و کاربردی شدن و تست قابلیتهای آن در عمل توسط شرکتهای مختلف، بالاخره طلسم چند ده سالهی اوراکل شکسته شده و اگرچه اوراکل نسبت به سال قبل رشد داشته است، ولی sql server مایکروسافت توانسته، هم در قابلیت اجرای فعلی و هم در بصیرت و آینده نگری بالاتر از محصول شرکت اوراکل بایستد. بنابراین عملا دوران پادشاهی مطلق اوراکل در حوزهی پایگاههای دادهی عملیاتی به سر رسیده است.
در انتها لازم میبینم به نکاتی مهم اشاره کنم:
- شرکت اوراکل بر خلاف تصور خیلی از افراد، همانند شرکتهای مایکروسافت، آی بی ام و ... محصولات گسترده و مختلفی دارد و این بررسی و نتایج تنها در حوزهی سیستمهای مدیریت پایگاه داده عملیاتی بود.
- بالاتر بودن sql server مایکروسافت از اوراکل در سال 2015 به این معنا نیست که اوراکل نمیتواند به جایگاه قبلی خود برگردد؛ بلکه شاید در سالهای آینده این رتبه بندی باز هم تغییر کند. در واقع این گزارش به این معنا است که فاصلهی زیاد قدیم بین sql server و oracle db از بین رفته و در حال حاضر این دو به رقیب سر سختی برای یکدیگر تبدیل شدهاند.
- وجود رقابت نزدیک بین شرکتهای بزرگ باعث میشود که این شرکتها حداکثر تلاش خود را برای بهتر کردن محصولات خود انجام بدهند و برندگان اصلی این وضعیت، استفاده کنندگان از این محصولات هستند.
- بنده به عنوان نگارندهی این پست شخصا با هر دو محصول oracle db و sql server کار میکنم و تلاش کردم که این پست بی طرفانه باشد؛ پس لطفا متعصبانه قضاوت نکنید.