Those of you who worked with ASP.NET web forms will recollect that certain server controls such as DropDownList have a property called AutoPostBack. This property when set to true automatically submits the form to the server whenever the selection changes and raises some server side event. In modern web development people prefer to use Ajax over AutoPostBack but at times AutoPostBack is what you might need. To that end this article shows how AutoPostBack can be implemented in ASP.NET Core applications.
class ConcreteCreator : Creator { public override IProduct FactoryMethod(string type) { switch (type) { case "A": return new ConcreteProductA(); case "B": return new ConcreteProductB(); default: throw new ArgumentException("Invalid type", "type"); } } }
public interface IVehicleFactory { IDiesel GetDiesel(); IMotorCar GetMotorCar(); }
public class IranKhodro : IVehicleFactory { public IDiesel GetDiesel() { return new Arena(); } public IMotorCar GetMotorCar() { return new Peugeot206(); } } public class Saipa : IVehicleFactory { public IDiesel GetDiesel() { return new Foton(); } public IMotorCar GetMotorCar() { return new Peride(); } }
public interface IDiesel { string GetName();} public interface IMotorCar { string GetName();}
public class Foton : IDiesel { public string GetName() { return "This is Foton"; } } public class Arena : IDiesel { public string GetName() { return "This is Arena"; } } public class Peugeot206 : IMotorCar { public string GetName() { return "This is Peugeot206"; } } public class Peride : IMotorCar { public string GetName() { return "This is Peride"; } }
IVehicleFactory factory = new IranKhodro(); Console.WriteLine("***" + factory.GetType().Name + "***"); IDiesel diesel = factory.GetDiesel(); Console.WriteLine(diesel.GetName()); IMotorCar motorCar = factory.GetMotorCar(); Console.WriteLine(motorCar.GetName()); factory = new Saipa(); Console.WriteLine("***" + factory.GetType().Name + "***"); diesel = factory.GetDiesel(); Console.WriteLine(diesel.GetName()); motorCar = factory.GetMotorCar(); Console.WriteLine(motorCar.GetName());
- پیاده سازی و نامگذاری Product در Factory مربوطه متمرکز میشود و بدین ترتیب Client به نام و نحوه پیاده سازی Typeهای مختلف Product وابستگی نخواهد داشت.
- به راحتی میتوان Concrete Factory مورد استفاده در برنامه را تغییر داد، بدون اینکه تاثیری در عملکرد سایر بخشها داشته باشد.
- در مواردی که بیش از یک محصول برای هر خانواده وجود داشته باشد، استفاده از Abstract Factory تضمین میکند که Productهای هر خانواده همه در کنار هم قرار دارند و با هم فعال و غیر فعال میشوند. (یا همه، یا هیچکدام)
- Factoryها معمولا Singleton هستند. زیرا هر Application بطور معمول فقط به یک instance از هر Concrete Factory نیاز دارد.
- انتخاب Concrete Factory مناسب معمولا توسط پارامترهایی انجام میشود.
و کلام آخر در مورد این الگو:
- Abstract Factory یک interface یا کلاس abstract است که signature متدهای ساخت Objectها در آن تعریف شده است و Concrete Factoryها آنها را implement مینمایند.
- در Abstract Factory Pattern همه Productهای هم خانواده در Concrete Factory مربوط به آن خانواده پیاده سازی و مجتمع میگردند.
- در کدهای برنامه تنها با Abstract Factory و Abstract Productها سر و کار داریم و به هیچ وجه درگیر این مساله که کدام یک از Concrete Classها در برنامه مورد استفاده قرار میگیرند، نمیشویم.
- برنامههای وب نیازی به نصب بر روی تک تک کلاینتها و همچنین به روز رسانی مداوم کلاینتها را ندارند. به این صورت مدیریت چند صد کاربر در یک سازمان سادهتر از قبل خواهد بود. دیگر نگران این نخواهید بود که آیا فلان کاربر آخرین به روز رسانیها را نصب کرده (دریافت کرده) یا خیر.
- امکان دسترسی از راه دور، برای مثال از طریق اینترنت یا VPN یا RRAS و خطوط دایال آپ (برای مثال دسترسی سادهتر دفاتر مختلف یک سازمان به اطلاعات یکدیگر یا امکان داشتن کارکنانی که از راه دور برای شما کار میکنند).
- امکان ذخیره سازی دادهها در سازمانی دیگر (هاست کردن این برنامهها در محیطهای ابری(!) (cloud computing) هزینههای تهیه و نگهداری سخت افزارهای یک سازمان را نیز کاهش میدهند).
- کاهش هزینههای سازمان با توجه به اینکه اگر از سرورهای قدرتمندی استفاده شود؛ از یک برنامهی وب چندین هزار یا چند میلیون کاربر میتوانند استفاده کنند بدون اینکه نگران تامین هزینه مجوز استفاده از برنامهی تهیه شده به ازای هر کاربر باشید.
- امکان دسترسی به برنامهی وب تهیه شده در انواع و اقسام سیستم عاملهایی که تنها مجهز به یک مرورگر وب هستند (نتیجه نهایی قابل استفاده مستقل از سکو است). برای مثال این روزها به کمک Adobe AIR ، Silverlight و یا کتابخانههای اسکریپتی مانند jQuery و ASP.Net Ajax، بسیاری از تواناییهای نمایشی برنامههای دسکتاپ را در وب نیز میتوان شاهد بود با این خصوصیت که نتیجهی نهایی مستقل از سکو است.
- در این حالت کلاینتها نیازی به داشتن سخت افزارهای قوی ندارند (که در کاهش هزینههای یک سازمان مؤثر است). همچنین این برنامهها مشکلات ناسازگاری با سخت افزارها و نگارشهای مختلف سیستم عاملها را نیز ندارند. بنابراین یک سازمان میتواند بودجهی خود را صرف تهیهی سرورهای بهتری کند.
- کلاینتها با توجه به محدود بودن دسترسیهای امنیتی اعمالی توسط مرورگرها، امنیت بیشتری خواهند داشت. به همین ترتیب کاربران برای استفاده از این برنامهها نیز نیازی به دسترسی بالا در یک سازمان برای اجرای مرورگر خود نخواهند داشت (کمتر جملهی "من دسترسی ادمین میخواهم" را خواهید شنید).
- امکان مونیتور کردن سادهتر فعالیت کاربران در برنامه.
- در صورت محافظت از سرور، کدهای شما از خطر دزدیده شدن مصون(تر) هستند.
- مدیریت سادهتر و مجتمع اطلاعات تولیدی با توجه به اینکه همه چیز باید بر روی سرور ذخیره شود. به این صورت مدیریت نقل مکان کاربران از یک کامپیوتر به کامپیوتری دیگر نیز سادهتر می شود؛ زیرا چیزی را قرار نیست جابجا کنند (نه اطلاعات و نه برنامه را). اگر یکی از کامپیوترهای کلاینتها قابل استفاده نباشد، به سادگی میتواند از کامپیوتری دیگر در شبکه استفاده کند، بدون اینکه معطل تیم فنی شود تا برنامهای را برای او نصب و راه اندازی کنند. به علاوه تهیه پشتیبان از اطلاعات سرورها نیز همیشه سادهتر است از تهیه پشتیبان از 100 ها کامپیوتر موجود در شبکه.
- اگر خروجی برنامهی وب شما تنها از صفحات وب و جاوا اسکریپت تشکیل شده باشد، امکان دسترسی آن در دستگاههای موبایل به سادگی میسر است.
پیاده سازی generic repository یک ضد الگو است
الف) اگر از یک Generic Repository داخل کدهای یک مثلا کنترلر مستقیما استفاده میکنید، فقط خودتان را گول زدهاید! این طراحی نشتی دارد و همچنین حد و مرز مشخصی را نمیتوان برای آن قائل شد.
نشتی یا وابستگی که اینجا عنوان شده نمونهاش یک چنین متدی است:
IEnumerable<T> Find(Expression<Func<T, bool>> query);
نمونه خوبی را که مثال زده به صورت زیر است:
public IEnumerable<Customer> FindCustomerByName(string input) { return internalRepository.FindBy(c => c.Name == input); }
ب) الزامی به تعریف یک Generic Repository نیست اما اگر آنرا تعریف کردید، باید این کلاس خاص داخل کدهایی که منطق تجاری برنامه را با حد و مرز مشخصی کپسوله میکنند استفاده کنید. نه اینکه آنرا در خط مقدم کاری (مانند استفاده مستقیم در یک کنترلر) بکار بگیرید. به آن بیشتر به شکل یک سری متد کمکی نگاه کنید و بس. نامگذاری جالبی را هم در این حالت پیشنهاد داده: internalRepository
TFS یا GIT؟ از کدامیک استفاده کنم؟
- سایت BitBucket امکان تهیه مخزن خصوصی را هم میدهد. البته به نظر در تعداد افراد تیم محدودیت دارد.
اهمیت Controller های ساده در ASP.NET MVC
ولی متاسفانه این ایده که منطق تجاری را در کنترلر قرار دهیم کاملا غلط است.واضحترین دلیل هم این است که منطق تجاری ممکن است بین چندین استفاده کننده مشترک باشد، خیلی ساده فرض کنید قرار است نسخه وب و ویندوز یک سیستم ساخته شود.
مدیریت سفارشی سطوح دسترسی کاربران در MVC
ASP.NET Web API فریم ورکی برای ساختن APIهای وب بر روی فریم ورک دات نت است. در این مقاله با استفاده از این فریم ورک، API وبی خواهیم ساخت که لیستی از محصولات را بر میگرداند. صفحه وب کلاینت، با استفاده از jQuery نتایج را نمایش خواهد داد.
یک پروژه Web API بسازید
در ویژوال استودیو 2013 پروژه جدیدی از نوع ASP.NET Web Application بسازید و نام آن را "ProductsApp" انتخاب کنید.
در دیالوگ New ASP.NET Project قالب Empty را انتخاب کنید و در قسمت "Add folders and core references for" گزینه Web API را انتخاب نمایید.
می توانید از قالب Web API هم استفاده کنید. این قالب با استفاده از ASP.NET MVC صفحات راهنمای API را خواهد ساخت. در این مقاله از قالب Empty استفاده میکنیم تا تمرکز اصلی، روی خود فریم ورک Web API باشد. بطور کلی برای استفاده از این فریم ورک لازم نیست با ASP.NET MVC آشنایی داشته باشید.
افزودن یک مدل
یک مدل (model) آبجکتی است که داده اپلیکیشن شما را معرفی میکند. ASP.NET Web API میتواند بصورت خودکار مدل شما را به JSON, XML و برخی فرمتهای دیگر مرتب (serialize) کند، و سپس داده مرتب شده را در بدنه پیام HTTP Response بنویسد. تا وقتی که یک کلاینت بتواند فرمت مرتب سازی دادهها را بخواند، میتواند آبجکت شما را deserialize کند. اکثر کلاینتها میتوانند XML یا JSON را تفسیر کنند. بعلاوه کلاینتها میتوانند فرمت مورد نظرشان را با تنظیم Accept header در پیام HTTP Request مشخص کنند.
بگذارید تا با ساختن مدلی ساده که یک محصول (product) را معرفی میکند شروع کنیم.
کلاس جدیدی در پوشه Models ایجاد کنید.
نام کلاس را به "Product" تغییر دهید، و خواص زیر را به آن اضافه کنید.
namespace ProductsApp.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } }
افزودن یک کنترلر
در Web API کنترلرها آبجکت هایی هستند که درخواستهای HTTP را مدیریت کرده و آنها را به اکشن متدها نگاشت میکنند. ما کنترلری خواهیم ساخت که میتواند لیستی از محصولات، یا محصولی بخصوص را بر اساس شناسه برگرداند. اگر از ASP.NET MVC استفاده کرده اید، با کنترلرها آشنا هستید. کنترلرهای Web API مشابه کنترلرهای MVC هستند، با این تفاوت که بجای ارث بری از کلاس Controller از کلاس ApiController مشتق میشوند.
کنترلر جدیدی در پوشه Controllers ایجاد کنید.
در دیالوگ Add Scaffold گزینه Web API Controller - Empty را انتخاب کرده و روی Add کلیک کنید.
در دیالوگ Add Controller نام کنترلر را به "ProductsController" تغییر دهید و روی Add کلیک کنید.
توجه کنید که ملزم به ساختن کنترلرهای خود در پوشه Controllers نیستید، و این روش صرفا قراردادی برای مرتب نگاه داشتن ساختار پروژهها است. کنترلر ساخته شده را باز کنید و کد زیر را به آن اضافه نمایید.
using ProductsApp.Models; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Web.Http; namespace ProductsApp.Controllers { public class ProductsController : ApiController { Product[] products = new Product[] { new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } }; public IEnumerable<Product> GetAllProducts() { return products; } public IHttpActionResult GetProduct(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { return NotFound(); } return Ok(product); } }
کنترلر ما دو متد برای دریافت محصولات تعریف میکند:
- متد GetAllProducts لیست تمام محصولات را در قالب یک <IEnumerable<Product بر میگرداند.
- متد GetProductById سعی میکند محصولی را بر اساس شناسه تعیین شده پیدا کند.
همین! حالا یک Web API ساده دارید. هر یک از متدهای این کنترلر، به یک یا چند URI پاسخ میدهند:
URI | Controller Method |
api/products/ | GetAllProducts |
api/products/id/ | GetProductById |
برای اطلاعات بیشتر درباره نحوه نگاشت درخواستهای HTTP به اکشن متدها توسط Web API به این لینک مراجعه کنید.
فراخوانی Web API با جاوا اسکریپت و jQuery
در این قسمت یک صفحه HTML خواهیم ساخت که با استفاده از AJAX متدهای Web API را فراخوانی میکند. برای ارسال درخواستهای آژاکسی و بروز رسانی صفحه بمنظور نمایش نتایج دریافتی از jQuery استفاده میکنیم.
در پنجره Solution Explorer روی نام پروژه کلیک راست کرده و گزینه Add, New Item را انتخاب کنید.
در دیالوگ Add New Item قالب HTML Page را انتخاب کنید و نام فایل را به "index.html" تغییر دهید.
حال محتوای این فایل را با لیست زیر جایگزین کنید.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Product App</title> </head> <body> <div> <h2>All Products</h2> <ul id="products" /> </div> <div> <h2>Search by ID</h2> <input type="text" id="prodId" size="5" /> <input type="button" value="Search" onclick="find();" /> <p id="product" /> </div> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> <script> var uri = 'api/products'; $(document).ready(function () { // Send an AJAX request $.getJSON(uri) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); }); function formatItem(item) { return item.Name + ': $' + item.Price; } function find() { var id = $('#prodId').val(); $.getJSON(uri + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); } </script> </body> </html>
گرفتن لیستی از محصولات
برای گرفتن لیستی از محصولات، یک درخواست HTTP GET به آدرس "api/products/" ارسال کنید.
تابع getJSON یک درخواست آژاکسی ارسال میکند. پاسخ دریافتی هم آرایه ای از آبجکتهای JSON خواهد بود. تابع done در صورت موفقیت آمیز بودن درخواست، اجرا میشود. که در این صورت ما DOM را با اطلاعات محصولات بروز رسانی میکنیم.
$(document).ready(function () { // Send an AJAX request $.getJSON(apiUrl) .done(function (data) { // On success, 'data' contains a list of products. $.each(data, function (key, item) { // Add a list item for the product. $('<li>', { text: formatItem(item) }).appendTo($('#products')); }); }); });
گرفتن محصولی مشخص
برای گرفتن یک محصول توسط شناسه (ID) آن کافی است یک درخواست HTTP GET به آدرس "api/products/id/" ارسال کنید.
function find() { var id = $('#prodId').val(); $.getJSON(apiUrl + '/' + id) .done(function (data) { $('#product').text(formatItem(data)); }) .fail(function (jqXHR, textStatus, err) { $('#product').text('Error: ' + err); }); }
اجرای اپلیکیشن
اپلیکیشن را با F5 اجرا کنید. صفحه وب باز شده باید چیزی مشابه تصویر زیر باشد.
برای گرفتن محصولی مشخص، شناسه آن را وارد کنید و روی Search کلیک کنید.
اگر شناسه نامعتبری وارد کنید، سرور یک خطای HTTP بر میگرداند.
استفاده از F12 برای مشاهده درخواستها و پاسخ ها
هنگام کار با سرویسهای HTTP، مشاهدهی درخواستهای ارسال شده و پاسخهای دریافتی بسیار مفید است. برای اینکار میتوانید از ابزار توسعه دهندگان وب استفاده کنید، که اکثر مرورگرهای مدرن، پیاده سازی خودشان را دارند. در اینترنت اکسپلورر میتوانید با F12 به این ابزار دسترسی پیدا کنید. به برگه Network بروید و روی Start Capturing کلیک کنید. حالا صفحه وب را مجددا بارگذاری (reload) کنید. در این مرحله اینترنت اکسپلورر ترافیک HTTP بین مرورگر و سرور را تسخیر میکند. میتوانید تمام ترافیک HTTP روی صفحه جاری را مشاهده کنید.
به دنبال آدرس نسبی "api/products/" بگردید و آن را انتخاب کنید. سپس روی Go to detailed view کلیک کنید تا جزئیات ترافیک را مشاهده کنید. در نمای جزئیات، میتوانید headerها و بدنه درخواستها و پاسخها را ببینید. مثلا اگر روی برگه Request headers کلیک کنید، خواهید دید که اپلیکیشن ما در Accept header دادهها را با فرمت "application/json" درخواست کرده است.
اگر روی برگه Response body کلیک کنید، میتوانید ببینید چگونه لیست محصولات با فرمت JSON سریال شده است. همانطور که گفته شده مرورگرهای دیگر هم قابلیتهای مشابهی دارند. یک ابزار مفید دیگر Fiddler است. با استفاده از این ابزار میتوانید تمام ترافیک HTTP خود را مانیتور کرده، و همچنین درخواستهای جدیدی بسازید که این امر کنترل کاملی روی HTTP headers به شما میدهد.
قدمهای بعدی
امروزه توسعه و نگه داری از یک نرم افزار هر چند مهمتر از مراحل ساخت آن نباشد دارای ارزش کمتری نیست! همهی ما مجبوریم پس از مدتی به پروژهی گذشتهی خود بر گردیم و اینجاست که اگر از خط و محورهای خاصی در تولید نرم افزار پیروی نکرده باشیم ارزش اینگونه Patternهای ساخت نرم افزار را درک میکنیم ، داشتن یک خط مشی مشخص در تولید و نگه داری نرم افزار قطعن باعث از بین رفتن سر دردهای آینده خواهد شد و مهمتر از آن باعث کاهش هزینههای بعد از تولید نرم افزار خواهد بود
در این کتاب نه تنها با پترن MVVM آشنا میشوید بلکه در راه رسیدن به این موضوع نویسنده شما را با انواع وجههای مختلف در ساخت یک برنامهی تجاری آشنا و آماده خواهد کرد!