نظرات مطالب
برنامه Cppcheck ابزار آنالیز سورس کدهای برنامههای C و CPP جهت یافتن اشتباهات برنامه نویسی، مشکلات امنیتی، نشتی حافظه و امثال آن است. این برنامه رایگان و سورس باز را میتوانید از آدرس زیر دریافت کنید:
در دو نسخهی خط فرمان و همچنین GUI عرضه میشود که نگارش دارای UI آن از QT استفاده میکند. تا به حال 22 باگ موجود در کرنل لینوکس توسط این برنامه کشف و برطرف شده و همچنین در بسیاری از برنامههای سورس باز دیگر نیز مورد استفاده قرار گرفته است.
لیست مواردی را که این برنامه بررسی میکند، در این آدرس قابل مشاهده است.
پیشنیاز کار با C# 8.0
هرچند بسیاری از قابلیتهای C# 8.0 در خود کامپایلر #C پیاده سازی شدهاند، اما برای مثال قابلیتی مانند «پیاده سازی پیشفرض اینترفیسها» نیاز به یک runtime جدید دارد که به همراه NET Core 3.0. ارائه میشود. بنابراین NET Full 4x. شاهد پیاده سازی C# 8.0 نخواهد بود. همچنین یک سری از قابلیتهای C# 8.0 وابستهی به NET Standard 2.1. و netcoreapp3.0 هستند؛ مانند نوعهای جدید System.IAsyncDisposable و یا System.Range. به همین جهت است که برای کار با C# 8.0، حتما نیاز به نصب NET Core 3.0. نیز میباشد و به روز رسانی کامپایلر #C کافی نیست.
چه نگارشهایی از Visual Studio از NET Core 3.0. پشتیبانی میکنند؟
مطابق مستندات رسمی موجود، یک چنین جدولی در مورد نگارشهای مختلف NET Core. و نگارشهای ویژوال استودیوهایی از که از آنها پشتیبانی میکنند، وجود دارد:
بنابراین فقط VS 2019 است که قابلیت پشتیبانی از NET Core 3.0. را دارد. به همین جهت اگر قصد دارید با ویژوال استودیو کار کنید، نصب VS 2019 برای کار با C# 8.0 الزامی است.
فعالسازی C# 8.0 در ویژوال استودیو 2019
در زمان نگارش این مطلب، NET Core 3.0. در حالت پیشنمایش، ارائه شدهاست. به همین جهت جزء یکپارچهی VS 2019 محسوب نشده و باید جداگانه نصب شود:
- برای این منظور ابتدا نیاز است آخرین نگارش NET Core 3.0 SDK. را دریافت و نصب کنید.
- سپس از منوی Tools | Options، گزینهی Projects and Solutions را انتخاب و در ادامه گزینهی Use previews of the .NET Core SDK را انتخاب کنید.
- پس از آن، این SDK جدید NET Core. به صورت زیر قابل انتخاب خواهد بود:
البته انتخاب شماره SDK صحیح به تنهایی برای کار با C# 8.0 کافی نیست؛ بلکه باید شمارهی زبان مورد استفاده را نیز صریحا انتخاب کرد:
برای اینکار بر روی پروژه کلیک راست کرده و گزینهی Properties آنرا انتخاب کنید. سپس در اینجا در برگهی Build، بر روی دکمهی Advanced کلیک کنید تا بتوان شماره نگارش زبان را مطابق تصویر فوق انتخاب کرد. در اینجا بجای C# 8.0 (beta)، گزینهی unsupported preview را نیز میتوانید انتخاب کنید.
یک نکته: خلاصهی تمام این مراحل، منوها و تصاویر، همان تنظیمات فایل csproj است که در ادامه بررسی میکنیم.
فعالسازی C# 8.0 در VSCode
مدتها است که برای کار با NET Core. نیازی به استفادهی از نگارش کامل ویژوال استودیو نیست. همینقدر که VSCode را به همراه افزونهی #C آن نصب کرده باشید، میتوانید برنامههای مبتنی بر NET Core. را بر روی سیستم عاملهای مختلفی که NET Core SDK. بر روی آنها نصب شدهاست، توسعه دهید.
پشتیبانی ابتدایی از C# 8.0، با نگارش v1.18.0 افزونهی #C مخصوص VSCode ارائه شد. بنابراین هم اکنون اگر آخرین نگارش آنرا نصب کرده باشید، امکان کار با پروژههای NET Core 3.0 و C# 8.0 را نیز دارید.
بنابراین در اینجا به صورت خلاصه:
- ابتدا باید NET Core 3.0 SDK. را به صورت جداگانهای دریافت و نصب کنید.
- سپس آخرین نگارش افزونهی #C مخصوص VSCode را نیز نصب کنید.
- در آخر، یک پوشهی جدید را ایجاد کرده و در خط فرمان دستور dotnet new console را صادر کنید. این دستور بر اساس آخرین شماره نگارش SDK نصب شده، یک پروژهی جدید کنسول را ایجاد میکند که ساختار فایل csproj آن به صورت زیر است:
همانطور که مشاهده میکنید، TargetFramework را به آخرین SDK نصب شده، تنظیم کردهاست (معادل دومین تصویر این مطلب). مرحلهی بعد، تنظیم شماره نگارش زبان آن است. برای این منظور یکی از دو حالت زیر را میتوان انتخاب کرد:
- یا معادل همان گزینهی unsupported preview در تصویر سوم این مطلب:
- و یا تعیین صریح شماره نگارش C# 8.0 (beta) به صورت زیر:
یک نکته: در اینجا نمیتوان LangVersion را به latest تنظیم کرد؛ چون C# 8.0 هنوز در مرحلهی بتا است. زمانیکه از مرحلهی بتا خارج شد، مقدار پیشفرض آن دقیقا latest خواهد بود و ذکر صریح آن غیر ضروری است. انتخاب latest در اینجا به latest minor version یا همان نگارش C# 7.3 فعلی (آخرین نگارش پایدار زبان #C در زمان نگارش این مطلب) اشاره میکند.
Rider و پشتیبانی از C# 8.0
Rider 2019.1 نیز به همراه پشتیبانی از C# 8.0 ارائه شدهاست و میتواند گزینهی مطلوب دیگری برای توسعهی برنامههای مبتنی بر NET Core. باشد.
نصب NET Core 3.0 SDK. و عدم اجرای برنامههای پیشین
یکی از مزایای کار با NET Core.، امکان نصب چندین نوع مختلف SDK آن، به موازت هم است؛ بدون اینکه بر روی یکدیگر تاثیری بگذارند. البته این نکته را باید درنظر داشت که برنامههای NET Core. بدون وجود فایل مخصوص global.json در پوشهی ریشهی آنها، همواره از آخرین نگارش SDK نصب شده، برای اجرا استفاده خواهند کرد. اگر این مورد بر روی کار شما تاثیرگذار است، میتوانید شماره SDK مورد استفادهی برنامهی خود را قفل کنید، تا SDKهای جدید نصب شده، به عنوان SDK پیشفرض برنامههای پیشین، انتخاب نشوند. بنابراین ابتدا لیست SDKهای نصب شده را با دستور زیر پیدا کنید:
سپس برای پروژههای قدیمی خود که فعلا قصد به روز رسانی آنها را ندارید، یک فایل global.json را به صورت زیر، در ریشهی پروژه تولید کنید:
در اینجا 2.2.300 یکی از شمارههایی است که توسط دستور dotnet --list-sdks یافتهاید و پروژهی قبلی شما بر اساس آن کار میکند.
هرچند بسیاری از قابلیتهای C# 8.0 در خود کامپایلر #C پیاده سازی شدهاند، اما برای مثال قابلیتی مانند «پیاده سازی پیشفرض اینترفیسها» نیاز به یک runtime جدید دارد که به همراه NET Core 3.0. ارائه میشود. بنابراین NET Full 4x. شاهد پیاده سازی C# 8.0 نخواهد بود. همچنین یک سری از قابلیتهای C# 8.0 وابستهی به NET Standard 2.1. و netcoreapp3.0 هستند؛ مانند نوعهای جدید System.IAsyncDisposable و یا System.Range. به همین جهت است که برای کار با C# 8.0، حتما نیاز به نصب NET Core 3.0. نیز میباشد و به روز رسانی کامپایلر #C کافی نیست.
چه نگارشهایی از Visual Studio از NET Core 3.0. پشتیبانی میکنند؟
مطابق مستندات رسمی موجود، یک چنین جدولی در مورد نگارشهای مختلف NET Core. و نگارشهای ویژوال استودیوهایی از که از آنها پشتیبانی میکنند، وجود دارد:
.NET Core SDK | .NET Core Runtime | Compatible Visual Studio | MSBuild | Notes |
---|---|---|---|---|
2.1.5nn | 2.1 | 2017 | 15 | Installed as part of VS 2017 version 15.9 |
2.1.6nn | 2.1 | 2019 | 16 | Installed as part of VS 2019 |
2.2.1nn | 2.2 | 2017 | 15 | Installed manually |
2.2.2nn | 2.2 | 2019 | 16 | Installed as part of VS 2019 |
3.0.1nn | 3.0 (Preview) | 2019 | 16 | Installed manually |
بنابراین فقط VS 2019 است که قابلیت پشتیبانی از NET Core 3.0. را دارد. به همین جهت اگر قصد دارید با ویژوال استودیو کار کنید، نصب VS 2019 برای کار با C# 8.0 الزامی است.
فعالسازی C# 8.0 در ویژوال استودیو 2019
در زمان نگارش این مطلب، NET Core 3.0. در حالت پیشنمایش، ارائه شدهاست. به همین جهت جزء یکپارچهی VS 2019 محسوب نشده و باید جداگانه نصب شود:
- برای این منظور ابتدا نیاز است آخرین نگارش NET Core 3.0 SDK. را دریافت و نصب کنید.
- سپس از منوی Tools | Options، گزینهی Projects and Solutions را انتخاب و در ادامه گزینهی Use previews of the .NET Core SDK را انتخاب کنید.
- پس از آن، این SDK جدید NET Core. به صورت زیر قابل انتخاب خواهد بود:
البته انتخاب شماره SDK صحیح به تنهایی برای کار با C# 8.0 کافی نیست؛ بلکه باید شمارهی زبان مورد استفاده را نیز صریحا انتخاب کرد:
برای اینکار بر روی پروژه کلیک راست کرده و گزینهی Properties آنرا انتخاب کنید. سپس در اینجا در برگهی Build، بر روی دکمهی Advanced کلیک کنید تا بتوان شماره نگارش زبان را مطابق تصویر فوق انتخاب کرد. در اینجا بجای C# 8.0 (beta)، گزینهی unsupported preview را نیز میتوانید انتخاب کنید.
یک نکته: خلاصهی تمام این مراحل، منوها و تصاویر، همان تنظیمات فایل csproj است که در ادامه بررسی میکنیم.
فعالسازی C# 8.0 در VSCode
مدتها است که برای کار با NET Core. نیازی به استفادهی از نگارش کامل ویژوال استودیو نیست. همینقدر که VSCode را به همراه افزونهی #C آن نصب کرده باشید، میتوانید برنامههای مبتنی بر NET Core. را بر روی سیستم عاملهای مختلفی که NET Core SDK. بر روی آنها نصب شدهاست، توسعه دهید.
پشتیبانی ابتدایی از C# 8.0، با نگارش v1.18.0 افزونهی #C مخصوص VSCode ارائه شد. بنابراین هم اکنون اگر آخرین نگارش آنرا نصب کرده باشید، امکان کار با پروژههای NET Core 3.0 و C# 8.0 را نیز دارید.
بنابراین در اینجا به صورت خلاصه:
- ابتدا باید NET Core 3.0 SDK. را به صورت جداگانهای دریافت و نصب کنید.
- سپس آخرین نگارش افزونهی #C مخصوص VSCode را نیز نصب کنید.
- در آخر، یک پوشهی جدید را ایجاد کرده و در خط فرمان دستور dotnet new console را صادر کنید. این دستور بر اساس آخرین شماره نگارش SDK نصب شده، یک پروژهی جدید کنسول را ایجاد میکند که ساختار فایل csproj آن به صورت زیر است:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> </Project>
- یا معادل همان گزینهی unsupported preview در تصویر سوم این مطلب:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> <LangVersion>preview</LangVersion> </PropertyGroup> </Project>
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> <LangVersion>8.0</LangVersion> </PropertyGroup> </Project>
یک نکته: در اینجا نمیتوان LangVersion را به latest تنظیم کرد؛ چون C# 8.0 هنوز در مرحلهی بتا است. زمانیکه از مرحلهی بتا خارج شد، مقدار پیشفرض آن دقیقا latest خواهد بود و ذکر صریح آن غیر ضروری است. انتخاب latest در اینجا به latest minor version یا همان نگارش C# 7.3 فعلی (آخرین نگارش پایدار زبان #C در زمان نگارش این مطلب) اشاره میکند.
Rider و پشتیبانی از C# 8.0
Rider 2019.1 نیز به همراه پشتیبانی از C# 8.0 ارائه شدهاست و میتواند گزینهی مطلوب دیگری برای توسعهی برنامههای مبتنی بر NET Core. باشد.
نصب NET Core 3.0 SDK. و عدم اجرای برنامههای پیشین
یکی از مزایای کار با NET Core.، امکان نصب چندین نوع مختلف SDK آن، به موازت هم است؛ بدون اینکه بر روی یکدیگر تاثیری بگذارند. البته این نکته را باید درنظر داشت که برنامههای NET Core. بدون وجود فایل مخصوص global.json در پوشهی ریشهی آنها، همواره از آخرین نگارش SDK نصب شده، برای اجرا استفاده خواهند کرد. اگر این مورد بر روی کار شما تاثیرگذار است، میتوانید شماره SDK مورد استفادهی برنامهی خود را قفل کنید، تا SDKهای جدید نصب شده، به عنوان SDK پیشفرض برنامههای پیشین، انتخاب نشوند. بنابراین ابتدا لیست SDKهای نصب شده را با دستور زیر پیدا کنید:
> dotnet --list-sdks
> dotnet new globaljson --sdk-version 2.2.300 > type global.json
نظرات مطالب
انتشار VS2010
من دات نت فریم ورک 4 رو حذف و بعد از ری استارت دوباره نصب کردم ولی باز هم موقع نصب خطا داد و نصب نشد!؟ من حتی نسخه اکسپرس رو هم نتونستم نصب کنم!
دموی نسبتا مفصلی از تواناییهای دات نت فریم ورک 4 و VS2010 را که توسط تیم مربوطه در مورد پردازش موازی تهیه شده است، از آدرس زیر میتوانید دریافت نمائید.
IdentityServer با سایر زبانها هم کار میکند؛ چون یک پروتکل استاندارد را پیاده سازی کردهاست و این پروتکل برای انواع و اقسام زبانهای موجود دارای کلاینت است: کلاینت و مثال جاوا، کلاینت پایتون و سایر کلاینتهای تائید شده و یا اگر مخزن کد آنرا بررسی کنید هر آنچه که نگارش 3
دارد و الان آرشیو شده و دیگر پشتیبانی نمیشود، مختص به MVC 5x است. اما
چون نگارش 3 و یا 4 و یا n آن، پروتکل استاندارد OpenID Connect را پیاده
سازی میکنند، یک کلاینت MVC 5x و یا یک کلاینت PHP هم میتواند از آن استفاده کند. یعنی همان مثالهای MVC 5x آن با نگارش 4 هم کار میکنند.
- لیست زبانهای برنامه نویسی محبوب در ماه آوریل 2009؛ سی شارپ یک پله صعود نسبت به سال قبل، زبان برنامه نویسی MATLAB مجددا وارد لیست شده
- اولین محصول نگارش 2010 مایکروسافت برای دریافت، exchange server 2010 beta (میل سرور)
سایر اولین سری محصولات 2010 ، شامل Microsoft Office 2010 ، Microsoft Visio 2010 و Microsoft Project 2010 خواهند بود.
- مجموعه انتشارات مایکروسافت 25 امین سالگرد تاسیس خود را جشن میگیرد. به همین منظور دو کتاب زیر را به رایگان برای دریافت قرار دادهاند:
عنوانی رو که براش انتخاب کردن جالب است "the world's most advanced open source database"
در سایت جاری از ویرایشگر آنلاین RedActor استفاده شده و کار کردن با آن هم بسیار ساده است:
یک TextArea ساده را به صفحه اضافه کرده و این افزونه جیکوئری را بر روی آن اجرا میکنید. به این ترتیب TextArea به صورت خودکار تبدیل به یک ویرایشگر مطلوب خواهد شد. برای مثال:
اما فعال سازی قسمت ارسال فایل و تصویر همراه با آن چطور؟
یک سری مثال نوشته شده با PHP به همراه این ویرایشگر آنلاین هستند که برای ایده گرفتن بد نیستند (البته به این معنا نیست که این ویرایشگر نیازی به PHP دارد. تنها قسمت سمت سرور مثالهای آن با PHP است). برای مثال اگر در PHP از دستور echo برای ارائه یک نتیجه نهایی به ویرایشگر RedActor استفاده شده، معادل آن در ASP.NET MVC مساوی return Content است.
برای فعال سازی قسمت آپلود این ادیتور نیاز است پارامترهای imageUpload، fileUpload و linkFileUpload مقدار دهی شوند.
همانطور که ملاحظه میکنید از T4MVC برای مشخص سازی مسیرها استفاده شده. برای مثال MVC.RedactorUpload.ImageUpload به این معنا است که در کنترلری به نام RedactorUpload، اکشن متدی به نام ImageUpload پذیرای ارسال فایل ادیتور خواهد بود و به همین ترتیب در مورد سایر پارامترها.
RedactorUploadController هم ساختار بسیار سادهای دارد. برای مثال هر کدام از متدهای آپلود یاد شده یک چنین امضایی دارند:
البته در مورد مسایل امنیتی آپلود هم پیشتر در سایت بحث شده است. برای مثال در اینجا استفاده از فیلتر زیر را فراموش نکنید:
در هر کدام از متدهای آپلود (به سه متد برای سه پارامتر یاد شده نیاز است)، ابتدا HttpPostedFileBase را در پوشهایی که مدنظر دارید ذخیره کنید. سپس باید محتوایی را به RedActor بازگشت دهید و اصل کار یکپارچگی با ASP.NET MVC نیز در همینجا است:
در حالت imageUpload، محتوایی به شکل زیر باید بازگشت داده شود:
در حالت fileUpload، پس از ذخیره سازی فایل در سرور، مسیر آن باید به نحو زیر بازگشت داده شود:
و در حالت linkFileUpload فقط باید مسیر نهایی فایل ذخیره شده بر روی سرور را بازگشت دهید:
همچنین باید دقت داشت که کار ارسال فایل به سرور توسط خود افزونه RedActor انجام میشود و نیازی به کدنویسی ندارد. فقط باید سمت سرور آنرا به نحوی که عنوان شد مدیریت کنید. ابتدا فایل را در سرور ذخیره کنید. سپس باید یک محتوای رشتهای را به نحو یاد شده، ساخت و توسط return Content بازگشت داد.
پ.ن.
قسمتی از مطالب متن فوق در نگارش جدید این ویرایشگر به نحو زیر تغییر کرده است.
یک TextArea ساده را به صفحه اضافه کرده و این افزونه جیکوئری را بر روی آن اجرا میکنید. به این ترتیب TextArea به صورت خودکار تبدیل به یک ویرایشگر مطلوب خواهد شد. برای مثال:
@Html.TextAreaFor(model => model.ArticleBody, htmlAttributes: new { style = "width:98%; height:500px" }) <script type="text/javascript"> $('#ArticleBody').redactor({ autoformat: false, convertDivs: false }); </script>
یک سری مثال نوشته شده با PHP به همراه این ویرایشگر آنلاین هستند که برای ایده گرفتن بد نیستند (البته به این معنا نیست که این ویرایشگر نیازی به PHP دارد. تنها قسمت سمت سرور مثالهای آن با PHP است). برای مثال اگر در PHP از دستور echo برای ارائه یک نتیجه نهایی به ویرایشگر RedActor استفاده شده، معادل آن در ASP.NET MVC مساوی return Content است.
<script type="text/javascript"> $('#ArticleBody').redactor({ imageUpload: "@Url.Action(result: MVC.RedactorUpload.ImageUpload())", fileUpload: "@Url.Action(result: MVC.RedactorUpload.FileUpload())", linkFileUpload: "@Url.Action(result: MVC.RedactorUpload.FileLinkUpload())" , autoformat: false , convertDivs: false }); </script>
همانطور که ملاحظه میکنید از T4MVC برای مشخص سازی مسیرها استفاده شده. برای مثال MVC.RedactorUpload.ImageUpload به این معنا است که در کنترلری به نام RedactorUpload، اکشن متدی به نام ImageUpload پذیرای ارسال فایل ادیتور خواهد بود و به همین ترتیب در مورد سایر پارامترها.
RedactorUploadController هم ساختار بسیار سادهای دارد. برای مثال هر کدام از متدهای آپلود یاد شده یک چنین امضایی دارند:
[HttpPost] public virtual ActionResult ImageUpload(HttpPostedFileBase file) { }
[AllowUploadSpecialFilesOnly(".jpg,.gif,.png")]
در حالت imageUpload، محتوایی به شکل زیر باید بازگشت داده شود:
return Content("<img src='" + path + "' />");
return Content("<a href=" + path + ">" + someName + "</a>");
return Content(path);
همچنین باید دقت داشت که کار ارسال فایل به سرور توسط خود افزونه RedActor انجام میشود و نیازی به کدنویسی ندارد. فقط باید سمت سرور آنرا به نحوی که عنوان شد مدیریت کنید. ابتدا فایل را در سرور ذخیره کنید. سپس باید یک محتوای رشتهای را به نحو یاد شده، ساخت و توسط return Content بازگشت داد.
پ.ن.
قسمتی از مطالب متن فوق در نگارش جدید این ویرایشگر به نحو زیر تغییر کرده است.
یکی از مزایای کار با ORMها، امکان تعویض نوع بانک اطلاعاتی برنامه، بدون نیازی به تغییری در کدهای برنامه است. برای مثال فرض کنید میخواهید با تغییر رشتهی اتصالی برنامه، یکبار از بانک اطلاعاتی SQL Server و بار دیگر از بانک اطلاعاتی کاملا متفاوتی مانند SQLite استفاده کنید. در این مطلب نکات استفادهی از چندین نوع بانک اطلاعاتی متفاوت را در برنامههای مبتنی بر EF Core بررسی خواهیم کرد.
هر بانک اطلاعاتی باید Migration و Context خاص خودش را داشته باشد
تامین کنندهی بانکهای اطلاعاتی مختلف، عموما تنظیمات خاص خودشان را داشته و همچنین دستورات SQL متفاوتی را نیز تولید میکنند. به همین جهت نمیتوان از یک تک Context، هم برای SQLite و هم SQL Server استفاده کرد. به علاوه قصد داریم اطلاعات Migrations هر کدام را نیز در یک اسمبلی جداگانه قرار دهیم. در یک چنین حالتی EF نمیپذیرد که Context تولید کنندهی Migration، در اسمبلی دیگری قرار داشته باشد و باید حتما در همان اسمبلی Migration قرار گیرد. بنابراین ساختار پوشه بندی مثال جاری به صورت زیر خواهد بود:
- در پوشهی EFCoreMultipleDb.DataLayer فقط اینترفیس IUnitOfWork را قرار میدهیم. از این جهت که وقتی قرار شد در برنامه چندین Context تعریف شوند، لایهی سرویس برنامه قرار نیست بداند در حال حاضر با کدام Context کار میکند. به همین جهت است که تغییر بانک اطلاعاتی برنامه، تغییری را در کدهای اصلی آن ایجاد نخواهد کرد.
- در پوشهی EFCoreMultipleDb.DataLayer.SQLite کدهای Context و همچنین IDesignTimeDbContextFactory مخصوص SQLite را قرار میدهیم.
- در پوشهی EFCoreMultipleDb.DataLayer.SQLServer کدهای Context و همچنین IDesignTimeDbContextFactory مخصوص SQL Server را قرار میدهیم.
برای نمونه ابتدای Context مخصوص SQLite چنین شکلی را دارد:
و IDesignTimeDbContextFactory مخصوص آن که برای Migrations از آن استفاده میشود، به صورت زیر تهیه خواهد شد:
هدف از این فایل، ساده سازی کار تولید اطلاعات Migrations برای EF Core است. به این صورت ساخت new SQLiteDbContext توسط ما صورت خواهد گرفت و دیگر EF Core درگیر جزئیات وهله سازی آن نمیشود.
تنظیمات رشتههای اتصالی بانکهای اطلاعاتی مختلف
در اینجا محتویات فایل appsettings.json را که در آن تنظیمات رشتههای اتصالی دو بانک SQL Server LocalDB و همچنین SQLite در آن ذکر شدهاند، مشاهده میکنید:
همین رشتهی اتصالی است که در SQLiteDbContextFactory مورد استفاده قرار میگیرد.
یک کلید InUseKey را هم در اینجا تعریف کردهایم تا مشخص باشد در ابتدای کار برنامه، کلید کدام رشتهی اتصالی مورد استفاده قرار گیرد. برای مثال در اینجا کلید رشتهی اتصالی SQLite تنظیم شدهاست.
در این تنظیمات یک DataDirectory را نیز مشاهده میکنید. مقدار آن در فایل Startup.cs برنامه به صورت زیر بر اساس پوشهی جاری تعیین میشود و در نهایت به wwwroot\app_data اشاره خواهد کرد:
دستورات تولید Migrations و به روز رسانی بانک اطلاعاتی
چون تعداد Contextهای برنامه بیش از یک مورد شدهاست، دستورات متداولی را که تاکنون برای تولید Migrations و یا به روز رسانی ساختار بانک اطلاعاتی اجرا میکردید، با پیام خطایی که این مساله را گوشزد میکند، متوقف خواهند شد. راه حل آن ذکر صریح Context مدنظر است:
برای تولید Migrations، از طریق خط فرمان، به پوشهی اسمبلی مدنظر وارد شده و دستور زیر را اجرا کنید:
در اینجا ذکر startup-project و همچنین context برای پروژههایی که context آنها خارج از startup-project است و همچنین بیش از یک context دارند، ضروریاست. بدیهی است این دستورات را باید یکبار در پوشهی EFCoreMultipleDb.DataLayer.SQLite و یکبار در پوشهی EFCoreMultipleDb.DataLayer.SQLServer اجرا کنید.
دو سطر اول آن، زمان اجرای دستورات را به عنوان نام فایلها تولید میکنند.
پس از تولید Migrations، اکنون نوبت به تولید بانک اطلاعاتی و یا به روز رسانی بانک اطلاعاتی موجود است:
در این مورد نیز ذکر startup-project و همچنین context مدنظر ضروری است.
بدیهی است این رویه را پس از هربار تغییراتی در موجودیتهای برنامه و یا تنظیمات آنها در Contextهای متناظر، نیاز است مجددا اجرا کنید. البته اجرای اولین دستور اجباری است؛ اما میتوان دومین دستور را به صورت زیر نیز اجرا کرد:
متد applyPendingMigrations، کار وهله سازی IUnitOfWork را انجام میدهد. سپس متد Migrate آنرا اجرا میکند، تا تمام Migrations تولید شده، اما اعمال نشدهی به بانک اطلاعاتی، به صورت خودکار به آن اعمال شوند. متد Migrate نیز به صورت زیر تعریف میشود:
مرحلهی آخر: انتخاب بانک اطلاعاتی در برنامهی آغازین
پس از این تنظیمات، قسمتی که کار تعریف IUnitOfWork و همچنین DbContext جاری برنامه را انجام میدهد، به صورت زیر پیاده سازی میشود:
در اینجا ابتدا مقدار InUseKey از فایل تنظیمات برنامه دریافت میشود. بر اساس مقدار آن، رشتهی اتصالی مدنظر دریافت شده و سپس یکی از دو حالت SQLite و یا SQLServer انتخاب میشوند. برای مثال اگر Sqlite انتخاب شده باشد، IUnitOfWork به SQLiteDbContext تنظیم میشود. به این ترتیب لایهی سرویس برنامه که با IUnitOfWork کار میکند، به صورت خودکار وهلهای از SQLiteDbContext را دریافت خواهد کرد.
آزمایش برنامه
ابتدا کدهای کامل این مطلب را از اینجا دریافت کنید: EFCoreMultipleDb.zip
سپس آنرا اجرا نمائید. چنین تصویری را مشاهده خواهید کرد:
اکنون برنامه را بسته و سپس فایل appsettings.json را جهت تغییر مقدار InUseKey به کلید SqlServerConnection ویرایش کنید:
اینبار اگر مجددا برنامه را اجرا کنید، چنین خروجی قابل مشاهدهاست:
مقدار username، در contextهای هر کدام از این بانکهای اطلاعاتی، با مقدار متفاوتی به عنوان اطلاعات اولیهی آن ثبت شدهاست. سرویسی هم که اطلاعات آنرا تامین میکند، به صورت زیر تعریف شدهاست:
همانطور که مشاهده میکنید، با تغییر context برنامه، هیچ نیازی به تغییر کدهای UsersService نیست؛ چون اساسا این سرویس نمیداند که IUnitOfWork چگونه تامین میشود.
هر بانک اطلاعاتی باید Migration و Context خاص خودش را داشته باشد
تامین کنندهی بانکهای اطلاعاتی مختلف، عموما تنظیمات خاص خودشان را داشته و همچنین دستورات SQL متفاوتی را نیز تولید میکنند. به همین جهت نمیتوان از یک تک Context، هم برای SQLite و هم SQL Server استفاده کرد. به علاوه قصد داریم اطلاعات Migrations هر کدام را نیز در یک اسمبلی جداگانه قرار دهیم. در یک چنین حالتی EF نمیپذیرد که Context تولید کنندهی Migration، در اسمبلی دیگری قرار داشته باشد و باید حتما در همان اسمبلی Migration قرار گیرد. بنابراین ساختار پوشه بندی مثال جاری به صورت زیر خواهد بود:
- در پوشهی EFCoreMultipleDb.DataLayer فقط اینترفیس IUnitOfWork را قرار میدهیم. از این جهت که وقتی قرار شد در برنامه چندین Context تعریف شوند، لایهی سرویس برنامه قرار نیست بداند در حال حاضر با کدام Context کار میکند. به همین جهت است که تغییر بانک اطلاعاتی برنامه، تغییری را در کدهای اصلی آن ایجاد نخواهد کرد.
- در پوشهی EFCoreMultipleDb.DataLayer.SQLite کدهای Context و همچنین IDesignTimeDbContextFactory مخصوص SQLite را قرار میدهیم.
- در پوشهی EFCoreMultipleDb.DataLayer.SQLServer کدهای Context و همچنین IDesignTimeDbContextFactory مخصوص SQL Server را قرار میدهیم.
برای نمونه ابتدای Context مخصوص SQLite چنین شکلی را دارد:
public class SQLiteDbContext : DbContext, IUnitOfWork { public SQLiteDbContext(DbContextOptions options) : base(options) { } public virtual DbSet<User> Users { set; get; }
namespace EFCoreMultipleDb.DataLayer.SQLite.Context { public class SQLiteDbContextFactory : IDesignTimeDbContextFactory<SQLiteDbContext> { public SQLiteDbContext CreateDbContext(string[] args) { var basePath = Directory.GetCurrentDirectory(); Console.WriteLine($"Using `{basePath}` as the BasePath"); var configuration = new ConfigurationBuilder() .SetBasePath(basePath) .AddJsonFile("appsettings.json") .Build(); var builder = new DbContextOptionsBuilder<SQLiteDbContext>(); var connectionString = configuration.GetConnectionString("SqliteConnection") .Replace("|DataDirectory|", Path.Combine(basePath, "wwwroot", "app_data")); builder.UseSqlite(connectionString); return new SQLiteDbContext(builder.Options); } } }
تنظیمات رشتههای اتصالی بانکهای اطلاعاتی مختلف
در اینجا محتویات فایل appsettings.json را که در آن تنظیمات رشتههای اتصالی دو بانک SQL Server LocalDB و همچنین SQLite در آن ذکر شدهاند، مشاهده میکنید:
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "SqlServerConnection": "Data Source=(LocalDB)\\MSSQLLocalDB;Initial Catalog=ASPNETCoreSqlDB;AttachDbFilename=|DataDirectory|\\ASPNETCoreSqlDB.mdf;Integrated Security=True;MultipleActiveResultSets=True;", "SqliteConnection": "Data Source=|DataDirectory|\\ASPNETCoreSqliteDB.sqlite", "InUseKey": "SqliteConnection" } }
یک کلید InUseKey را هم در اینجا تعریف کردهایم تا مشخص باشد در ابتدای کار برنامه، کلید کدام رشتهی اتصالی مورد استفاده قرار گیرد. برای مثال در اینجا کلید رشتهی اتصالی SQLite تنظیم شدهاست.
در این تنظیمات یک DataDirectory را نیز مشاهده میکنید. مقدار آن در فایل Startup.cs برنامه به صورت زیر بر اساس پوشهی جاری تعیین میشود و در نهایت به wwwroot\app_data اشاره خواهد کرد:
var connectionStringKey = Configuration.GetConnectionString("InUseKey"); var connectionString = Configuration.GetConnectionString(connectionStringKey) .Replace("|DataDirectory|", Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "app_data"));
دستورات تولید Migrations و به روز رسانی بانک اطلاعاتی
چون تعداد Contextهای برنامه بیش از یک مورد شدهاست، دستورات متداولی را که تاکنون برای تولید Migrations و یا به روز رسانی ساختار بانک اطلاعاتی اجرا میکردید، با پیام خطایی که این مساله را گوشزد میکند، متوقف خواهند شد. راه حل آن ذکر صریح Context مدنظر است:
برای تولید Migrations، از طریق خط فرمان، به پوشهی اسمبلی مدنظر وارد شده و دستور زیر را اجرا کنید:
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c_%%a_%%b) For /f "tokens=1-2 delims=/:" %%a in ("%TIME: =0%") do (set mytime=%%a%%b) dotnet build dotnet ef migrations --startup-project ../EFCoreMultipleDb.Web/ add V%mydate%_%mytime% --context SQLiteDbContext
دو سطر اول آن، زمان اجرای دستورات را به عنوان نام فایلها تولید میکنند.
پس از تولید Migrations، اکنون نوبت به تولید بانک اطلاعاتی و یا به روز رسانی بانک اطلاعاتی موجود است:
dotnet build dotnet ef --startup-project ../EFCoreMultipleDb.Web/ database update --context SQLServerDbContext
بدیهی است این رویه را پس از هربار تغییراتی در موجودیتهای برنامه و یا تنظیمات آنها در Contextهای متناظر، نیاز است مجددا اجرا کنید. البته اجرای اولین دستور اجباری است؛ اما میتوان دومین دستور را به صورت زیر نیز اجرا کرد:
namespace EFCoreMultipleDb.Web { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { applyPendingMigrations(app); // ... } private static void applyPendingMigrations(IApplicationBuilder app) { var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>(); using (var scope = scopeFactory.CreateScope()) { var uow = scope.ServiceProvider.GetService<IUnitOfWork>(); uow.Migrate(); } } } }
namespace EFCoreMultipleDb.DataLayer.SQLite.Context { public class SQLiteDbContext : DbContext, IUnitOfWork { // ... public void Migrate() { this.Database.Migrate(); } } }
مرحلهی آخر: انتخاب بانک اطلاعاتی در برنامهی آغازین
پس از این تنظیمات، قسمتی که کار تعریف IUnitOfWork و همچنین DbContext جاری برنامه را انجام میدهد، به صورت زیر پیاده سازی میشود:
namespace EFCoreMultipleDb.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddScoped<IUsersService, UsersService>(); var connectionStringKey = Configuration.GetConnectionString("InUseKey"); var connectionString = Configuration.GetConnectionString(connectionStringKey) .Replace("|DataDirectory|", Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "app_data")); switch (connectionStringKey) { case "SqlServerConnection": services.AddScoped<IUnitOfWork, SQLServerDbContext>(); services.AddDbContext<SQLServerDbContext>(options => { options.UseSqlServer( connectionString, dbOptions => { var minutes = (int)TimeSpan.FromMinutes(3).TotalSeconds; dbOptions.CommandTimeout(minutes); dbOptions.EnableRetryOnFailure(); }); }); break; case "SqliteConnection": services.AddScoped<IUnitOfWork, SQLiteDbContext>(); services.AddDbContext<SQLiteDbContext>(options => { options.UseSqlite( connectionString, dbOptions => { var minutes = (int)TimeSpan.FromMinutes(3).TotalSeconds; dbOptions.CommandTimeout(minutes); }); }); break; default: throw new NotImplementedException($"`{connectionStringKey}` is not defined in `appsettings.json` file."); } services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
آزمایش برنامه
ابتدا کدهای کامل این مطلب را از اینجا دریافت کنید: EFCoreMultipleDb.zip
سپس آنرا اجرا نمائید. چنین تصویری را مشاهده خواهید کرد:
اکنون برنامه را بسته و سپس فایل appsettings.json را جهت تغییر مقدار InUseKey به کلید SqlServerConnection ویرایش کنید:
{ "ConnectionStrings": { // … "InUseKey": "SqlServerConnection" } }
مقدار username، در contextهای هر کدام از این بانکهای اطلاعاتی، با مقدار متفاوتی به عنوان اطلاعات اولیهی آن ثبت شدهاست. سرویسی هم که اطلاعات آنرا تامین میکند، به صورت زیر تعریف شدهاست:
namespace EFCoreMultipleDb.Services { public interface IUsersService { Task<User> FindUserAsync(int userId); } public class UsersService : IUsersService { private readonly IUnitOfWork _uow; private readonly DbSet<User> _users; public UsersService(IUnitOfWork uow) { _uow = uow; _users = _uow.Set<User>(); } public Task<User> FindUserAsync(int userId) { return _users.FindAsync(userId); } } }
با استفاده از مونو امکان اجرای برنامههای دات نت تحت لینوکس وجود دارند. در ادامه سعی خواهیم کرد تا نگارش 3 آنرا بر روی اوبونتو نصب کنیم. مونو 3 تا دات نت 4 و نیم را پشتیبانی میکند.
دریافت اوبونتو
برای دریافت اوبونتو به آدرس ذیل مراجعه نمائید.
نسخه سرور آن GUI ندارد (هرچند بعدا در طی یک بسته 450 مگابایتی قابل نصب است). نسخه دسکتاپ آن به همراه GUI نیز هست. البته برای نصب دات نت بر روی آن این مساله تفاوتی نمیکند. برای نصب آزمایشی و مجازی آن هم میتوانید برای مثال از VMWare workstation استفاده کنید؛ بدون اینکه نیاز داشته باشید این توزیع خاص لینوکس را واقعا بر روی کامپیوتر خود نصب کنید.
در تمام قسمتهای ذیل فرض بر این است که ترمینال خط فرمان لینوکس را گشودهاید و همچنین سیستم به اینترنت وصل است.
دریافت Git و Curl
ابتدا دستور زیر را در خط فرمان لینوکس اجرا کنید تا سیستم بستههای لینوکس به روز شده و همچنین یک سری پیشنیاز مانند git ، curl و امثال آن نصب شوند (کتابخانه curl جهت استفاده در محیطهای برنامه نویسی کاربرد دارد و همچنین برنامه پیشرفتهای است برای کار با وب و دریافت فایلها):
نصب آخرین نگارش Mono و وابستگیهای آن
در ادامه نوبت به نصب آخرین نگارش مونو است. از روش متداول ذیل برای نصب مونو استفاده نکنید :
این دستور نگارش 2.10.8.1 را تا این تاریخ بر روی سیستم شما نصب خواهد کرد و اگر پیشتر مونو را به این روش نصب کردهاید، با استفاده از دستور ذیل آنرا حذف کنید:
برای دسترسی به آخرین نگارش نگارش مونو، نیاز است آنرا از روی سورس آن کامپایل کنیم. اسکریپت کامل نصب آنرا در آدرس ذیل میتوانید پیدا کنید:
و یا اگر آدرس فوق برقرار نبود از اینجا: install_mono-3.0-sh
برای نمونه جهت نصب mono نگارش 3 از اسکریپت install_mono-3.0.sh به نحو ذیل استفاده خواهیم کرد (این دستورات را به ترتیب در ترمینال لینوکس اجرا کنید):
این پروسه مدتی طول خواهد کشید (تا تمام بستههای لازم از اینترنت دریافت شوند). استفاده از اسکریپت فوق کار را بسیار ساده کرده و بسیاری از مراحل لازم نصب مونو را یکجا در خود به همراه دارد. مونو 3 تا دات نت 4 و نیم را پشتیبانی میکند.
بعد از اجرای فرمان فوق به خطای ذیل خواهید رسید:
این مورد مشکلی است که در نگارش 3.0.10 رخ داده و فراموش کردهاند که یک پوشه را کپی کنند (در نگارشهای قبلی آن این مشکل وجود نداشته و با توجه به آگاه شدن از آن، در نگارشهای بعدی نیز نباید مشکلی باشد).
برای رفع آن ابتدا به مسیر ذیل وارد شوید (پوشه build/mono-3.0.10/po)، فایل mcs را حذف (این مورد در اصل یک پوشه است و نه یک فایل) و سپس بسته اصلی mono را از github دریافت کنید. آنرا unzip کرده و کل پوشه mcs داخل آنرا به درون پوشه po جاری کپی کنید. سپس فایل zip دریافت شده را حذف کنید:
البته برای اینکه وقت شما زیاد تلف نشود، پوشه mcs نگارش 3.0.10 را از آدرس ذیل دریافت و پس از unzip درون پوشه mono-3.0/build/mono-3.0.10/po کپی کنید. (6 سطر فوق هم نیازی به اجرا ندارند)
پس از باز سازی پوشه مفقود mcs، باید مرحله «building mono packages» موجود در فایل install_mono-3.0.sh اجرا شود. برای این منظور، فایل final-build-mono-3.0.sh را از آدرس ذیل دریافت و در کنار فایل install_mono-3.0.sh موجود کپی کنید.
سپس در خواص این فایل، مجوز execute را نیز فعال نمائید. اکنون آنرا اجرا کنید:
فایل final-build-mono-3.0.sh در حقیقت همان فایل install_mono-3.0.sh اصلی است. با این تفاوت که قسمت ابتدای فایل که در آن وابستگیهای لازم از اینترنت دریافت میشدند، حذف شده است. چون پیشتر اینکار را انجام داده بودیم (با اجرای اولیه آن).
اکنون مدتی صبر کنید تا کار کامپایل نهایی تمام بستههای دریافت شده پس از اجرای اسکریپت final-build-mono-3.0.sh انجام شود.
آزمایش Mono نصب شده
برای اینکه مطمئن شویم، Mono درست نصب شده است، دستور زیر را در خط فرمان صادر کنید:
برای اینکه این مسیر را به Path لینوکس اضافه کنیم تا قادر شویم فرمان mono را در هر مسیری اجرا کنیم، ابتدا دستور ذیل را اجرا کرده
و سپس در ادیتور باز شده، مسیر و عبارات ذیل را به انتهای مقدار جاری اضافه کنید:
بعد ctrl+x را زده، به پیام ذخیره سازی تغییرات پاسخ مثبت دهید. سپس نیاز است یکبار logoff و login کنید تا این تغییرات اعمال شوند.
یک نکته تکمیلی:
اگر به صفحه نگارشهای رسمی Mono 3.x مراجعه کنید، نگارشهای جدیدتری را نیز میتوانید ملاحظه کنید. فایلهای قابل نصب آنها نیر در آدرسهای ذیل قرار دارند:
برای استفاده از اسکریپت install_mono-3.0.sh با این نگارشهای جدیدتر فقط کافی است تعاریف ذیل را بر اساس شماره نگارش بستههای جدید اصلاح کنید:
دریافت اوبونتو
برای دریافت اوبونتو به آدرس ذیل مراجعه نمائید.
نسخه سرور آن GUI ندارد (هرچند بعدا در طی یک بسته 450 مگابایتی قابل نصب است). نسخه دسکتاپ آن به همراه GUI نیز هست. البته برای نصب دات نت بر روی آن این مساله تفاوتی نمیکند. برای نصب آزمایشی و مجازی آن هم میتوانید برای مثال از VMWare workstation استفاده کنید؛ بدون اینکه نیاز داشته باشید این توزیع خاص لینوکس را واقعا بر روی کامپیوتر خود نصب کنید.
در تمام قسمتهای ذیل فرض بر این است که ترمینال خط فرمان لینوکس را گشودهاید و همچنین سیستم به اینترنت وصل است.
دریافت Git و Curl
ابتدا دستور زیر را در خط فرمان لینوکس اجرا کنید تا سیستم بستههای لینوکس به روز شده و همچنین یک سری پیشنیاز مانند git ، curl و امثال آن نصب شوند (کتابخانه curl جهت استفاده در محیطهای برنامه نویسی کاربرد دارد و همچنین برنامه پیشرفتهای است برای کار با وب و دریافت فایلها):
sudo apt-get update && sudo apt-get -y install git-core curl python-software-properties sudo apt-get install build-essential automake checkinstall intltool git
نصب آخرین نگارش Mono و وابستگیهای آن
در ادامه نوبت به نصب آخرین نگارش مونو است. از روش متداول ذیل برای نصب مونو استفاده نکنید :
sudo apt-get install mono-complete
sudo apt-get purge mono-runtime
و یا اگر آدرس فوق برقرار نبود از اینجا: install_mono-3.0-sh
برای نمونه جهت نصب mono نگارش 3 از اسکریپت install_mono-3.0.sh به نحو ذیل استفاده خواهیم کرد (این دستورات را به ترتیب در ترمینال لینوکس اجرا کنید):
mkdir mono-3.0 cd mono-3.0 wget --no-check-certificate https://github.com/nathanb/iws-snippets/raw/master/mono-install-scripts/ubuntu/install_mono-3.0.sh chmod 755 install_mono-3.0.sh ./install_mono-3.0.sh
بعد از اجرای فرمان فوق به خطای ذیل خواهید رسید:
config.status: error: cannot find input file: `po/mcs/Makefile.in.in'
برای رفع آن ابتدا به مسیر ذیل وارد شوید (پوشه build/mono-3.0.10/po)، فایل mcs را حذف (این مورد در اصل یک پوشه است و نه یک فایل) و سپس بسته اصلی mono را از github دریافت کنید. آنرا unzip کرده و کل پوشه mcs داخل آنرا به درون پوشه po جاری کپی کنید. سپس فایل zip دریافت شده را حذف کنید:
cd mono-3.0/build/mono-3.0.10/po rm mcs wget https://github.com/mono/mono/archive/master.zip unzip master.zip mv mcs/ mono-3.0/build/mono-3.0.10/po rm -rf mono-master master.zip
پس از باز سازی پوشه مفقود mcs، باید مرحله «building mono packages» موجود در فایل install_mono-3.0.sh اجرا شود. برای این منظور، فایل final-build-mono-3.0.sh را از آدرس ذیل دریافت و در کنار فایل install_mono-3.0.sh موجود کپی کنید.
سپس در خواص این فایل، مجوز execute را نیز فعال نمائید. اکنون آنرا اجرا کنید:
./final-build-mono-3.0.sh
اکنون مدتی صبر کنید تا کار کامپایل نهایی تمام بستههای دریافت شده پس از اجرای اسکریپت final-build-mono-3.0.sh انجام شود.
آزمایش Mono نصب شده
برای اینکه مطمئن شویم، Mono درست نصب شده است، دستور زیر را در خط فرمان صادر کنید:
/opt/mono-3.0/bin/mono -V
برای اینکه این مسیر را به Path لینوکس اضافه کنیم تا قادر شویم فرمان mono را در هر مسیری اجرا کنیم، ابتدا دستور ذیل را اجرا کرده
sudo nano /etc/environment
:/opt/mono-3.0/bin
بعد ctrl+x را زده، به پیام ذخیره سازی تغییرات پاسخ مثبت دهید. سپس نیاز است یکبار logoff و login کنید تا این تغییرات اعمال شوند.
یک نکته تکمیلی:
اگر به صفحه نگارشهای رسمی Mono 3.x مراجعه کنید، نگارشهای جدیدتری را نیز میتوانید ملاحظه کنید. فایلهای قابل نصب آنها نیر در آدرسهای ذیل قرار دارند:
برای استفاده از اسکریپت install_mono-3.0.sh با این نگارشهای جدیدتر فقط کافی است تعاریف ذیل را بر اساس شماره نگارش بستههای جدید اصلاح کنید:
PACKAGES=("mono-3.0.10" "libgdiplus-2.10.9" "gtk-sharp-2.12.11" "xsp-2.10.2" "mod_mono-2.10") URLS=("http://download.mono-project.com/sources/mono/mono-3.0.10.tar.bz2" "http://download.mono-project.com/sources/libgdiplus/libgdiplus-2.10.9.tar.bz2" "http://download.mono-project.com/sources/gtk-sharp212/gtk-sharp-2.12.11.tar.bz2" "http://download.mono-project.com/sources/xsp/xsp-2.10.2.tar.bz2" "http://download.mono-project.com/sources/mod_mono/mod_mono-2.10.tar.bz2")