یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

پروژه نمونه بررسی قابلیتهای Google MAP API و تلفیق آن با MVC

     در این پروژه مشخصا امکان استفاده از Google Map API در MVC بهانه ای برای کسب تجربه بیشتر و استفاده از تکنیک های این تکنولوژی در سناریو های مرتبط با GIS می باشد .
در مرحله اول به هیچ وجه از Spatial Data و مخصوصا امکانات استثنایی SQL در محاسبات GIS استفاده نشده و هر آنچه در این پروژه ساده استفاده شده فقط به کار گیری بخشی از تواناییهای مفاهیمی مثل Layout ، View و Partial View و تلفیق JQuery UI و بهره گیری از Google Javascript API V 3.0 و AJAX و JSON می باشد .
به دلیل حفظ ساختار اصلی Template پروژه استاندارد، ابتدا یک Layout مستقل به نام _AppLayout.cshtml ایجاد شده که در فولدر View/Shared قرار گرفته است .
این Layout دارای یک ساختار ساده 2 ستونی Right To Left می باشد که پنل سمت راست در واقع یک Div استاندارد با float متمایل به راست می باشد که اندازه طول (Width) آن 156 پیکسل در نظر گرفته شده است .
پنل سمت راست به طور مشابه دارای یک float متمایل به چپ بوده که به دلیل حفظ فاصله مناسب از سمت راست (Padding-right) دارای طولی (Width) به اندازه 170 پیکسل می باشد .
پنل های Header و Footer هم به سادگی در Layout قرار دارند .
پنل سمت راست در Layout به صورت یک Section ایجاد شده است که وظیفه Rendering و محتویات آن به صورت اختیاری :
 

<div id="rightPanel" >
     @RenderSection("RightPanel", required: false)
</div>

در View میهمان (منظور آن View که از Layout استفاده میکند) تامین میشود که این وظیفه را index.cshtml انجام داده است . برای تامین محتویات پنل سمت راست index.cshtml از یک Partial View ساده به نام _PartialRightMapMenu استفاده کرده است که هدف از این کار (Reusable Controls) استفاده مستقل از این Partial View در بخشهای مختلف برنامه بوده است .
این Partial View شامل یک کنترل استاندارد Accordion در JQuery می باشد که از آن به عنوان منوی اصلی برنامه استفاده شده است . گزینه های هر Category از منو به سادگی با یک Button معمولی ایجاد شده اند .
به دلیل بهره گیری از امکانات آخرین نسخه JQuery من تصمیم گرفتم که از نسخه 1.10.2 که در زمان تهیه این برنامه آخرین نسخه آن بوده است استفاده کنم . رفرنس به اسکریپت های مورد نیاز از طریق تکنیک Bundle در MVC انجام شده است که جزییات آن در فایل BundleConfig قابل مشاهده است .
همچنین برای استفاده از Google API درج یک رفرنس به کتابخانه اسکریپت توابع گوگل الزامیست که این کار در داخل index.cshtml انجام شده که این View، نقش میزبان اصلی سناریو های مرتبط با نقشه را بازی میکند .
علاوه بر رفرنس به کتابخانه اصلی اسکریپت های Javascript در Google به دلیل استفاده از سرویس ها و نقشه های وضعیت آب و هوا رفرنس دومی هم به اسکریپت مورد نیاز در همان محل قرار داده شده است .
همچنین کلیه توابع مورد نیاز ما در JQuery و Javascript به طور مستقل در فایل mvcgoogle.js و در فولدر Scripts قرار داده شده است . تقریبا همه عملیات برنامه از داخل این فایل کنترل شده و Event های مربوط به انتخاب گزینه های سمت راست از طریق این فایل Handle می شوند . برای استفاده از این فایل مجددا در BundleConfig.cs آن را به همراه سایر اسکریپت های مورد نیاز برنامه Bundle و بسته بندی کرده ام و در index.cshtml یک رفرنس برای آن در نظر گرفته ام.
در داخل این فایل از استراتژی استفاده از یک متغیر map سراسری و Global استفاده شده است . با توجه به پیش بینی حداقل 30 گزینه در منوی سمت راست و پرهیز از پاسکاری خسته کننده آبجکت map بین توابع فعلا این راهکار مورد استفاده قرار گرفته شده است . مگر آنکه در آینده و در گسترش های بعدی تغییراتی در آن ایجاد گردد .
در اولین نسخه و اولین ارائه این برنامه تعداد 13 سناریوی مختلف بررسی و پیاده سازی شده است که متناسب با هریک از آنها، یک گزینه در منوی سمت راست تدارک دیده شده است . از همان ابتدای فکر طراحی و اجرای این برنامه در نظر داشتم که تنوع زیادی در تکنیک های مرتبط با نقشه استفاده کنم .
مثالها از نمایش یک نقشه ساده شروع شده و انتخاب یک نقطه خاص (در این مثال برج میلاد) و در ادامه امکان ترسیم دلخواه (Custom Style Rendering) در نقشه ها نشان داده شده است . همچنین در گزینه weather استفاده از سرویس وضعیت آب و هوایی و رندر کردن آن روی نقشه توسط Google API نشان داده شده است . همچنین در گزینه "ماهواره – کلیک" نقشه گوگل به صورت Hybrid ترسیم شده است و با هر کلیک بر روی نقشه مختصات محل کلیک شده در نوار Footer انتهای برنامه منعکس خواهد شد . در مجموع جزییات دیگری هم در هر یک از مثالها وجود دارد که توصیه میکنم در هنگام اجرا با دقت به نوع عملیاتی که انجام میشود توجه نمایید .
در این برنامه نمونه و البته ساده، به نظر من یک چارچوب مناسب برای گسترش های آینده ایجاد شده و زمینه برای انجام سناریو های بسیار پیچیده تر نظیر استفاده از داده های جغرافیایی (Sql Spatial Data) در SQL و بهره گیری از معماری SOA با استفاده از WCF مهیا شده است . در Category نام گذاری شده با فرمت KML اختصاصا امکان استفاده از فرمت بسیار مهم KML در Google بررسی شده است . در این باره مطالب زیادی وجود دارد که من توضیح مفصل تر در این مورد را به آینده موکول میکنم و فقط لازم است اشاره کنم که هر 4 گزینه این قسمت اطلاعات خود را از سایت http://earthquake.usgs.gov/ دریافت میکنند که اطلاعات بسیار به روز و طبقه بندی شده را در ارتباط با آمار آخرین زلزله ها حداقل در دو فرمت JSON و KML ارائه میدهد . امکان استفاده از فرمت KML در Google یکی از مزایای استثنایی این API محسوب شده و امکانات موجود در این زمینه بسیار متنوع و جالب می باشند .
تنها محدودیت در این زمینه این است که فایلهای KML باید در یک Domain مستقل و Online قرار داشته باشند و امکان استفاده از فایلها به صورت local وجود ندارد .
تکته بسیار مهمی که باید برای استفاده از این پروژه در نظر داشته باشیداین است که حتما مشخصات ConnectionString را در web.config مطابق با وضعیت سیستم خود تغییر داده و اقدام به استفاده از برنامه نمایید .
در Category سوم استفاده از اطلاعات Dynamic مورد توجه قرار گرفته شده است . برای این کار من یک Database نمونه به نام DBGisSample و به صورت Backup ضمیمه برنامه کرده ام که با SQL Server Express 2012 ایجاد شده و در حال حاضر شامل دو جدول مستقل است . یکی از جدول ها دارای چند رکورد مرتبط با مختصات شهرهای مهم ایران است . دومین جدول مجددا مربوط به اطلاعات زلزله ها ی شدید به تفکیک سال و شدت و زمان آنها می بالشد که فقط توجه به یک نکته بسیار مهم در اینجا ضروریست .
در این جداول به هیچ وجه از امکانات Spatial Data و به طور مشخص نوع داده های Geometry و Geography در Sql استفاده نشده است . هر چند جدول earthquake دارای چنین اطلاعاتی می باشد اما اگر با دقت به هر دو Table نگاه کنیم ، هر کدام از ایk جداول دارای دو ستون مستقل به ترتیب برای طول و عرض جغرافیایی هستند که برنامه ما ابتدا با استفاده از EF اطلاعات این جداول را دریافت کرده و سپس Controller مورد نظر ما به نام Home در MVC آبجکت های موجود در Model را با توجه به اطلاعات دریافت شده از SQL ایجاد کرده و یک لیست Generic از این اطلاعات در فرمت JSON توسط Action ها Serialize شده و دو فراخوانی مستقل و جداگانه از متد $.getJSON در داخل mcvgoogle.js بدون نیاز به Postback و توسط Ajax اطلاعات مورد نیاز ما را به View مورد نظر ما یعنی index منتقل میکند .
بعد از این کار، فراخوانی متدهای مرتبط و مورد نیاز در Google API عملیات ترسیم و Rendering نقاط را انجام داده و هر نقطه دارای یک Tooltip اختصاصی است که سایر اطلاعات و فیلدهای هر رکورد را به این ترتیب نمایش میدهد . بعد ها از infoWindow در Google API برای همین کار استفاده خواهیم کرد که بسیار روش مناسب تری می باشد .
همچنین باید اشاره کنم که در گزینه دوم مربوط به این قسمت، هر چند کار انجام شده بسیار شبیه به گزینه اول می باشد اما تفاوت اندکی وجود دارد :

اولین نکته این است که در گزینه دوم متد Action مورد نظر یعنی SQLEarthQuake دو پارامتر دریافت میکند که من از آن برای محدود کردن تعداد رکورد های دریافتی استفاده کرده ام . در جدول Earthquakes در حدود 5351 رکورد مختلف وجود دارد که متد Action ما با ایجاد یک فیلتر ساده فقط رکورد های مربوط به سال 1960 تا 2013 را دریافت میکند که تعداد رکورد ها تقریبا به نصف کاهش می یابد . پس این Action اولا امکان انتقال پارامتر به متد Action را نشان داده ثانیا بوضوح نشان میدهد که این روش از چه کارآیی و قدرت بالایی برخوردار است که حدود 2500 رکورد را در زمان بسیار کوتاهی بدون Postback دریافت کرده و Google Api به محض دریافت آنها، به ازای هر نقطه یک Pushpin روی نقشه ترسیم میکند .

جزییات انجام این عملیات همه در mvcgoogle.js قابل مطالعه است .
به نظرم می رسد که جزییات زیادی برای مطرح شدن وجود دارد که فعلا به دلیل کوتاه شدن این مطلب از آنها پرهیز میکنم .

لینک دریافت سورس کد پروژه به همراه SQL Data Backup :
https://onedrive.live.com/redir?resid=FC40347DCF4AAF1D%211252

امیدوارم از نظرات و پیشنهادات خود مرا مطلع نمایید .

۱ نظر موافقین ۰ مخالفین ۰ ۰۹ خرداد ۹۳ ، ۲۲:۴۳
مهران حسین نیا
پنجشنبه, ۱ خرداد ۱۳۹۳، ۰۳:۴۱ ب.ظ

گزارش یک تجربـه در MVC

ماجرا از آنجایی آغاز شد که در نظر داشتم سناریویی که در برنامه RouthingDemo قبلا در Windows 8 Store App انجام شده بود را در MVC بازسازی کرده و علاوه بر فرمت GPX ، امکان استفاده از API مشخصی که آن را به منظور کار با Shape File ها و Platform Independent طراحی کرده بودم استفاده کنم. کد کامل این API را در اولین فرصت به همراه توضیحات لازم ارائه خواهم کرد .
 در مجموع هدف من Rendering اطلاعات فرمت های اصلی File-based در GIS یعنی KML ، Shape و Gpx حداقل بر روی سه Base Map متفاوت یعنی استفاده از Provider های Bing Map و Google Map و OpenStreet Map می باشد .
تقریبا هر سه Provider مجهز به API های اختصاصی خود هستند که به طور مشترک در Client-side قابل استفاده بوده و امکان Render نمودن یک Base Map و در ادامه امکان ترسیم Geometry های اصلی در GIS یعنی Point ، Line و Polygon را فراهم نموده و عملیات رایجی مثل Panning و Zooming را میسر میسازند .
در سناریوی نهایی به نظرم رسید که خلاص شدن از شر فرمت های File-based تنها در صورت وجود یک Converter و انتقال اطلاعات موجود در آنها به SQL  در قالب Spatial Data بهترین راهکار موجود است که قبل از پرداختن به جزییات موجود در این راه  اولین نکته روش برقراری ارتباط بین MVC (در دیدگاه کلی Server-side)  و JQuery (اشاره به امکان استفاده از API هر یک از Provider های نقشه )  می باشد که در اینجا گزارشی از تجربه موفقیت آمیزی که با استفاده از AJAX و فرمت JSON  داشته ام ، ارائه شده است .

کلاسی که در زیر ایجاد شده تقریبا منطبق بر اطلاعات موجود در فرمت GPX می باشد .
 

    public class GISRoute

    {

        public GISRoute()

        {

        }

        public double Longitude

        {

            get;

            set;

        }

        public double Latitude

        {

            get;

            set;

        }

        public int Altitude

        {

            get;

            set;

        }

        public DateTime TimePoint

        {

            get;

            set;

        }

 

    }
 

در ادامه کلاس دسترسی به فرمت فایلهای GPX و Encapsulation اطلاعات آن در کلاس GISRoute ایجاد کردم که چندان بهینه نیست اما در ساده ترین شکل خود با ایجاد یک لیست جنریک از کلاس GISRoute اطلاعات موجود در فرمت GPX را در کد زیر، با استفاده از LINQ To XML در لیست Gerneric قرار داده و لیست مذکور جهت استفاده در Controller آماده میشود :

    public class GPXReader

    {

        List<GISRoute> gisrouteList;

        public GPXReader()

        {

            Test tst = new Test();

            gisrouteList = new List<GISRoute>();

            LoadGPXFile();

        }

        public GPXReader(string path)

        {

        }

        public List<GISRoute> GisRouteList

        {

            get

            {

                return this.gisrouteList;

            }

        }

        public void LoadGPXFile()

        {

 

            XDocument xmlFile = XDocument.Load(".....\\Projects\\MvcMap\\MvcMap\\App_Data\\sample.gpx");

            XNamespace xns = "http://www.topografix.com/GPX/1/0/";

            foreach (var element in xmlFile.Root.Elements("trkpt"))

            {

                double lat = double.Parse(element.FirstAttribute.Value);

                double lon = double.Parse(element.LastAttribute.Value);

                int alt = Int32.Parse(element.Element("ele").Value);

                DateTime time = DateTime.Parse(element.Element("time").Value);

                string str = element.Value;

                gisrouteList.Add(new GISRoute

                {

                    Altitude = alt,

                    Latitude = lat,

                    Longitude = lon,

                    TimePoint = time

                });

            }       

        }

    }

 

اینک Controller صرفا با Instantiate کردن کلاس GPXReader می تواند به محض دریافت یک Request از نوع Get ،اطلاعات موجود در یک مسیر ثبت شده در فرمت GPX را در فرمت JSON به صورت اتوماتیک Serialize کرده و آن را به یک View دلخواه (حتی نوع Strongly Typed View)   هدایت نماید .
برای ساده تر شدن موضوع، من از همان View استاندارد Index.cshtml استفاده کردم. کد Action مورد نظر من با نام MyMethod به صورت زیر در کنترلر Home ایجاد شد .

 

  public class HomeController : Controller

    {

        public ActionResult Index()

        {

            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

 

            return View();

        }

 

        public ActionResult About()

        {

            ViewBag.Message = "Your app description page.";

            GPXReader gpxReader = new GPXReader();

            return View(gpxReader.GisRouteList);

        }

 

        public ActionResult Contact()

        {

            ViewBag.Message = "Your contact page.";

 

            return View();

        }

        [HttpGet]

        public ActionResult MyMethod(int keyid, int newval)

        {           

GPXReader gpxReader = new GPXReader();

            ViewBag.Message = keyid.ToString() + " " + newval.ToString();

            return Json(new { smylist = gpxReader.GisRouteList },

                        JsonRequestBehavior.AllowGet);

        }

    }

 

چنانچه در کد بوضوح مشاهده میشود با استفاده از Annotation و درست در ابتدای متد، نوع درخواستی که متد MyMethod قادر به Handle کردن آن میباشد Get تعیین شده است . همچنین دو پارامتر موجود در این متد صرفا برای بررسی امکان ارسال پارامتر به این متد به کار رفته و در سناریوی فرضی من از این دو پارامتر استفاده نشده است . به عبارت ساده تر با استفاده از JQuery چنانچه در ادامه خواهیم دید امکان ارسال هر تعداد پارامتر مورد نیاز با هر نوع دلخواه به Action تعیین شده در یک Controller میسر خواهد بود که تواناییهای بالقوه این روش استثنایی را آشکار میکند .

برای آزمایش این روش ابتدا در View میزبان از چند المان HTML ساده به صورت زیر استفاده شده است :

 

        <div>

            <button id="btnStart"JSON Send</button>

            <div id="divReturnedData"> </div>

            <ol id="list">

            </ol>

        </div>

 

واضح است که از Button به عنوان یک Trigger برای آغاز این عملیات استفاده شده و برای نمایش خروجی حاصل از احضار متد می توان از divReturnedData و یا list استفاده کرد که جزییات این کار در ادامه شرح داده شده است .

در ادامه و در مرحله اول عملیات Client-side برای رویداد Click در  Button مورد نظر  مطابق زیر یک Event Handler مشخص می کنیم که در آن بوضوح مشخص شده است که به محض لود شدن کلیه عناصر DOM (با توجه به اینکه معمولا Script ما در Header قرار میگیرد و عناصر DOM و تگ های HTML در Body قرار دارند ، این یک روش مناسب و استاندارد  برای اطمینان از لود شدن کل المانهای DOM می باشد) فراخوانی متد GetJSONWithAjax به محض کلیک بر روی Button تعیین شده با btnStart انجام شود . کد متد GetJSONWithAjax در ادامه ارائه شده است .

 

   <script type="text/javascript">

       $(document).ready(function() {

           $("#btnStart").click(GetJSONWithAjax);

       });

    </script>

 

من در ابتدا با استفاده از متد $.ajax درخواستی از نوع Get به Action و Controller ارسال کرده ام که واضح است در آغاز این فراخوانی نوع درخواست get تعیین شده و فرمت اطلاعات دریافتی json تعیین شده است . همچنین ویژگی Caching در اینجا Disable شده و با استفاده از Helper بسیار مفید و کاربردی Url.Action و توسط Razor ابتدا نام Action یعنی MyMethod و سپس نام Controller یعنی Home تعیین شده است .
چنانچه قبلا اشاره شد متد ما در کنترلر دارای دو پارامتر است که با وجود اینکه ما از آن استفاده نکرده ایم اما برای نمایش امکان ارسال پارامتر به همراه Request مقادیر فرضی 1 و 10 را در پارامترها  مقدار دهی کرده و یک Handler از نوع Anonymous   (منظور function بدون نامی است که دارای یک پارامتر بوده و در حقیقت حاوی اطلاعات ارسال شده از Controller می باشد . به عبارت ساده تر ما از این function به به عنوان Callback در هنگام موفقیت آمیز بودن عملیات استفاده کرده ایم) تعیین کرده و در داخل این Callback (توضیح آنکه Callback اشاره به نوع خاصی از Function ها در عملیات Asynchronous می باشد که در مراحل مختلف این عملیات فراخوانی شده و امکان کنترل کامل در کل این فرآیند را میسر میسازند) ابتدا با دریافت تعداد عناصر لیست یا آرایه از صحت دریافت آنها مطمئن می شویم. هر چند در عمل نیازی هم به انجام این کار نیست و فراخوانی این Callback خود دلیل خوبی برای موفقیت آمیز بودن این عملیات است . از divReturnedData هم به عنوان یک Placeholder برای نمایش تعداد عناصر لیست/آرایه استفاده کرده ام . تردید من در استفاده از واژه لیست و یا  آرایه به این دلیل است که مجموعه ای که به عوان Data منتقل شده است در Controller به صورت یک لیست Generic تدارک دیده شده بود و اینجا آن را به صورت یک آرایه در JQuery دریافت کرده ایم .
به محض دریافت آرایه حالا JQuery دارای مجموعه ای از متد ها و توابع بسیار قدرتمند و کارآمدیست که انواع پردازش های دلخواه را بر روی عناصر آرایه فراهم میکند . این ابزارهای بی نظیر حتی بدون نیاز به یک loop سنتی می توانند کلیه نیازهای موجود در زمینه مرور و Traverse کردن عناصر آرایه و پردازش دلخواه بر روی تک تک اعضای آرایه را فراهم کنند . از مهمترین این توابع میتوان به each ، map و grep اشاره کرد که مخصوصا در مورد آخر با تلفیق با Regular Expression  یک ابزار فوق العاده کارآمد محسوب میشود . من با استفاده از متد each در JQuery کار را به ترتیبی که در کد ملاحظه میکنید ادامه داده ام . این متد در پارامتر اول ، خود آرایه را دریافت کرده و در پارامتر دوم امکان مشخص نمودن یک Callback ،  که به ازای هر عنصری از آرایه فراخوانی میشود را میسر می سازد . مجددا من از یک تابع Anonymous استفاده کرده ام و فقط یک Property در آرایه یعنی Longitude (برای توضیح به کد کلاس GISoute مراجعه نمایید) را در یک تگ Ordered List در HTML نمایش داده ام . توضیح اینکه متد append هر بار به تعداد اعضای آرایه یک node از نوع li به تگ OL اضافه میکند .
متد $.ajax چنانچه مشاهده میکنید امکان تعیین یک Callback در شرایطی که انجام عملیات به هر دلیلی با مشکلی مواجه شود را فراهم میکند .

 

 

           function GetJSONWithAjax() {

               $.ajax({

                   type: 'get',

                   dataType: 'json',

                   cache: false,

                   url: '@Url.Action("MyMethod","Home")',

  data: { keyid: 1, newval: 10 },

  success: function (response) {

      var arrayCount = response.mylist.length;

      var memlist = $("#list");

      $("#divReturnedData").html("<b>" + arrayCount + "</b>);

      $.each(response.mylist, function (index, value) {

          memlist.append($("<li>" + value.Longitude + "</li>"));

      });  },

  error: function (jqXHR, textStatus, errorThrown) {

      //alert('Error ' + errorThrown);

  }

        });

           }

 

با توجه به این نکته بسیار مهم که فرمت اطلاعات دریافت شده در اینجا از نوع JSON می باشد ، به نظر میرسد استفاده از متد $.getJSON نیز علاوه بر امکان رسیدن به نتیجه مشابه ، این کار را با تلاش کمتری میسر میسازد . برای استفاده از آن می توان از کد زیر استفاده کرد که توصیه میکنم به مشابهت های آن با روش قبلی و مزیت آن توجه نمایید .

 

    function GetJSON()

           {

        var params = { keyid: 1, newval: 10 };

        $.getJSON('@Url.Action("MyMethod","Home")',

            params,

            function (response) {

                  var arint = response.mylist.length;

                  var memlist = $("#list");

                  $("#divReturnedData").html(arint + " <b>" + response.mydata + "</b> " + response.oldval + " " + "<hr/> " + response.mylist[0].Longitude);

                  $.each(response.mylist,function( index, value ){

                                  memlist.append($( "<li>" + value.Longitude + "</li>" ));

                  });

        });

 

با توجه به توضیحاتی که قبلا داده شد به نظر میرسد هیچ نقطه مبهمی در این فراخوانی وجود ندارد .
با استفاده از Developer Tools در Google Chrome مطلع شدم که کل این درخواست و دریافت اطلاعات در مدت 550 میلی ثانیه انجام شده اما اگر با دقت به تصویری که ضمیمه شدهاستتوجه کنیم ملاحظه خواهیم کرد که ارسال اطلاعات و زمان مورد نیاز Response فقط 1.5 میلی ثانیه بوده است که برای حدود 1000 رکورد از نوع GISRoute زمان بسیار خوب و نشان دهنده سرعت بالای این روش می باشد . بر طبق گزارش Developer Tools سیستم به مدت 547 میلی ثانیه توقف داشته است که با بهینه سازی کد و روشها میتوان این مقدار را تا یک سوم مقدار فعلی کاهش داد .




 امیدوارم توضیح این تجربه، برای دوستان و همکاران محترم مفید بوده و این عزیزان از  نظرات و پیشنهادهای خود مرا بی نصیب نگذارند .

 

۰ نظر موافقین ۰ مخالفین ۰ ۰۱ خرداد ۹۳ ، ۱۵:۴۱
مهران حسین نیا
پنجشنبه, ۱۸ ارديبهشت ۱۳۹۳، ۰۷:۱۰ ب.ظ

معرفی کتاب Professional ASP .NET MVC 4 ویرایش چهارم، انتشارات APress

      کتاب Professional ASP .NET MVC 4 را می توان به جرأت یکی از بهترین ها در زمینه آشنایی حرفه ای با تکنولوژی MVC معرفی نمود.
این کتاب مشتمل بر 738 صفحه و شامل دو قسمت (Part) می باشد که مجموعا در 26 فصل جداگانه تدوین شده است. آدام فریمن (Adam Freeman) نیازی به معرفی ندارد و سابقه درخشان او مخصوصا در اتشارات APress خود گویای این نکته است که تیراژ بسیار بالای آثار او به هیچ وجه اتفاقی نیست . تسلط عمیق و نگاه دقیق و ریز بینانه این برنامه نویس نویسنده از همان ابتدا خواننده را مجاب میکند که باید سطر به سطر توضیحات او را دقیقا مطالعه کرده و مخصوصا در مورد مثالها با صرف وقت مناسب نکات مورد نظر نویسنده را تشخیص دهد.



تصویر روی جلد کتاب
 


سطح کتاب چنانچه از نام آن نیز مشخص است کاملا حرفه ای بوده و مخصوصا از آغاز بخش دوم، که ابتدای فصل 12 نیز محسوب میشود، بوضوح سطح مطالب افزایش چشمگیری داشته و بعد از تشریح مراحل طراحی و تدوین یک Application نمونه ،(که در قسمت اول انجام شده) نویسنده ظرافت های منحصر بفرد این تکنولوژی را با دقت توصیف نموده و با مثالهای بسیار مهم، خواننده را با تکنیکهای آن آشنا می نماید .

با مطالعه دقیق این کتاب، نه تنها با دیدگاه های یکی از خبرگان مطرح این تکنولوژی آشنا خواهیم شد بلکه روش های مطالعه صحیح و راه های گسترش کیفیت و کمیت تکنیکهای مطرح شده در کتاب را نیز فرا خواهیم گرفت .
باید اعتراف کنم که انگیزه اصلی من از این معرفی نوعی ادای دین به این نویسنده توانا بوده و دوستان و همکاران محترم یقینا مطلع هستند که آدام فریمن و آثار او نیاز به معرفی یا تبلیغ ندارد .

لینک مستقیم دانلود کتاب :
فرمت PDF ، ویرایش چهارم
حجم فایل: 24 مگابایت

http://it-ebooks.info/book/1617/

۰ نظر موافقین ۰ مخالفین ۰ ۱۸ ارديبهشت ۹۳ ، ۱۹:۱۰
مهران حسین نیا
پنجشنبه, ۱۸ ارديبهشت ۱۳۹۳، ۰۵:۲۷ ب.ظ

درباره MVC

هنگامی که در یک ارزیابی شخصی، حداقل از نظر خودم متوجه شدم که هر یک از اجزای یک پروژه Default از نوع MVC در Visual Studio 2012 و بالاتر، چه نقشی دارند و چه کاری انجام میدهند و در نهایت چه ارتباطی بین اجزا یک پروژه (در همان ابتدای ایجاد پروژه و بدون اضافه کردن یک سطر کد اضافه) وجود دارد، باید اعتراف کنم که اشتباها، جسارت توضیح در مورد این مطلب را پیدا کردم و سعی کردم مطالبی با عنوان کالبد شکافی یک پروژه Default در MVC بنویسم.در ادامه بر اساس فید بک هایی که دریافت کردم، متوجه شدم مشکلاتی وجود دارد و یا حداقل نتیجه، با چیزی که من تصور میکردم بسیار متفاوت است.
به عبارت ساده تر به نظرم رسید از دو حال خارج نیست. یا درک من از موضوع درست نبوده و نیست و یا بیان من با اشکالاتی همراه بوده که موضوع نه تنها ساده تر نشد بلکه پیچیده تر و درک آن با مشکلات بیشتری همراه شد.
نکته ای که باید به آن توجه مخصوصی داشت این است که اصولا MVC بر خلاف ASP .NET Web Form از قواعد بسیار منظمی پیروی میکند که نظیر آن را میتوان در کاربرد معماری MVVM در WPF و مخصوصا Silverlight مشاهده کرد. در اینجا بر خلاف گذشته اولا برنامه نویس اجازه استفاده از سلیقه های شخصی (نه به مفهوم محدود کردن برنامه نویس و سلب ابتکار عمل) را به هیچ وجه نداشته و ثانیا آشنایی مناسب با برنامه نویسی شئ گرا (OOP) از پیش نیازهای اساسی کار با این تکنولوژی منحصر بفرد می باشد . مضافا اینکه در یک سناریوی واقعی و در یک Web-based Application منطبق با مقیاسها و معیارهای امروز، نیاز مبرمی به تلفیق با Client-side Programming و به طور مشخص استفاده از کتابخانه های غنی JQuery وجود دارد. همچنین باید استفاده از AJAX و همچنین Asynchronous Programming و بهره گیری از Real Time Application Programming را با استفاده از کتابخانه هایی نظیر SignalR مورد توجه بیشتری قرار داد . هر چند این موارد اخیر در یک پروژه Default وجود ندارند و نیازی هم به وجود آنها در مقطع آغاز یک برنامه، نمی باشد.
در هر حال به نظرم رسید با یک تجدید نظر مجدد در نحوه دیدگاه شخصی خودم و البته این بار با اتکا به مقایسه تکنیک های موجود در MVC و مخصوصا با تکنیک های نظیر در ASP .NET Web Form رویکرد متفاوتی نسبت به این موضوع داشته و با نگاه متفاوتی این کار را مجددا آغاز کنم.
در مجموع با توجه به این واقعیت مهم که هم اکنون تکنولوژی برگزیده، جدید و توصیه شده Microsoft در زمینه تولید برنامه های مبتنی بر وب، MVC می باشد، با اشتیاق بسیار مایل هستم سمت و سوی مطالبی که در این گروه به بحث گذاشته میشود، حول و حوش موضوع MVC و مسائل مرتبط بوده و در راستای رسیدن به این هدف، محیط مناسبی برای تبادل نظر و آشنایی با ایده ها و تکنیک های موجود در این زمینه فراهم شود .

 

۰ نظر موافقین ۰ مخالفین ۰ ۱۸ ارديبهشت ۹۳ ، ۱۷:۲۷
مهران حسین نیا

یکی از ارکان اصلی MVC و حتی MVVM توجه به این نکته بسیار مهم است که هیچ ارتباط مستقیم (Reference) و وابستگی (Dependency) بین Model و View وجود نداشته باشد. از سوی دیگر همه ما می دانیم که یکی از نیازهای اصلی در یک برنامه تبادل اطلاعات بین UI و Data source می باشد که وظیفه انجام این ارتباط در MVVM به عهده ViewModel و در MVC به عهده Controller است .
به طور کلی در MVC سه مکانیسم برای ارتباط Controller با View جهت تامین اطلاعات مورد نیاز در View پیش بینی شده است .


روش اول استفاده از ViewData


از دیدگاه ساختمان داده، ViewData یک Dictionary تلقی میشود و چنانچه از قبل میدانیم Dictionary دارای ساختاری است که با استفاده از دو جزء کلید و مقدار (Key Value Pair) امکان دستیابی به این ساختار میسر می باشد . به طور مشخص با تعیین یک کلید (Key) دلخواه مقادیر مورد نظر خودمان را در Dictionary ذخیره کرده و یا بازیابی می کنیم.


public ActionResult ShowError()
{
ViewData["ErrorCode"] = 12345;
ViewData["ErrorDescription"] = "Something bad happened";
ViewData["ErrorDate"] = DateTime.Now;
ViewData["Exception"] = new Exception();
return View();
}


در مثال ارائه شده با تعیین کلیدهای ErrorCode و ErrorDescription و ٍErrorDate و Exception به ترتیب مقادیر 12345 و Something bad happened و DateTime.Now و یک نمونه از کلاس Exception در ViewData قرار گرفته است . بر اساس همین مثال میتوان نتیجه گرفت که امکان قرار دادن هر نوع داده دلخواه ، شامل انواع پایه ساده (Primitive Types) تا انواع داده های پیچیده تر (Complex Types) در ViewData میسر می باشد . همچنین در مثال فوق واضح است که با توجه به اینکه با استفاده از return View() ، صریحا نام View ذکر نشده است، چنانچه قبلا اشاره شد، Controller اطلاعات موجود در ViewData را به یک View هم نام با action یعنی ShowError (فایل ShowError.cshtml) ارسال خواهد کرد. همچنین در مورد محل پیش فرض این فایل قبلا توضیحاتی ارائه شده است .
در Viewی مذکور و با استفاده از موتور Razor دسترسی به اطلاعات ارسال شده به View میسر خواهد بود .


<h1>An error was found</h1>
<h2>Error Code: @ViewBag.ErrorCode</h2>
<h2>@ViewBag.ErrorDescription</h2>


روش دوم استفاده از ViewBag


این روش از بسیاری از جهات با روش قبلی مشابه می باشد و تنها تفاوت در اینجاست که در ViewBag از ویژگی خواص پویا (Dynamic Property) که در C# 4.0 معرفی شد استفاده شده است . لطفا به مثال ساده ای در این زمینه توجه کنید:


public ActionResult ShowError()
{
ViewBag.ErrorCode = 12345;
ViewBag.ErrorDescription = "Something bad happened";
ViewBag.Exception = new Exception();
return View();
}


در این صورت روش دسترسی به داده های موجود در ViewBag از View به صورت زیر خواهد بود :

 

<h1>An error was found</h1>
<h2>Error Code: @ViewBag.ErrorCode</h2>
<h2>@ViewBag.ErrorDescription</h2>


جالب است بدانیم که حتی در هنگام استفاده از ViewBag عملا از ViewData استفاده میشود که در حقیقت از یک Dictionary واحد برای ارسال اطلاعات از Controller به View استفاده خواهد شد. در اینجا ممکن است این سوال مطرح شود که بر اساس مطلبی که عنوان شد آیا استفاده از ViewBag.ErrorCode = 12345; و در ادامه دستیابی به این اطلاعات در View به صورت <h2>@ViewData["ErrorCode"]</h2> مجاز می باشد ؟
پاسخ این سوال مثبت است و البته واضح است که لزومی به استفاده از این روش نیست و تا زمانیکه نام کلید دارای کاراکتر های مجاز و مخصوصا فاقد Space باشد مورد مذکور معتبر خواهد بود .
یک نکته بسیار مهم در هنگام استفاده از ViewData یا ViewBag این است که به محض آنکه View میزبان اطلاعات موجود در یکی از این دو ساختار Dictionary را Render نماید ، دسترسی مجدد به این Dictionary مجاز نخواهد بود . در یک سناریوی ساده تصور کنید که بعد از یک بار Render کردن اطلاعات Dictionary با استفاده از یک RedirectToAction ، مسیر پردازش به یک action دیگر (که نام آن در پارامتر ذکر خواهد شد) هدایت شود . در این صورت واضح است که یک کد 302 به Browser ارسال میشود که از سوی Browser ، اجبار به Redirect به یک مقصد جدید تلقی و تفسیر خواهد شد . در این صورت محتویات Dictionary (منظور ViewData یا ViewBag) به طور کامل Reset شده و دستیابی به این اطلاعات در View جدید به هیچ وجه میسر نخواهد بود . در صورت برخورد با سناریوی مشابه (یعنی نیاز به حفظ موقت اطلاعات تقریبا مشابه با تکنیک استفاده از ViewState در ASP .NET Web Form) توصیه شده که از ساختار TempData که اطلاعات خود را در Session ذخیره می نماید استفاده شود .


روش سوم استفاده از مفهوم Strongly Typed View:


با وجود انعطاف و سادگی دو روش مذکور در بسیاری از سناریو ها امکان استفاده از ViewData و ViewBag میسر نیست و بر اساس توضیحاتی که ارائه شد، به نظر میرسد که استفاده از این دو با محدودیت هایی روبرو می باشد ...

 ... ادامه دارد
 

۰ نظر موافقین ۰ مخالفین ۰ ۱۸ ارديبهشت ۹۳ ، ۱۷:۲۴
مهران حسین نیا

مطابق با ترمینولوژی MVC ، کلاس Controller نقش Dispatcher و توزیع کننده پردازش را با استفاده از متدهای Public خود که action نامیده میشوند، به عهده دارد. در بهترین حالت Controller موظف است که اطلاعات مورد نیاز برنامه را با استفاده از فراخوانی متد های کلاس Model یا شکل مناسب تر آن یعنی ViewModel آماده نموده و آنها را به منظور ایجاد یک خروجی قابل درک توسط انسان به یک View مشخص هدایت نموده تا View آنها را Render نموده و نمایش دهد .
جالب است بدانید که کل ادامه مطلب در حقیقت توضیح وضعیت های متفاوتی است که در پاراگراف قبل به صورت بسیار کلی ذکر شد . در حقیقت هدف من این است که Controller را معرفی نموده حالت های مختلف در متدهای action و تنوعی که در بحث انتخاب View وجود دارد را تشریح نموده و روشهای تبادل اطلاعات Controller و View را بررسی کنم.
اما با بررسی متد نمونه Index در کنترلر Home متوجه خواهیم شد که با توجه به آنچه در این متد مشاهده میکنیم :


public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

return View();
}


نوع خروجی متد Index از نوع ActionResult تعیین شده است که کلاس جد انواع متعدد View هایی است که در مورد آنها صحبت خواهیم کرد.
همچنین وجود View() به عنوان مقدار خروجی به طور مشخص ذکر نمیکند که کدام View برای عملیات Rendering در Browser تعیین شده است . رایج ترین شکل استفاده از View حالتی است که در آن، نام View در قالب پارامتر، صریحا ذکر شده و چنانچه در ادامه خواهیم دید حتی نوع اطلاعاتی که به آن Import میشود می تواند به عنوان پارامتر دوم (در قالب مفهوم Model) مشخص شود .
برای شروع پیشنهاد میکنم یک پروژه Internet در MVC 4 ایجاد نمایید . سپس در فولدر Controller کلاس HomeController را باز نموده و متدی به نام NoView به صورت زیر به این کلاس اضافه نمایید. بعد از اضافه کردن این متد کلاس زیر شبیه به کد زیر خواهد بود :


public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

return View();
}

public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public string NoView()
{
return "What's going on?";
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";

return View();
}
}

 

در ادامه برای تکمیل این مثال در فولدر Views/Shared فایل _Layout.cshtml را باز نموده و محتویات داخل <ul id="menu"> را به صورت زیر تغییر دهید . در حقیقت تنها یک سطر به محتویات تگ ul اضافه نموده ایم که نتیجه مطابق با وضعیت زیر خواهد بود :


<ul id="menu">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li>@Html.ActionLink("No View", "NoView", "Home")</li>
</ul>

 

نتیجه این عملیات این است که ما ابتدا یک action بسیار ساده به نام NoView به کنترلر پیش فرض خودمان یعنی HomeController اضافه می کنیم. در MVC پسوند Controller باید مطابق با یک قاعده (که وحی منزل نیست و قابل تغییر است) همیشه به کلاس Controller ما اضافه شود . به عنوان مثال کنترلر Home به صورت کلاس HomeController ایجاد میشود ولی در هنگام رجوع به آن از نام اصلی یعنی Home استفاده می کنیم .
ما سپس در _Layout.cshtml با استفاده از متد Helper بسیار مهمی به نام ActionLink یک لینک استاندارد با عبارت No View (پارامتر اول) به متد action تازه ایجاد شده خود یعنی NoView (پارامتر دوم) در کنترلر Home (پارامتر سوم) ایجاد کردیم. نتیجه این کار را می توان با اجرای مجدد پروژه، ملاحظه کرد .
اما هدف من از این مثال این است که:
در ارزش هر قطعه ای گویند هر نوعی سخن
من خود به چشم خویشتن دیدم که کنترلر مهم تر است.!!!!:)


در منابع مختلفی که توسط نویسندگان و برنامه نویسان خبره مطرح شده، گاهی مشاهده می کنیم که مقایسه ای در مورد اهمیت هر یک از اجزای Model و View و Controller انجام شده است.
در اینجا شاید ورود به این بحث که اهمیت و ارزش وزنی کدامیک از این اجزا در یک پروژه MVC مهمتر می باشد ، فایده ای نداشته باشد ولی همین مثال ساده ثابت میکند که Controller ما قادر است به طور کاملا مستقل و بدون نیاز به View و Model با استفاده از این action جدید خروجی هر چند ساده و بی اهمیتی را به Browser ارسال کند .
به طور دقیق تر در اینجا عبارت Whats Going On مستقیما و بدون نیاز به View به Response.Stream ارسال میشود که بدون هیچ فرمت خاصی در Browser ظاهر میشود .
چنانچه قبلا هم اشاره شد در مورد action ی که با نام Index در پروژه پیش فرض وجود دارد مشخصا از یک View برای ارسال خروجی و Rendering آن در Browser استفاده شده است اما در متد Index نام این View صریحا ذکر نشده است .
در این حالت MVC به صورت پیش فرض جستجویی در فولدر Views/Home یا Views/Shared به دنبال یک View هم نام با action مذکور انجام داده و در صورت موفقیت آمیز بودن این جستجو، View یافت شده برای ارسال خروجی نهایی انتخاب میشود.
در اینجا این شرط محقق شده است . یعنی با ملاحظه در داخل فولدر Views/Home مشاهده میکنیم که index.cshtml وجود دارد که از دیدگاه MVC یک View ایجاد شده با استفاده از Razor می باشد .
Razor نیز از دیدگاه MVC یک ViewEngine یا موتور ارائه View می باشد که در MVC 3 معرفی شد و از Syntax بسیار ساده ای برخوردار است . کماکان ViewEngine سنتی Asp .NET یعنی aspx در پروژه های MVC قابل استفاده است اما به دلیل سادگی و مقبولیت Razor استفاده از Razor به جای aspx در اغلب منابع توصیه شده است . در Razor ترکیب متجانسی از کد سی شارپ و دستورات HTML را مشاهده خواهیم کرد که در این صورت پسوند چنین فایلهایی cshtml خواهد بود . علاقمندان به VB همچنان به دلیل ارادت مالیخولیایی بیل گیتس به Visual Basic امکان استفاده از vb در Razor را دارند که در این صورت نتیجه فایلهایی با پسوند vbhtml را شاهد خواهیم بود .
از شما چه پنهان من هیچ تعجب نخواهم کرد اگر در آینده ای نزدیک ،Xaml به عنوان یک View Engine مستقل در MVC معرفی و قابل استفاده باشد .
در اینجا لازم به تذکر است که در هنگام انتخاب نوع View به عنوان خروجی متدهای action در Controller ما از طیف متنوعی از انتخاب ها روبرو هستیم که هر یک در موارد خاصی کابرد داشته و استفاده از آنها در MVC رایج می باشد. از مهمترین آنها می توان به موارد زیر اشاره کرد:
ViewResult که رایج ترین و پر استفاده ترین انتخاب در این مورد تلقی میشود و از آنجاییکه ViewResult یکی از کلاسهای منشعب از ActionResult می باشد، قادر است با ارسال View به عنوان خروجی متد action امکان انتخاب View پیش فرض را فراهم نماید. در بیشتر مواقع با استفاده از یک پارامتر نام View مورد نظر در هنگام ارسال View به خروجی ذکر میشود . مثلا ارسال View(“ThatOne”) معادل با انتخاب یک View به نام ThatOne می باشد که در فولدر View با نام ThatOne.cshtml وجود دارد .
PartialViewResult : این کلاس هم مستقیما از ActionResult منشعب شده و برای انتخاب یک Partial View به عنوان مسئول Render کردن بخش یا قسمت مشخصی از یک View بزرگتر کاربرد دارد . برای درک بهتر این مفهوم می توان آن را با MasterPage و Content در ASP .NET Web Form مقایسه کرد. جاییکه Content ها وظیفه Render نمودن قسمت های از پیش تعیین شده MasterPage را بر عهده داشتند .
FileResult که کاربرد آن در زمانیست که نیاز به دانلود مستقیم یک فایل وجود داشته باشد . همچنین از این کلاس به عنوان خروجی یک متد action در مواقعی که قصد نمایش Dynamic تصاویر را داشته باشیم میتوان استفاده نمود .
HttpUnauthorizedResult: که همانطوریکه از نام آن مشخص است برای ارسال کد 401 به Browser استفاده میشود که به معنی Not Authorize بوده و به طور معمول منجر به Redirect شدن درخواست به سمت Page یا صفحه Login خواهد شد که پروسه های Authentication و Authorization انجام شوند.
HttpStatusCodeResult که مشخصا برای ارسال کد های رایج Http به Browser مورد استفاده قرار میگیرد. به عنوان مثال از آن می توان برای ارسال کد 404 به عنوان Page Not Found استفاده نمود .
JsonResult با توجه به شهرت و کاربردهای متنوع فرمت Javascript Object Notation ، بخصوص در وب سرویس ها پیش بینی شده و استفاده میشود .
بعد از یک مرور اجمالی در انتخابهای موجود اینک وقت آن رسیده که جزییات بیشتری را در View بررسی کرده و آنها را بر اساس کاربردهای متنوعی که دارند طبقه بندی کنیم . برای انجام این کار دوباره به قالب استاندارد پروژه بازگشته و توصیه میکنم به محتویات فایل _Layout.cshtml در فولدر Views/Shared با دقت بیشتری نگاه کنید .
نقش _Layout.cshtml بسیار شبیه به نقش MasterPage در ASP .NET Web Form می باشد. معنی دقیق این گفته این است که این فایل (یا مفهوم) معمولا برای تامین اطلاعات قسمت های تکراری در یک Page مثل Header و Logo و Footer و منو های اصلی برنامه کاربرد دارد. این یک روش ایده آل برای انجام این کار و پرهیز از تکرار این قسمتها در همه View های تخصصی برنامه است .
در داخل این فایل مخصوصا فراخوانی @RenderSection("featured", required: false) و @RenderBody() از اهمیت بیشتری برخوردار هستند .
مفهوم Section یک مفهوم نوظهور و ویژه در MVC می باشد که البته هیچ ارتباطی با تگ Sectin در HTML5 ندارد .
فراخوانی RenderSection در این فایل با استفاده از یک پارامت،ر به این معنی می باشد که Layout اصلی Page یک Section و قسمت ویژه ای به نام featured را مشخص نموده که View مسئولیت Render کردن محتویات آن Section را به عهده میگیرد . در صورت استفاده از یک پارامتر در این متد انجام این وظیفه به صورت اجباری به View محول میشود.
در صورت استفاده از پارامتر دوم که نوع آن bool می باشد می توان انجام این عمل را با ارائه مقدار false اختیاری نمود . در صورت عدم ذکر پارامتر دوم مقدار پیش فرض true بوده و معنای آن این است که View موظف است به صورت اجباری یک Section با نام تعیین شده در پارامتر اول پیش بینی نماید .
خود View نیز می تواند قبل از اقدام به تعریف یک Section ابتدا با استفاده از IsSectionDefined که پارامتری از نوع String دریافت میکند از وجود یا عدم وجود این Section مطمئن بشود. پارامتر IsSectionDefined در حقیقت نام Section می باشد.
در داخل View (مثلا در Index.cshtml) ملاحظه خواهیم کرد که ویوی Index اطلاعات مشخصی را برای نمایش در Section رزرو شده به صورت زیر تدارک دیده است :


@section featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
<h2>@ViewBag.Message</h2>
</hgroup>
<p>
To learn more about ASP.NET MVC visit
<a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
The page features <mark>videos, tutorials, and samples</mark> to help you get the most from ASP.NET MVC.
If you have any questions about ASP.NET MVC visit
<a href="http://forums.asp.net/1146.aspx/1?MVC" title="ASP.NET MVC Forum">our forums</a>.
</p>
</div>
</section>
}


همچنین توجه به این نکته ضروریست که یک View می تواند صریحا با ذکر


@{
Layout = null;
{


به صورت مستقل (بدون نیاز به Layout) وظیفه ارسال خروجی به Browser را تقبل نماید . در شکل دیگری از این دستور ، View می تواند یک Layout متفاوت با Layout پیش فرض را به عنوان Layout وابسته خود انتخاب نماید و در نهایت اینکه اگر مقدار null در Layout (حالت اول) ذکر نشود MVC با مراجعه به _ViewStart.cshtml نام و جایگاه Layout پیش فرض را مشخص نموده و از آن به عنوان Layout مورد نظر View استفاده خواهد کرد .
پس انتخابها کاملا روشن و واضح می باشند و نکته مهم در اینجا توجه به رابطه بین یک View و Layout همچنین امکان استفاده از Partial View و Section هاست که باز در مقایسه با ASP .NET Web Form به صورت Nested Master Page و یا حتی شبیه به مفهوم User Control از آنها استفاده نمود .
در انتهای این قسمت باید اشاره کنم که RenderBody در حقیقت نوع خاصی از RenderSection است که محتویات و اطلاعات مشخص شده در View را که در هیچ Section خاصی قرار ندارند ، انتخاب نموده و در محل مشخص شده در فراخوانی RenderBody نمایش داده و یا به طور دقیق تر Render میکند . در برخی از منابع از آن به عنوان Unnamed Section یا Section بدون نام، اسم برده شده است .
در قسمت بعد، به سه مکانیسم مشخص و تعریف شده MVC برای تبادل اطلاعات بین Controller و View اشاره خواهم کرد و مثالهایی در این باره بررسی خواهیم نمود تا زمینه مساعدی برای معرفی و آشنایی با Model و شکل تخصصی تر آن یعنی ViewModel فراهم شود.

 

۰ نظر موافقین ۰ مخالفین ۰ ۱۸ ارديبهشت ۹۳ ، ۱۷:۲۱
مهران حسین نیا

مدت نسبتا زیادی است که ذهن من مشغول جزییات این مطلب بوده و متاسفانه به دلایل زیادی امکان پرداختن به آن میسر نشد.
در این فرصت، به بهانه بررسی ساختار پیش فرض یک پروژه ASP .NET MVC سعی خواهم کرد که نقش اجزای مختلف این پروژه را مشخص کرده و با توجه به بضاعت اندک خود، در مورد ارتباط این اجزا توضیحاتی را تقدیم علاقمندان نمایم. پیشاپیش از توجه دوستان و همکاران محترم سپاسگذارم.

به منظور کالبد شکافی یک پروژه ساده MVC و بررسی ارتباط بین اجزای مختلف موجود در Template پیش فرض این پروژه ها به نظر میرسد که توجه به نقش و عملکرد فایل Global.asax به عنوان نقطه شروع این بررسی ، بسیار مفید و آموزنده می باشد.
در واقع فایل Global.asax که به نام ASP .NET Application File نیز شناخته میشود از همان ابتدای تولد تکنولوژی Active Server Page که امروزه به نام ASP کلاسیک شناخته میشود به صورت فایلی با نام Global.asa و بعدها در سال 2003 با ظهور تکنولوژی ASP .NET Web Form به صورت Global.asax به عنوان یک ویژگی Optional و اختیاری به همراه پروژه های ASP .NET مورد استفاده قرار گرفته است. قبل از توضیحاتی در مورد نقش این فایل لازم به ذکر است که تقریبا مقارن با معرفی .NET Framework 3.5 مایکروسافت در ی: اقدام غافلگیرانه به طور کلی معماری Internet Information Server را تغییر داده و با معرفی Integrated Mode در IIS عملا در ساده ترین شکل ممکن این امکان را فراهم کرد که برنامه نویسان نرم افزارهای کاربردی وب با استفاده از رویدادها و Event های تدارک دیده شده در Global.asax و یا در سطوح بالاتر با ایجاد Module های اختصاصی در مراحل مختلف پردازش IIS نظارت و یا در صورت نیاز دخالت نمایند.
خود تکنولوژی MVC محصوصل چنین نگرش متفاوتی میباشد که امکان تغییرات بنیادی در معماری برنامه های مبتنی بر وب را پدید آورد . موضوع رویدادها و Stage های متعددی که در طول مسیر پردازش IIS وجود دارد (این مسیر معمولا به اختصار IIS Processing Pipeline نامیده می شود) موضوع بسیار جالبی برای برنامه نویسانی است که نیاز به کنترل بیشتر در برنامه های مبتنی بر وب (به عنوان مثال ایجاد ماژول های سفارشی Authenticate و Authorize) دارند و در اینجا مجال پرداختن به آن وجود ندارد.
در یک جمع بندی خلاصه می توان چنین گفت که Global.asax امکان مشارکت یا اصطلاحا Subscribe در مراحل مختلف IIS Processing Pipeline که همان مسیر یا مقاطع متعدد و متنوع پردازش در تکنولوژی ASP .NET است ) از مرحله دریافت Request تا مرحله پایانی ارسال Response) را فراهم میکند. در زیر لیستی از رویداد های Synchronous را مشاهده میکنید:


1. BeginRequest
2. AuthenticateRequest
3. PostAuthenticateRequest
4. AuthorizeRequest
5. PostAuthorizeRequest
6. ResolveRequestCache
7. PostResolveRequestCache
8. MapRequestHandler
9. PostMapRequestHandler
10. AcquireRequestState
11. PostAcquireRequestState
12. PreRequestHandlerExecute
اینجا مرحله ایست که پردازش های Page Handler در ASP .NET آغاز میشود
13. PostRequestHandlerExecute
14. ReleaseRequestState
15. PostReleaseRequestState
16. UpdateRequestCache
17. PostUpdateRequestCache
18. LogRequest
19. PostLogRequest
20. EndRequest


دلیل اصلی اینکه ما Global.asax را به عنوان نقطه شروع کالبد شکافی پروژه های MVC انتخاب کردیم این است که طراحان MVC جزییات مربوط به منطق Config و تنظیمات پایه و عمل ثبت و Register کردن آنها را در رویداد Application_Start فایل Global.asax قرار داده اند که چنانچه از نام این رویداد نیز مشخص است یک رویداد Application Level محسوب شده و به ازای هر بار اجرای Application (فراموش نکنیم که رایج ترین روش اجرای یک Application مبتنی بر وب ، درخواست یا Request می باشد که به طور معمول با تایپ آدرس یا URL برنامه در مرورگر انجام میشود) متد وابسته به این رویداد اجرا خواهد شد.

 

protected void Application_Start(){
   AreaRegistration.RegisterAllAreas();
   WebApiConfig.Register(GlobalConfiguration.Configuration);
   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
   RouteConfig.RegisterRoutes(RouteTable.Routes);
   BundleConfig.RegisterBundles(BundleTable.Bundles);
   AuthConfig.RegisterAuth();
}


در میان مواردی که در کد مشاهده میکنید در حال حاضر فراخوانی متد استاتیک RegisterRoutes کلاس RouteConfig بیشتر از سایرین مورد توجه ما می باشد .
اما در یک نگاه اجمالی واضح است که منشاء بسیاری از تنظیمات و Config اولیه MVC از همینجا آغاز میشود. به عنوان مثال RegisterBundles عملیات بسته بندی بخشهای استاتیک سایت ، مواردی نظیر Stylesheet و کد های Client-side و کتابخانه های JQuery را بر عهده دارد. هدف از این بسته بندی در درجه اول این است که اطلاعات این فایلهای استاتیک را ضمن نوعی فشرده سازی در یک بسته واحد متمرکز نموده و استفاده از آنها در مراحل مختلف پروژه با سرعت و کیفیت بالاتری میسر گردد. همچنین متد استاتیک RegisterAuth وظیفه الحاق کتابخانه های مورد نیاز برای استفاده از روش OAuth را در MVC فراهم میسازد .
نکته : همینطور که در مسیر این بررسی جلوتر می رویم، بهانه های متعددی برای انحراف از مسیر اصلی وجود دارد که من عمدا بدون توجه به آنها فقط به مواردی که جهت ایجاد یک دیدگاه کلی از پروژه های MVC الزامی هستند توجه خواهم کرد .
چنانچه اشاره شد فراخوانی متد استاتیک RegisterRoute نقطه آغاز تنظیمات Routhing در MVC می باشد که در حال حاضر موضوع اصلی بحث ما خواهد بود.
یقین دارم که ارائه یک معادل فارسی برای Routhing هیچ کمکی به درک نقش آن نخواهد کرد پس بنابراین من در سرتاسر این مطلب صرفا از همین اصطلاح استفاده خواهم کرد و در اینجا فقط به وظایف و نقش بسیار کلیدی آن خواهم پرداخت .
مفهوم Routhing در MVC به طور مشخص برای تفسیر URL و قسمت های مختلف آن که Segment یا قطعه نامیده میشوند به کار میرود. چنانچه از قبل مطلع هستیم به طور معمول یک URL معتبر معمولا دارای Segment های مختلفی است که هر Segment با استفاده از علامت / از سایرین متمایز میشود .
تا قبل از MVC و مخصوصا در ASP .NET Web Form با ملاحظه یک URL نمونه مثل :


http://example.com/albums/list.aspx?catid=17313&genreid=33723


اطلاعات بسیار مفیدی در آن مشاهده میکنیم که صرفنظراز بسیاری از جزییات موجود در آن، توجه به این نکته مهم ضروری است که تا قبل از MVC یک تناظر مشخص بین URL و ساختار فایلها و فولدرهای Application وجود داشته و در URL مثال قبل بدون هیچ تردیدی می توان دریافت که قطعا فایلی به نام List.aspx در فولدری به نام Albums در یک Domain ثبت شده به نام Example.com وجود دارد که کاربر با تایپ URL فوق ، با استفاده از پروتکل http قصد دریافت سرویس (یا اجرای برنامه) موجود در آدرس مذکور را داشته است. مجددا آنچه از ظواهر امر پیداست، این است که کاربر با ارسال اطلاعاتی که بعد از علامت ? قرار دارد اصطلاحا با ارسال یک Query String قصد دارد لیستی از آلبومهای خاصی از یک موضوع یا ژانر خاص را مشاهده نماید.
در MVC این تناظر ساختار فایلها با فولدرها دیگر معتبر نبوده (یا حداقل به شدت قبل نیست) و همه چیز وابسته به تفسیری است که Routhing انجام داده و حاصل تفسیر این اطلاعات در این تکنولوژِی (یعنی MVC) مستقیما در اختیار Controller قرار داده میشود که به نحوی که به آن اشاره خواهیم کرد، مسیر پردازش و نحوه ارسال پاسخ یا Response را مشخص نماید .
پس در یک جمع بندی در ابتدا فایل Global.asax در رویداد Application_OnStart متد استاتیک RegisterRoutes را فراخوانی میکند . پارامتر این متد یعنی RouteTable.Routes یک نمونه یا Instance از کلاس RouteCollection می باشد که در یک تعبیر ساده، کلکسیونی از مسیر های قابل درک برنامه ما را در خود ذخیره میکند. این کلکسیون شامل آیتم هایی است که به طور معمول در MVC ما برای پردازش آنها به ازای هر یک از آنها متدی از کلاس Controller را در نظر گرفته ایم . البته باید توجه داشت که در MVC کماکان امکان پردازش فایلهای استاتیک پیش بینی شده است و ما در مورد این موضوع در مباحث مربوط به View بیشتر صحبت خواهیم کرد .
در فولدر App_Start کلاس RouteConfig وظیفه ثبت مسیرهای مورد نظر در برنامه ما را به عهده دارد در پیاده سازی پیش فرض این کلاس ما بوضوح مشاهده خواهیم کرد که در طول فراخوانی متد RegisterRoute که در Global.asax انجام میشود چه عملیاتی انجام میشود:


public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}


در ابتدا و در سطر اول، ملاحظه میکنید که فراخوانی متد IgnoreRoute ظاهرا از هر گونه دسترسی مستقیم کاربر به فایلهای Resource مشخص شده با پسوند .axd جلوگیری میکند. شاید جالب باشد که بدانید در واقع عملا هیچ فایلی با پسوند axd در پروژه ما وجود ندارد!!!! اما در ASP .NET با استفاده از یک ترفند ویژه به طور معمول برخی از اطلاعات استاتیک پروژه نظیر کد های Javascript و حتی Image ها که اغلب آنها را Resource یا منابع پروژه می نامیم به صورت Dynamic در قالب فایلهایی با پسوند .axd تبدیل شده و برای تکمیل پروسه Render و نمایش در Browser نقش آفرینی میکنند .
اما اولین و مهمترین مسیر مجاز با فراخوانی MapRoute در RouteCollection ثبت میگردد که اصطلاحا به آن مسیر پیش فرض یا Default Route گفته میشود . این وضعیت تا حد بسیار زیادی مشابه با تعیین Defaullt Document در ASP .NET Web Form می باشد که خوانندگان محترم قطعا با این مفهوم آشنا می باشند .
آنچه به طور دقیق توسط فراخوانی متد MapRoute در سطر دوم انجام میشود این است که ما به عنوان طراح اصلی برنامه مشخص میکنیم که URL مورد نظر برای دستیابی به برنامه ما می تواند حداکثر شامل سه Segment یا قطعه باشد که به ترتیب با استفاده از سه Placeholder (نام جایگزین) با نامهای Controller و action و id آنها را مشخص کرده ایم. اسامی جایگزین یا همان Placeholder ها در داخل یک جفت {} تایپ شده و هر Segment با یک علامت / از دیگری جدا شده است .
همچنین در ادامه برای هر یک از این سه قطعه قواعدی مشخص نموده ایم که مشخصات این قواعد را به طور دقیق در defaults به طور واضح بیان میکنیم. ما برای controller مقدار پیش فرض home و برای action (که در واقع متد های Public موجود در کلاس Controller به عنوان جزء اصلی پردازش هر مسیر هستند) مقدار پیش فرض Index را در نظر گرفته ایم. با ذکر UrlParameter.Optional ، صریحا ذکر id را اختیاری یا Optional تعیین کرده ایم .
اگر فرض کنیم که نام پروژه ما MVCPreview باشد در این صورت مسیر های زیر برای برنامه ما مجاز محسوب خواهند شد :


1) http://MVCPreview/Home/Index/210
2) http://MVCPreview/Home/Index/
3) http://MVCPreview/Home
4) http://MVCPreview/

 

در حالت اول نتیجه پردازش Routhing این است که برای Placeholder های id و action و controller به ترتیب مقادیر 210 و index و Home تعیین شده است . در واقع URL مذکور شامل سه Segment می باشد که با شرط ذکر شده در فراخوانی متد MapRoute (یعنی "{controller}/{action}/{id}") کاملا منطبق است . به عبارت ساده تر در URL مذکور ما صریحا مشخص کرد ه ایم که مسیر پردازش به متد Index (که نقش action را عهده دار است) در کلاس Home (که نقش Controller را ایفا میکند) هدایت شده و مقدار 210 به عنوان پارامتر و جایگزین id در اختیار این متد قرار بگیرد .
در حالت دوم مقدار جایگزین برای id ذکر نشده که با توجه به UrlParameter.Optional توسط Routhing همچنان به عنوان یک مسیر معتبر تفسیر شده و مسیر پردازش به متد Index در Controller هدایت خواهد شد.
در حالت سوم از ذکر نام action خود داری شده ولی باز به دلیل ارائه مقدار Default در فراخوانی MapRoute یعنی درست با ذکر action = "Index" مفسر Routhing به صورت خودکار جای خالی action را با مقدار index تامین خواهد کرد. بنابراین مجددا این URL نیز توسط Routhing معتبر و مجاز تشخیص داده شده و مسیر پردازش درست به مانند دو حالت قبل ادامه خواهد یافت.
در حالت چهارم هر سه Segment غایب هستند و این بار هم مجددا عدم وجود Controller توسط controller = "Home" جبران شده و مقدار Home در اینجا مشخص میکند که عدم ذکر نام Controller به معنی انتخاب Controller ی به نام Home خواهد بود و نتیجه آنکه مجددا این URL نیز معتبر و مجاز تشخیص داده شده و مسیر پردازش به متد Index در کلاس Controller ما یعنی Home هدایت میشود .
 

....ادامه دارد

۱ نظر موافقین ۰ مخالفین ۰ ۱۸ ارديبهشت ۹۳ ، ۱۷:۱۷
مهران حسین نیا
يكشنبه, ۱۱ اسفند ۱۳۹۲، ۰۵:۵۶ ب.ظ

استفاده یا عدم استفاده از Generic


     در این برنامه ابتدا یک کلاس بسیار ساده برای بررسی نحوه انجام پاکسازی حافظه و به دست آوردن معیار مناسبی جهت اندازه گیری میزان کارآیی استفاده از تکنیک Generic ایجاد شده است.
بررسی نتایج حاصل از این برنامه نشان میدهد که استفاده از Generic بویژه در انواع ValueType از نظر مصرف منابع، بسیار مقرون به صرفه تر بوده و سرعت انجام عملیات به مراتب بالاتر خواهد بود. در انواع Reference Type نیز این افزایش کارآیی وجود دارد اما به اندازه مورد قبلی محسوس نیست. خروجی برنامه ، زمان صرف شده برای درج 10 میلیون عنصر از یک نوع Value Type (در مثال ما این نوع int انتخاب شده است) به ترتیب با استفاده از Generic و بدون آن ، و همچنین همین عملیات مشابه برای یک نوع Reference Type (در مثال این نوع string انتخاب شده است) را با استفاده از کلاس StopWatch و تعداد تلاشهای CLR برای پاکسازی (Garbage Collector) با استفاده از متد استاتیک کلاس GC، در طول این پروسه ، نشان داده شده است. با تغییر در مقدار Max_Loop_Count ، مثلا بیشتر کردن آن امکان بروز Out Of Memory Exception وجود دارد و به دلیل سادگی و خلاصه بودن کد، در برنامه تمهیداتی برای کنترل آن در نظر گرفته نشده است.
 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace GenericOrNotGeneric
{

class Program
{

const Int32 Max_Loop_Count = 10000000;
static void Main(string[] args)
{

ValueTypePerformanceTest();
ReferenceTypePerformanceTest();
Console.WriteLine("Now in the end you decide. ");
Console.WriteLine("Please press <Enter> to exit...");
Console.ReadLine();
}

private static void ValueTypePerformanceTest()
{

using (new OperationTimer("List<Int32>"))
{
List<Int32> l = new List<Int32>();
for (Int32 n = 0; n < Max_Loop_Count; n++)
{
l.Add(n);
Int32 x = l[n];
}
l = null;
}
using (new OperationTimer("ArrayList of Int32"))
{
ArrayList a = new ArrayList();
for (Int32 n = 0; n < Max_Loop_Count; n++)
{
a.Add(n);
Int32 x = (Int32)a[n];
}
a = null;
}
}
private static void ReferenceTypePerformanceTest()
{

using (new OperationTimer("List<String>"))
{
List<String> l = new List<String>();
for (Int32 n = 0; n < Max_Loop_Count; n++)
{
l.Add("X");
String x = l[n];
}
l = null;
}
using (new OperationTimer("ArrayList of String"))
{
ArrayList a = new ArrayList();
for (Int32 n = 0; n < Max_Loop_Count; n++)
{
a.Add("X");
String x = (String)a[n];
}
a = null;
}
}
}

internal sealed class OperationTimer : IDisposable
{
private Stopwatch m_stopwatch;
private String m_text;
private Int32 m_collectionCount;
public OperationTimer(String text)
{
PrepareForOperation();
m_text = text;
m_collectionCount = GC.CollectionCount(0);
m_stopwatch = Stopwatch.StartNew();
}
public void Dispose()
{
Console.WriteLine("{0} (GCs={1,3}) {2}", (m_stopwatch.Elapsed),
GC.CollectionCount(0) - m_collectionCount, m_text);
}
private static void PrepareForOperation()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
}

 

۰ نظر موافقین ۰ مخالفین ۰ ۱۱ اسفند ۹۲ ، ۱۷:۵۶
مهران حسین نیا
شنبه, ۳ اسفند ۱۳۹۲، ۰۶:۱۳ ب.ظ

یک توضیح ضروری

مطلبی که در پست قبلی شروع کرده ام به ظاهر مطلب ساده ای میباشد. در آغاز مثل همیش سعی کردم که از چند منبع معتبر استفاده کنم که تا حد امکان موضوع اصلی حفظ شده و به بیراهه کشیده نشود. شاید بتوان به طور خلاصه چنین عنوان کرد که موضوع اصلی مورد نظر من Types و مباحث مربوط به آن در سی شارپ بود.
اما انتخاب یک حد و مرز مشخص برای این بحث بسیار مشکل تر از آن است که در ابتدا به نظر میرسید. هدف اصلی من نوع متفاوت تلقی کامپایلر از Reference Typeها و Value Typeها ، تفاوت نوع تخصیص حافظه و به دست آوردن یک معیار صحیح در انتخاب هر یک از این دو نوع در سناریو های رایج برنامه نویسی می باشد.
اما هر چه بیشتر در این باره فکر میکنم در می یابم که مطرح کردن این بحث بدون اشاره به مطالبی مثل انواع Nullableها، Boxing و UnBoxing ، توصیف دقیق و کافی در مورد Heap و Stack ، اشاره گرها و Pointer ها و طرح مثالهایی که مرتبط با موضوع باشند، ضروری ولی  بسیار دشوار است.
در این راه مخصوصا استفاده از Utility های WinDump و WinDBG که با گزارشهای مفصل و نشان دادن مکان های دقیق حافظه درک این مفاهیم را آسان تر خواهند نمود، اجتناب ناپذیر هستند.
این توضیح از آن جهت به نظرم ضروری بود که به هر حال معمولا در همه کتابهای آموزش برنامه نویسی سی شارپ، مخصوصا در ابتدا مباحث مربوط به Types ، انواع مختلف آنها و ظرفیت های هر یک در قالب یک جدول و توضیحات کامل ارائه میشود و تلاش من در این باره با همه بضاعت اندکی که دارم به این خاطر است که این مبحث با دید عمیق تری بررسی شده و نتایج با مثالهای گویا و روشن ارائه شوند.
یقینا در راه رسیدن به این هدف سعی خواهم کرد که از تکرار مکرارات پرهیز کرده و با استفاده از منابع معتبر مطالب مورد نظر را تا حد امکان موشکافی کرده و بررسی کنم. از توجه همه دوستان و همکاران محترم که مشوق اصلی من در این کار می باشد سپاسگذارم.
 

۰ نظر موافقین ۰ مخالفین ۰ ۰۳ اسفند ۹۲ ، ۱۸:۱۳
مهران حسین نیا
جمعه, ۲ اسفند ۱۳۹۲، ۰۹:۴۸ ب.ظ

نگاهی به مباحث پایه (قسمت اول)

     یکی از مباحث پایه در همه زبان های برنامه نویسی مبحث Types و مطالعه نوع پیاده سازی هر زبان برای ارائه انواع پایه  و در نهایت ترکیب آنها برای دستیابی به انواع پیچیده تر مثل Structure ها و Classهاست.
در C# از دیدگاه نوع تخصیص حافظه دو گروه Type یا نوع مختلف وجود دارد که Value Types و Reference Types نامیده میشوند.

انواع Value Types

 به استثنای نوع String تقریبا همه انواع پایه از این نوع میباشند. زماینکه یک متغیر از نوع Value Type ایجاد کرده و آن را مقدار دهی میکنیم ، مراجعه مستقیم به محلی از حافظه که این متغیر ایجاد شده است ،  به ما نشان خواهد داد که مقدار موجود در این متغیر در آن محل از حافظه قرار دارد. به عبارت ساده تر متغیر دقیقا به ناحیه ای از حافظه اشاره میکند که مقدار متغیر در آن قرار گرفته است.

من برای نشان دادن این موضوع از یک مثال خیلی ساده استفاده کرده ام که از آنجاییکه نوع استفاده من از Pointerها از دیدگاه کامپایلر Safe و ایمن نیست اجبارا باید آن را در داخل یک بلاک unsafe قرار داده و ضمنا به کامپایلر گوشزد کنم که این کد را unsafe کامپایل نماید.

 

Namespace TypesInCSharp

{

    class Program

    {

        static void Main(string[] args)

        {

            unsafe

            {

                int j = 10;

                int* ip = &j;

                Console.WriteLine(“The value of j which means {0} stored in {1} location.”, j, (int)ip);

                Console.WriteLine(“Please press <Enter> key to exit…”);

                Console.ReadLine();

            }

        }

    }

}

 

 

در سیستم من پس از اجرا خروجی چیزی شبیه به زیر خواهد بود:

 

The value of j which means 10 stored in 92465428

Please press………………………………………..

 

تفسیر ساده این مثال به این صورت است که با تعریف متغیر j فضایی در حافظه تخصیص (Allocate) داده میشود که آدرس این ناحیه از حافظه  92465428 می باشد. در داخل این فضا عدد 4 قرار گرفته است.

زمانیکه متغیر دیگری با همین مقدار (یعنی مقدار 10) تعریف شود لزوما یک فضای جداگانه با یک آدرس جدا گانه در حافظه ایجاد خواهد شد که هیچ ارتباطی با متغیر قبلی ندارد. این دقیقا همان اتفاقیست  که یک متغیر Value Type مثل j وارد یک متد میشود و بنابر این با توجه به اینکه یک کپی از این متغیر ایجاد شده است، هر تغییری در داخل متد انجام شود هیچ تاثیری بر مقدار اصلی متغیر نخواهد داشت. این موضوع در مثال ساده زیر نشان داده شده است:

 

namespace TypesInCSharp

{

    class Program

    {

        static void Main(string[] args)

        {

            unsafe

            {

                int j = 10;

                Console.WriteLine("The value of p before method call is {0} ", j);

                TryToChangeValue(j);

                Console.WriteLine("The value of p after method still unchanged and is {0} ", j);

                Console.WriteLine("Please press <Enter> key to exit...");

                Console.ReadLine();

            }

        }

        public static void TryToChangeValue(int p)

        {

            p = 20;

            Console.WriteLine("The value of p inside method is {0} ", p);

        }

    }

}

 

متد TryToChangeValue سعی بیهوده ای میکند که مقدار پارامتر وارد شده را تغییر دهد. J  قبل از ورود به داخل متد دارای مقدار 10 میباشد . در ادامه j به عنوان پارامتر وارد متد میشود. در داخل متد چنانچه خروجی برنامه نیز نشان میدهد با اینکه پارامتر ورودی تغییر داده شده اما مقدار آن بدون تغییر باقی می ماند. زیرا که متد مذکور فقط به یک کپی از متغیر j دسترسی دارد و توانایی تغییر در متغیر اصلی J را ندارد.

 

The value of p before method call is 10

The value of p inside method is 20

The value of p after method still unchanged and is 10

Please press <Enter> key to exit...

 

  اما در مثال زیر:

 

namespace TypesInCSharp

{

    class SimpleClass

    {

        public int i ;

    }

    class Program

    {

        static void Main(string[] args)

        {

            unsafe

            {

                SimpleClass simpleClass = new SimpleClass();

                simpleClass.i = 10;

                Console.WriteLine("The value of simpleClass.i before method call is {0} ", simpleClass.i);

                TryToChangeValue(simpleClass);

                Console.WriteLine("The value of simpleClass.i after method changed and is {0} ", simpleClass.i);

                Console.WriteLine("Please press <Enter> key to exit...");

                Console.ReadLine();

            }

        }

        public static void TryToChangeValue(SimpleClass simpleclass)

        {

            simpleclass.i = 20;

            Console.WriteLine("The value of simpleclass.i inside method is {0} ", simpleclass.i);

        }

    }

}

 

که خروجی زیر را ایجاد میکند ماجرا به گونه دیگریست

 

The value of simpleClass.i before method call is 10

The value of simpleclass.i inside method is 20

The value of simpleClass.i after method changed and is 20

Please press <Enter> key to exit...

 

                    ادامه دارد ...

۰ نظر موافقین ۰ مخالفین ۰ ۰۲ اسفند ۹۲ ، ۲۱:۴۸
مهران حسین نیا