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

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

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

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

پنجشنبه, ۱ خرداد ۱۳۹۳، ۰۳:۴۱ ب.ظ

گزارش یک تجربـه در 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 میلی ثانیه توقف داشته است که با بهینه سازی کد و روشها میتوان این مقدار را تا یک سوم مقدار فعلی کاهش داد .




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

 

موافقین ۰ مخالفین ۰ ۹۳/۰۳/۰۱
مهران حسین نیا

Serialize

JSON

Razor

AJAX

getJSON

Controller

JQuery

MVC

نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی