نگاهی به گزینههای مختلف مهیای جهت میزبانی SignalR
1) OWIN
2) ASP.NET Hosting
3) Self Hosting
4) Cloud و ویندوز Azure
1) OWIN
اگر به اسمبلیهای همراه با SignalR دقت کنید، یکی از آنها Microsoft.AspNet.SignalR.Owin.dll نام دارد. OWIN مخفف Open web server interface for .NET است و کار آن ایجاد لایهای بین وب سرورها و برنامههای وب میباشد. یکی از اهداف مهم آن ترغیب دنیای سورس باز به تهیه ماژولهای مختلف قابل استفاده در وب سرورهای دات نتی است. نکتهی مهمی که در SignalR و کلیه میزبانهای آن وجود دارد، بنا شدن تمامی آنها برفراز OWIN میباشد.
2) ASP.NET Hosting
بدون شک، میزبانی ASP.NET از هابهای SignalR، مرسومترین روش استفاده از این فناوری میباشد. این نوع میزبانی نیز برفراز OWIN بنا شده است. نصب آن توسط اجرای دستور پاور شل ذیل در یک پروژه وب صورت میگیرد:
PM> Install-Package Microsoft.AspNet.SignalR
3) خود میزبانی یا Self hosting
خود میزبانی نیز برفراز OWIN تهیه شده است و برای پیاده سازی آن نیاز است وابستگیهای مرتبط با آن، از طریق NuGet به کمک فرامین پاور شل ذیل دریافت شوند:
PM> Install-Package Microsoft.AspNet.SignalR.Owin PM> Install-Package Microsoft.Owin.Hosting -Pre PM> Install-Package Microsoft.Owin.Host.HttpListener -Pre
مراحل تهیه یک برنامه ثالث (برای مثال خارج از IIS یا یک وب سرور آزمایشی) به عنوان میزبان Hubs مورد نیاز به این شرح هستند:
الف) کلاس آغازین میزبان باید با پیاده سازی اینترفیسی به نام IAppBuilder تهیه شود.
ب) مسیریابیهای مورد نیاز تعریف گردند.
ج) وب سرور HTTP یا HTTPS توکار برای سرویس دهی آغاز گردد.
باید توجه داشت که در این حالت برخلاف روش ASP.NET Hosting، سایر اسمبلیهای برنامه جهت یافتن Hubهای تعریف شده، اسکن نمیشوند. همچنین هنگام کار با jQuery مباحث عنوان شده در مورد تنظیم دسترسیهای Cross domain نیز باید در اینجا اعمال گردند. به علاوه اجرای وب سرور توکار آن به دلایل امنیتی، نیاز به دسترسی مدیریتی دارد.
برای پیاده سازی یک نمونه، به برنامهای که تاکنون تهیه کردهایم، یک پروژه کنسول دیگر را به نام ConsoleHost اضافه کنید. البته باید درنظر داشت در دنیای واقعی این نوع برنامهها را عموما از نوع سرویسهای ویندوز NT تهیه میکنند.
در ادامه سه فرمان پاور شل یاد شده را برای افزودن وابستگیهای مورد نیاز فراخوانی نمائید. همچنین باید دقت داشت که این دستور بر روی پروژه جدید اضافه شده باید اجرا گردد.
using System; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.Owin.Hosting; using Owin; namespace SignalR02.ConsoleHost { public class Startup { public void Configuration(IAppBuilder app) { app.MapHubs(new HubConfiguration { EnableCrossDomain = true }); } } [HubName("chat")] public class ChatHub : Hub { public void SendMessage(string message) { var msg = string.Format("{0}:{1}", Context.ConnectionId, message); Clients.All.hello(msg); } } class Program { static void Main(string[] args) { using (WebApplication.Start<Startup>("http://localhost:1073/")) { Console.WriteLine("Press a key to terminate the server..."); Console.Read(); } } } }
سمت کلاینت استفاده از آن هیچ تفاوتی نمیکند و با جزئیات آن پیشتر آشنا شدهاید؛ برای مثال در کلاینت جیکوئری خاصیت connection.hub.url باید به مسیر جدید سرور هاب تنظیم گردد تا اتصالات به درستی برقرار شوند.
دریافت پروژه کامل مرتبط با این 4 قسمت (البته بدون فایلهای باینری آن، جهت کاهش حجم 32 مگابایتی)
SignalRSamples.zip
آموزش Knockout.Js #2
»Observable(قابل مشاهده کردن تغییرات)
KO از Observable برای ردیابی و مشاهده تغییرات خواص ViewModel استفاده میکند. در واقع Observable دقیقا شبیه به متغیرها در JavaScript عمل میکنند با این تفاوت که به KO اجازه میدهند که تغییرات این خواص را پیگیری کند و این تغییرات را به بخشهای مرتبط View اعمال نماید. اما سوال این است که KO چگونه متوجه میشود که این تغییرات بر کدام قسمت در View تاثیر خواهند داشت؟ جواب این سوال در مفهوم Binding است.
»Binding
برای اتصال بخشهای مختلف View به Observableها باید از binding(مقید سازی) استفاده کنیم. بدون عملیات binding، امکان اعمال تغییرات Observableها بر روی عناصر HTML امکان پذیر نیست.
برای مثال در شکل زیر یکی از خواص ViewModel را به View متناظر مقید شده است.
با کمی دقت در شکل بالا این نکته به دست میآید که میتوان در یک ViewModel، فقط خواص مورد نظر را به عناصر Html مقید کرد.
دانلود فایلهای مورد نیاز
فایلهای مورد نیاز برای KO رو میتوانید از اینجا
دانلود نمایید و به پروژه اضافه کنید. به صورت پیش فرض فایلهای مورد
نیاز KO، در پروژههای MVC 4 وجود دارد و نیاز به دانلود آنها نیست و شما
باید فقط مراحل BundleConfig را انجام دهید.
تعریف ViewModel
برای تعریف ViewModel و پیاده سازی مراحل Observable و binding باید به صورت زیر عمل نمایید:
<html lang='en'> <head> <title>Hello, Knockout.js</title> <meta charset='utf-8' /> <link rel='stylesheet' href='style.css' /> </head> <body> <h1>Hello, Knockout.js</h1> <script type='text/javascript' src='knockout-2.1.0.js'> <script type='text/javascript'> var personViewModel = { firstName: "Masoud", lastName: "Pakdel" }; ko.applyBindings(personViewModel); </script> </script> </body> </html>
مقید سازی عناصر HTML
برای مقید سازی عناصر HTML به ViewModelها باید از data-bind attribute استفاده نماییم. برای مثال:
<p><span data-bind='text: firstName'></span>'s Shopping Cart</p>
چگونه خواص را Observable کنیم
در پروژههای WPF، فقط در صورتی تغییرات خواص یک کلاس ردیابی میشوند که اولا کلاس اینترفیس INotifyPropertyChanged را پیاده سازی کرده باشد ثانیا، در متد set این خواص، متد OnPropertyChanged(البته این متد میتواند هر نام دیگری نیز داشته باشد) صدا زده شده باشد. نکته مهم و اساسی در KO نیز همین است که برای اینکه KO بتواند تغییرات هر خاصیت را مشاهده کند حتما خواص مورد نظر باید Observable شوند. برای این کار کافیست به صورت عمل کنید:
var personViewModel = { firstName: ko.observable("Masoud"), lastName: ko.observable("Pakdel") };
پیاده سازی متدهای get و set
personViewModel.firstName() // Get personViewModel.firstName("Masoud") // Set
function PersonViewModel() { this.firstName = ko.observable("Masoud"); this.lastName = ko.observable("Pakdel"); this.clickMe= function() { alert("this is test!"); }; };
<button data-bind='click: clickMe'>Click Me...</button>
پس به صورت خلاصه:
- ابتدا ViewModel مورد نظر را ایجاد نمایید؛
- سپس با استفاده از data-bind عملیات مقید سازی بین View و ViewModel را انجام دهید
- در نهایت با استفاده از Obsevable تغییرات خواص مورد نظر را ردیابی نمایید.
ادامه دارد...
Dynamic Semantics
Objectها علاوه بر داده و رفتار به عنوان توصیفات ثابت، در زمان اجرا دارای یک Local State (a snapshot) از مقادیر داینامیک مربوط به اعضای دادهای خود، میباشند. مجموعه تمام حالاتی که وهلههای یک کلاس میتوانند بین آنها گذر (transition) داشته باشد، dynamic semantics مربوط به کلاس نامیده میشود و به وهلههای کلاس این امکان را میدهند تا به یک پیغام مشابه رسیده و در زمانهای مختلف از چرخه زندگی خود، به اشکال مختلف پاسخ دهند.
Method junk for the class X if (local state #1) then do something else if (local state #2) then do something different End Method
بخش اصلی هر طراحی شیء گرا، dynamic semantics وهلهها میباشد. dynamic semantics هر کلاسی باید در قالب یک دیاگرام state-transition مستند شود. شکل زیر dynamic semantics پروسههای موجود در یک سیستم عامل را در قابل یک دیاگرام حالت نمایش میدهد. این پروسهها توانایی این را دارند که در هر کدام از حالات: runnable، current process، blocked، sleeping و یا در حالت exited، قرار داشته باشند. همچنین به عنوان مثال، یک پروسه زمانی میتواند در حالت current process قرار گیرد که حتما قبلا در حالت runnable قرار داشته باشد. این اطلاعات برای ایجاد تست برای کلاسها و وهلههای آنها میتواند مفید واقع شود.
شکل 2.8 State-transition diagram notation
برخی از طراحان به طور تصادفی، dynamic semantics یک کلاس را به عنوان static semantics آن کلاس مدل میکنند. به عنوان مثال اگر color یکی از اعضای داده ای (data member) کلاس توپ باشد و بعد از وهله سازی از کلاس توپ، color آن بازهم قابل تغییر باشد، منظور اینکه توپ آبی به عنوان یک وهله از کلاس توپ در زمان حیات خود تغییر رنگ دهد، اصطلاحا میگویند: color جزء dynamic semantics کلاس توپ میباشد. با توجه به توضحیاتی که داده شد، حال اگر طراحی برای هر رنگ توپ یک کلاس جدا در نظر گرفته باشد، dynamic semantics را به عنوان static semantics مدل کرده و به احتمال زیاد ما را به سمت ایجاد مشکل Class Proliferation (ازدیاد کلاس ها) سوق خواهد داد.
Abstract Classes
به سوالات زیر توجه کنید:
- آیا هرگز میوه خوردهاید؟
- آیا هرگز پیش غذا خوردهاید؟
- آیا هرگز دسر خوردهاید؟
حال با توجه به سوالات «مزه غذا چطور بود؟ دسری که خوردید، چه تعداد کالری داشت؟ هزینه پیش غذایی که خوردید چقدر بود» پاسخ چه خواهد بود؟
من (نویسنده) ادعا میکنم که هیچ کسی تا به حال میوه نخورده است. بیشتر مردم، سیب، موز و پرتقال خوردهاند؛ میوهی قرمز رنگی به ارزش 3 پوند را نخوردهاند.
شبیه به این مسئله برای زمانی است که گارسون رستوران از شما سوال میکند: «برای شام چه چیزی میل دارید» و شما جواب میدهید: «یک پیش غذا، یک غذای اصلی و یک دسر». در این حالت چون شما دقیقا مشخص نکردهاید چه نوعی میخواهید، گارسون، مات و مبهوت خواهد ماند. همه میدانیم که چیزی تحت عنوان میوه، پیش غذا و یا وهله دسر در واقعیت وجود ندارد؛ بله این عبارات اطلاعات مفیدی را تسخیر میکنند. اگر من در دستم یک ساعت زنگی گرفته و از شما میپرسیدم: «نظرتان در مورد میوه من چیست؟»؛ بدون شک فکر میکردید من دیوانه شدهام. حال اگر در دستم سیبی گرفته و سوال قبلی را میپرسیدم، این بار از نظر شما من یک شخص عاقل بودم.
با وجود اینکه نمیتوان از میوه وهله سازی کرد، اما اطلاعات مفیدی را تسخیر میکند. در واقع میوه، یک کلاسی (concept) است که دانشی از نحوه وهله سازی وهله هایش به وسیله Type پیاده ساز خود، ندارد.
کلاسی که دانشی از نحوه وهله سازی وهلههای خود ندارد، abstract class (کلاس مجرد یا انتزاعی) نامیده میشود.
کلاسی که دانش نحوه وهله سازی وهلههای خود دارد، concrete class نامیده میشود.
در پارادایم شیء گرا، مهمترین استفاده از کلاسهای انتزاعی در مباحث ارث بری مطرح میشود.
Roles Versus Classes
مطمئن باشید انتزاع هایی را که مدل میکنید کلاس بوده و نه نقشهایی که وهلههای آنها بازی میکنند. (Be sure the abstractions that you model are classes and not simply the roles objects play)آیا مادر و پدر به عنوان یک کلاس هستند یا نقشهایی هستند که وهلههای کلاس شخص، بازی میکند؟ پاسخ این سوال وابسته به دامینی (domain) است که طراح در حال مدل سازی آن میباشد. اگر در دامین مورد نظر، مادر و پدر رفتارهای مختلفی دارند، احتمالا باید به عنوان کلاسهای جدا مدل شوند. اگر رفتارهای یکسانی دارند، در نتیجه نقشهای مختلفی هستند که وهلههای کلاس شخص بازی میکنند. به عنوان مثال، میتوان کلاس خانواده را متشکل از وهلهای از کلاس پدر، وهلهای از کلاس مادر و مجموعهای از وهلههای کلاس فرزند در نظر گرفت. در مقابل ممکن است کلاس خانواده را متشکل از وهلهای از کلاس شخص به عنوان پدر، وهلهای از کلاس شخص به عنوان مادر و آرایهای از وهلههای شخص به عنوان فرزندان، مدل کنید. قرار گرفتن در وضیعتی که هر نقش، بخشی از رفتاریهای شخص را مورد استفاده قرار میدهد، کافی نیست و باید مطمئن شوید که رفتارها واقعا متفاوت میباشند. همچنین باید به یاد داشته باشید که زمانیکه وهلهای از بخشی از رفتارهای کلاس خود استفاده میکند، نیز مشکلی وجود ندارد و لازم نیست کلاسهای دیگری را به خاطر این موضوع در طراحی خود در نظر بگیرید.
شکل 2.9 Two views of a family
برخی از طراحان به این شکل تست میکنند که اگر عضوی از واسط عمومی را نمیتوان برای نقش مورد نظر مورد استفاده قرار داد، این موضوع نشان از این دارد که باید برای نقش مورد نظر در طراحی خود کلاس جداگانهای را در نظر داشته باشند. اگر هم عضو مذکور قابل استفاده نباشد، کلاس یکسانی برای نقشهای مختلف استفاده خواهد شد. به عنوان مثال، اگر عملیات ()go_into_labor جزء عملیاتی میباشد که مادر انجام میدهد، در حالیکه پدر چنین عملیاتی را نمیتواند انجام دهد، در این حالت نیز لازم است مادر به عنوان کلاس جداگانهای در نظر گرفته شود. اگر در دامین دیگری، عوض کردن پوشاک را تنها مادر انجام میدهد، در این حالت مادر نقشی از کلاس شخص میباشد، چرا که پدر هم توانایی انجام این عملیات را دارد.
قواعد شهودی فصل دوم
همه دادهها باید در داخل کلاس خود پنهان شده باشند. (All data should be hidden within its class)
استفاده کنندگان از کلاس باید به واسط عمومی آن وابسته باشند، اما یک کلاس نباید به استفاده کنندگان خود، وابسته باشد. (Users of a class must be dependent on its public interface, but a class should not be dependent on its users)
تعداد پیغامهای موجود در قرارداد یک کلاس را کمینه سازید. (Minimize the number of messages in the protocol of a class)
پیاده سازی یک واسط عمومی یکسان کمینه برای همه کلاسها (Implement a minimal public interface that all classes understand [e.g., operations such as copy (deep versus shallow), equality testing, pretty printing, parsing from an ASCII description, etc.].)
جزئیات پیاده سازی، مانند توابع خصوصی common-code ( توابعی که کد مشترک سایر متدهای کلاس را در بدنه خود دارند) را در واسط عمومی یک کلاس قرار ندهید. (Do not put implementation details such as common-code private functions into the public interface of a class)
واسط عمومی کلاس را با اقلامی که یا استفاده کنندگان از کلاس توانایی استفاده از آن را نداشته و یا تمایلی به استفاده از آنها ندارند، آمیخته نکنید. (Do not clutter the public interface of a class with items that users of that class are not able to use or are not interested in using )
اتصال و پیوستگی مابین کلاسها باید از نوع Nil یا Export باشد؛ به این معنی که یک کلاس فقط از واسط عمومی کلاس دیگر استفاده کند یا کاری با آن نداشته باشد. (Classes should only exhibit nil or export coupling with other classes, that is, a class should only use operations in the public interface of another class or have nothing to do with that class.)
یک کلاس باید یک و تنها یک Key Abstraction را تسخیر نماید. (A class should capture one and only one key abstraction)
داده و رفتار مرتبط را در یک جا (کلاس) نگه دارید. (Keep related data and behavior in one place)
اطلاعات نامرتبط به هم را در کلاسهای جدا از هم قرار دهید. ((Spin off nonrelated information into another class (i.e., noncommunicating behavior)
مطمئن باشید انتزاع هایی را که مدل میکنید کلاس بوده و نه نقشهایی که وهلههای آنها بازی میکنند. (Be sure the abstractions that you model are classes and not simply the roles objects play)
نمونهی این نکته را پیشتر با Structure Map ملاحظه کرده بودید. پیاده سازی آن با امکانات توکار تزریق وابستگیهای NET Core. یا بر اساس روش Factory است که در نکتهی قبل ملاحظه میکنید و یا اگر از یک اینترفیس چندین پیاده سازی در برنامه وجود داشته باشند و ارتباطات آنها در ابتدای کار برنامه به سیستم توکار تزریق وابستگیهای NET Core. معرفی شده باشند، فقط کافی است یک <IEnumerable<IMultiple را به سازندهی کلاس سرویس استفاده کننده تزریق کنیم:
private readonly IEnumerable<IMultiple> _services; public HomeController (IEnumerable<IMultiple> services) { _services = services; }
مرحلهی بعد، تشخیص و یا انتخاب یک پیاده سازی خاص است. الان لیستی از وهلههای تزریق شده را در اختیار داریم؛ اما میخواهیم فقط از یکی از آنها استفاده کنیم:
الف) انتخاب سرویس مدنظر بر اساس نوع کلاسی خاص
var serviceA = services.First(o => o.GetType() == typeof(ImplementationOne));
این روشی است که برای مثال در Structure Map هم استفاده میشود (تحت عنوان named instances). یک خاصیت Name را به اینترفیسی که چندین پیاده سازی دارد، اضافه کنید. سپس بر اساس این Name کوئری بگیرید:
var serviceB = services.First(o => o.Name.Equals("MyClassName"));
var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); var createCommand = connection.CreateCommand(); createCommand.CommandText = @" CREATE TABLE persian_letter ( value TEXT ); INSERT INTO persian_letter VALUES ('ا'),('ب'),('پ'),('ت'),('ث'),('ج'),('چ'),('ح'),('خ'),('د'),('ذ'),('ر'),('ز'),('ژ'),('س'),('ش'), ('ص'),('ض'),('ط'),('ظ'),('ع'),('غ'),('ف'),('ق'),('ک'),('گ'),('ل'),('م'),('ن'),('و'),('ه'),('ی'); "; createCommand.ExecuteNonQuery();
var queryCommand = connection.CreateCommand(); queryCommand.CommandText = @" SELECT value FROM persian_letter order by value "; var reader = queryCommand.ExecuteReader(); var sortedDbItems = new List<string>(); while (reader.Read()) { sortedDbItems.Add($"{reader["value"]}"); }
همانطور که ملاحظه میکنید، مرتب سازی حروف فارسی در اینجا به صورت پیشفرض کار نمیکند. علت اینجا است که روش پیشفرض مرتب سازی حروف در SQLite، بر اساس کد اسکی حروف است و فقط در مورد حروف ASCII از A تا Z درست کار میکند.
امکان تعریف Collation سفارشی در SQLite
در بانکهای اطلاعاتی، قابلیتی که مستقیما بر روی نحوهی جستجو و همچنین مرتب سازی حروف تاثیر میگذارد، Collation نام دارد و در SQLite برخلاف بسیاری از بانکهای اطلاعاتی دیگر، امکان تعریف Collation سفارشی نیز وجود دارد و برای این منظور باید یک function pointer را در اختیار آن قرار داد تا از آن در سمت بانک اطلاعاتی جهت مرتب سازی و جستجوی حروف استفاده کند.
خوشبختانه پروژهی Microsoft.Data.Sqlite امکان تبدیل یک managed delegate دات نتی را به یک function pointer مخصوص SQLite، میسر میکند. به عبارتی SQLite کدهای دات نتی را در حین انجام محاسبات خود اجرا خواهد کرد و این اجرا به صورتی نیست که ابتدا کل اطلاعات، به سمت برنامهی کلاینت منتقل شود و سپس در این سمت، در حافظه، عملیاتی بر روی آن صورت گیرد. کل عملیات در سمت بانک اطلاعاتی مدیریت میشود.
روش تعریف یک Collation جدید هم در اینجا بسیار سادهاست:
connection.CreateCollation("PersianCollationNoCase", (x, y) => string.Compare(x, y, ignoreCase: true));
SELECT value FROM persian_letter order by value COLLATE PersianCollationNoCase
تعریف Collation سفارشی غیرحساس به «ی و ک» !
این مورد شاید یکی از آرزوهای توسعه دهندگان SQL Server باشد! اما با SQLite به سادگی زیر قابل تعریف و مدیریت است:
connection.CreateCollation("PersianCollationNoCaseYekeInsensitive", (x, y) => string.Compare(x.ApplyCorrectYeKe(), y.ApplyCorrectYeKe(), ignoreCase: true));
در یک چنین حالتی اگر اطلاعاتی را به همراه «ی و ک» فارسی و یا عربی ثبت کنیم:
CREATE TABLE persian_letter ( value TEXT ); INSERT INTO persian_letter VALUES ('ی'),('ک');
SELECT count() FROM persian_letter WHERE value = 'ی' COLLATE PersianCollationNoCaseYekeInsensitive
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: SQLitePersianCollation.zip
در ادامه، تعاریف سایر موجودیتهای سیستم ثبت سفارشات و نگاشت آنها را بررسی خواهیم کرد.
کلاس Product تعریف شده در فایل جدید Product.cs در پوشه domain برنامه:
namespace NHSample1.Domain
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal UnitPrice { get; set; }
public bool Discontinued { get; set; }
}
}
using FluentNHibernate.Mapping;
using NHSample1.Domain;
namespace NHSample1.Mappings
{
public class ProductMapping : ClassMap<Product>
{
public ProductMapping()
{
Not.LazyLoad();
Id(p => p.Id).GeneratedBy.HiLo("1000");
Map(p => p.Name).Length(50).Not.Nullable();
Map(p => p.UnitPrice).Not.Nullable();
Map(p => p.Discontinued).Not.Nullable();
}
}
}
آزمون واحد بررسی این نگاشت نیز همانند مثال قبلی است.
کلاس ProductMapping_Fixture را در فایل جدید ProductMapping_Fixture.cs به پروژه UnitTests خود (که ارجاعات آنرا در قسمت قبل مشخص کردیم) خواهیم افزود:
using NUnit.Framework;
using FluentNHibernate.Testing;
using NHSample1.Domain;
namespace UnitTests
{
[TestFixture]
public class ProductMapping_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_product()
{
new PersistenceSpecification<Product>(Session)
.CheckProperty(p => p.Id, 1001)
.CheckProperty(p => p.Name, "Apples")
.CheckProperty(p => p.UnitPrice, 10.45m)
.CheckProperty(p => p.Discontinued, true)
.VerifyTheMappings();
}
}
}
ProductMapping_Fixture.can_correctly_map_product : Passed
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO "Product" (Name, UnitPrice, Discontinued, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 'Apples', @p1 = 10.45, @p2 = True, @p3 = 1001
NHibernate: SELECT product0_.Id as Id1_0_, product0_.Name as Name1_0_, product0_.UnitPrice as UnitPrice1_0_, product0_.Discontinued as Disconti4_1_0_ FROM "Product" product0_ WHERE product0_.Id=@p0;@p0 = 1001
در ادامه تعریف کلاس کارمند، نگاشت و آزمون واحد آن به صورت زیر خواهند بود:
using System;
namespace NHSample1.Domain
{
public class Employee
{
public int Id { set; get; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
}
using NHSample1.Domain;
using FluentNHibernate.Mapping;
namespace NHSample1.Mappings
{
public class EmployeeMapping : ClassMap<Employee>
{
public EmployeeMapping()
{
Not.LazyLoad();
Id(e => e.Id).GeneratedBy.Assigned();
Map(e => e.LastName).Length(50);
Map(e => e.FirstName).Length(50);
}
}
}
using NUnit.Framework;
using NHSample1.Domain;
using FluentNHibernate.Testing;
namespace UnitTests
{
[TestFixture]
public class EmployeeMapping_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_employee()
{
new PersistenceSpecification<Employee>(Session)
.CheckProperty(p => p.Id, 1001)
.CheckProperty(p => p.FirstName, "name1")
.CheckProperty(p => p.LastName, "lname1")
.VerifyTheMappings();
}
}
}
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO "Employee" (LastName, FirstName, Id) VALUES (@p0, @p1, @p2);@p0 = 'lname1', @p1 = 'name1', @p2 = 1001
NHibernate: SELECT employee0_.Id as Id4_0_, employee0_.LastName as LastName4_0_, employee0_.FirstName as FirstName4_0_ FROM "Employee" employee0_ WHERE employee0_.Id=@p0;@p0 = 1001
همانطور که ملاحظه میکنید، این آزمونهای واحد 4 مرحله را در یک سطر انجام میدهند:
الف) ایجاد یک وهله از کلاس Employee
ب) ثبت اطلاعات کارمند در دیتابیس
ج) دریافت اطلاعات کارمند در وهلهای جدید از شیء Employee
د) و در پایان بررسی میکند که آیا شیء جدید ایجاد شده با شیء اولیه مطابقت دارد یا خیر
اکنون در ادامه پیاده سازی سیستم ثبت سفارشات، به قسمت جالب این مدل میرسیم. قسمتی که در آن ارتباطات اشیاء و روابط one-to-many تعریف خواهند شد. تعاریف کلاسهای OrderItem و OrderItemMapping را به صورت زیر در نظر بگیرید:
کلاس OrderItem تعریف شده در فایل جدید OrderItem.cs واقع شده در پوشه domain پروژه:
که در آن هر سفارش (order) دقیقا از یک محصول (product) تشکیل میشود و هر محصول میتواند در سفارشات متعدد و مختلفی درخواست شود.
namespace NHSample1.Domain
{
public class OrderItem
{
public int Id { get; set; }
public int Quantity { get; set; }
public Product Product { get; set; }
}
}
using FluentNHibernate.Mapping;
using NHSample1.Domain;
namespace NHSample1.Mappings
{
public class OrderItemMapping : ClassMap<OrderItem>
{
public OrderItemMapping()
{
Not.LazyLoad();
Id(oi => oi.Id).GeneratedBy.Assigned();
Map(oi => oi.Quantity).Not.Nullable();
References(oi => oi.Product).Not.Nullable();
}
}
}
نکتهی دیگر مهم آن این مورد است که Id در اینجا به صورت یک کلید تعریف نشده است. یک آیتم سفارش داده شده، موجودیت به حساب نیامده و فقط یک شیء مقداری (value object) است و به خودی خود امکان وجود ندارد. هر وهله از آن تنها توسط یک سفارش قابل تعریف است. بنابراین id در اینجا فقط به عنوان یک index میتواند مورد استفاده قرار گیرد و فقط توسط شیء Order زمانیکه یک OrderItem به آن اضافه میشود، مقدار دهی خواهد شد.
اگر برای این نگاشت نیز آزمون واحد تهیه کنیم، به صورت زیر خواهد بود:
using NUnit.Framework;
using NHSample1.Domain;
using FluentNHibernate.Testing;
namespace UnitTests
{
[TestFixture]
public class OrderItemMapping_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_order_item()
{
var product = new Product
{
Name = "Apples",
UnitPrice = 4.5m,
Discontinued = true
};
new PersistenceSpecification<OrderItem>(Session)
.CheckProperty(p => p.Id, 1)
.CheckProperty(p => p.Quantity, 5)
.CheckReference(p => p.Product, product)
.VerifyTheMappings();
}
}
}
مشکل! این آزمون واحد با شکست مواجه خواهد شد، زیرا هنوز مشخص نکردهایم که دو شیء Product را که در قسمت CheckReference فوق برای این منظور معرفی کردهایم، چگونه باید با هم مقایسه کرد. در مورد مقایسه نوعهای اولیه و اصلی مانند int و string و امثال آن مشکلی نیست، اما باید منطق مقایسه سایر اشیاء سفارشی خود را با پیاده سازی اینترفیس IEqualityComparer دقیقا مشخص سازیم:
using System.Collections;
using NHSample1.Domain;
namespace UnitTests
{
public class CustomEqualityComparer : IEqualityComparer
{
public bool Equals(object x, object y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
if (x is Product && y is Product)
return (x as Product).Id == (y as Product).Id;
if (x is Customer && y is Customer)
return (x as Customer).Id == (y as Customer).Id;
if (x is Employee && y is Employee)
return (x as Employee).Id == (y as Employee).Id;
if (x is OrderItem && y is OrderItem)
return (x as OrderItem).Id == (y as OrderItem).Id;
return x.Equals(y);
}
public int GetHashCode(object obj)
{
//شاید وقتی دیگر
return obj.GetHashCode();
}
}
}
سپس برای بکار گیری این کلاس جدید، سطر مربوط به استفاده از PersistenceSpecification به صورت زیر تغییر خواهد کرد:
new PersistenceSpecification<OrderItem>(Session, new CustomEqualityComparer())
پس از این تغییرات و مشخص سازی نحوهی مقایسه دو شیء سفارشی، آزمون واحد ما پاس شده و خروجی SQL تولید شده آن به صورت زیر میباشد:
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO "Product" (Name, UnitPrice, Discontinued, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 'Apples', @p1 = 4.5, @p2 = True, @p3 = 1001
NHibernate: INSERT INTO "OrderItem" (Quantity, Product_id, Id) VALUES (@p0, @p1, @p2);@p0 = 5, @p1 = 1001, @p2 = 1
NHibernate: SELECT orderitem0_.Id as Id0_1_, orderitem0_.Quantity as Quantity0_1_, orderitem0_.Product_id as Product3_0_1_, product1_.Id as Id3_0_, product1_.Name as Name3_0_, product1_.UnitPrice as UnitPrice3_0_, product1_.Discontinued as Disconti4_3_0_ FROM "OrderItem" orderitem0_ inner join "Product" product1_ on orderitem0_.Product_id=product1_.Id WHERE orderitem0_.Id=@p0;@p0 = 1
قسمت پایانی کار تعاریف کلاسهای نگاشت، مربوط به کلاس Order است که در ادامه بررسی خواهد شد.
using System;
using System.Collections.Generic;
namespace NHSample1.Domain
{
public class Order
{
public int Id { set; get; }
public DateTime OrderDate { get; set; }
public Employee Employee { get; set; }
public Customer Customer { get; set; }
public IList<OrderItem> OrderItems { get; set; }
}
}
using NHSample1.Domain;
using FluentNHibernate.Mapping;
namespace NHSample1.Mappings
{
public class OrderMapping : ClassMap<Order>
{
public OrderMapping()
{
Not.LazyLoad();
Id(o => o.Id).GeneratedBy.GuidComb();
Map(o => o.OrderDate).Not.Nullable();
References(o => o.Employee).Not.Nullable();
References(o => o.Customer).Not.Nullable();
HasMany(o => o.OrderItems)
.AsList(index => index.Column("ListIndex").Type<int>());
}
}
}
قسمت جدید آن HasMany است که جهت تعریف رابطه one-to-many بکار گرفته شده است. یک سفارش رابطه many-to-one با یک مشتری و همچنین کارمندی که این رکورد را ثبت میکند، دارد. در اینجا مجموعه آیتمهای یک سفارش به صورت یک لیست بازگشت داده میشود و ایندکس آن به ستونی به نام ListIndex در یک جدول دیتابیس نگاشت خواهد شد. نوع این ستون، int میباشد.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using NHSample1.Domain;
using FluentNHibernate.Testing;
namespace UnitTests
{
[TestFixture]
public class OrderMapping_Fixture : FixtureBase
{
[Test]
public void can_correctly_map_an_order()
{
{
var product1 =
new Product
{
Name = "Apples",
UnitPrice = 4.5m,
Discontinued = true
};
var product2 =
new Product
{
Name = "Pears",
UnitPrice = 3.5m,
Discontinued = false
};
Session.Save(product1);
Session.Save(product2);
var items = new List<OrderItem>
{
new OrderItem
{
Id = 1,
Quantity = 100,
Product = product1
},
new OrderItem
{
Id = 2,
Quantity = 200,
Product = product2
}
};
var customer = new Customer
{
FirstName = "Vahid",
LastName = "Nasiri",
AddressLine1 = "Addr1",
AddressLine2 = "Addr2",
PostalCode = "1234",
City = "Tehran",
CountryCode = "IR"
};
var employee =
new Employee
{
FirstName = "name1",
LastName = "lname1"
};
var order = new Order
{
Customer = customer,
Employee = employee,
OrderDate = DateTime.Today,
OrderItems = items
};
new PersistenceSpecification<Order>(Session, new CustomEqualityComparer())
.CheckProperty(o => o.OrderDate, order.OrderDate)
.CheckReference(o => o.Customer, order.Customer)
.CheckReference(o => o.Employee, order.Employee)
.CheckList(o => o.OrderItems, order.OrderItems)
.VerifyTheMappings();
}
}
}
}
متد آزمون واحد فوق کمی طولانی است؛ زیرا در آن باید تعاریف انواع و اقسام اشیاء مورد استفاده را مشخص نمود (و ارزش کار نیز دقیقا در همینجا مشخص میشود که بجای SQL نوشتن، با اشیایی که توسط کامپایلر تحت نظر هستند سر و کار داریم).
تنها نکته جدید آن استفاده از CheckList برای بررسی IList تعریف شده در قسمت قبل است.
خروجی SQL این آزمون واحد پس از اجرا و موفقیت آن به صورت زیر است:
OrderMapping_Fixture.can_correctly_map_an_order : Passed
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 3, @p1 = 2
NHibernate: INSERT INTO "Product" (Name, UnitPrice, Discontinued, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 'Apples', @p1 = 4.5, @p2 = True, @p3 = 1001
NHibernate: INSERT INTO "Product" (Name, UnitPrice, Discontinued, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 'Pears', @p1 = 3.5, @p2 = False, @p3 = 1002
NHibernate: INSERT INTO "Customer" (FirstName, LastName, AddressLine1, AddressLine2, PostalCode, City, CountryCode, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7);@p0 = 'Vahid', @p1 = 'Nasiri', @p2 = 'Addr1', @p3 = 'Addr2', @p4 = '1234', @p5 = 'Tehran', @p6 = 'IR', @p7 = 2002
NHibernate: select next_hi from hibernate_unique_key
NHibernate: update hibernate_unique_key set next_hi = @p0 where next_hi = @p1;@p0 = 4, @p1 = 3
NHibernate: INSERT INTO "Employee" (LastName, FirstName, Id) VALUES (@p0, @p1, @p2);@p0 = 'lname1', @p1 = 'name1', @p2 = 3003
NHibernate: INSERT INTO "OrderItem" (Quantity, Product_id, Id) VALUES (@p0, @p1, @p2);@p0 = 100, @p1 = 1001, @p2 = 1
NHibernate: INSERT INTO "OrderItem" (Quantity, Product_id, Id) VALUES (@p0, @p1, @p2);@p0 = 200, @p1 = 1002, @p2 = 2
NHibernate: INSERT INTO "Order" (OrderDate, Employee_id, Customer_id, Id) VALUES (@p0, @p1, @p2, @p3);@p0 = 2009/10/10 12:00:00 ق.ظ, @p1 = 3003, @p2 = 2002, @p3 = 0
NHibernate: UPDATE "OrderItem" SET Order_id = @p0, ListIndex = @p1 WHERE Id = @p2;@p0 = 0, @p1 = 0, @p2 = 1
NHibernate: UPDATE "OrderItem" SET Order_id = @p0, ListIndex = @p1 WHERE Id = @p2;@p0 = 0, @p1 = 1, @p2 = 2
NHibernate: SELECT order0_.Id as Id1_2_, order0_.OrderDate as OrderDate1_2_, order0_.Employee_id as Employee3_1_2_, order0_.Customer_id as Customer4_1_2_, employee1_.Id as Id4_0_, employee1_.LastName as LastName4_0_, employee1_.FirstName as FirstName4_0_, customer2_.Id as Id2_1_, customer2_.FirstName as FirstName2_1_, customer2_.LastName as LastName2_1_, customer2_.AddressLine1 as AddressL4_2_1_, customer2_.AddressLine2 as AddressL5_2_1_, customer2_.PostalCode as PostalCode2_1_, customer2_.City as City2_1_, customer2_.CountryCode as CountryC8_2_1_ FROM "Order" order0_ inner join "Employee" employee1_ on order0_.Employee_id=employee1_.Id inner join "Customer" customer2_ on order0_.Customer_id=customer2_.Id WHERE order0_.Id=@p0;@p0 = 0
NHibernate: SELECT orderitems0_.Order_id as Order4_2_, orderitems0_.Id as Id2_, orderitems0_.ListIndex as ListIndex2_, orderitems0_.Id as Id0_1_, orderitems0_.Quantity as Quantity0_1_, orderitems0_.Product_id as Product3_0_1_, product1_.Id as Id3_0_, product1_.Name as Name3_0_, product1_.UnitPrice as UnitPrice3_0_, product1_.Discontinued as Disconti4_3_0_ FROM "OrderItem" orderitems0_ inner join "Product" product1_ on orderitems0_.Product_id=product1_.Id WHERE orderitems0_.Order_id=@p0;@p0 = 0
تا اینجای کار تعاریف اشیاء ، نگاشت آنها و همچنین بررسی صحت این نگاشتها به پایان میرسد.
نکته:
دیتابیس برنامه را جهت آزمونهای واحد برنامه، از نوع SQLite ساخته شده در حافظه مشخص کردیم. اگر علاقمند باشید که database schema تولید شده توسط NHibernate را مشاهده نمائید، در متد SetupContext کلاس FixtureBase که در قسمت قبل معرفی شد، سطر آخر را به صورت زیر تغییر دهید، تا اسکریپت دیتابیس نیز به صورت خودکار در خروجی اس کیوال آزمون واحد لحاظ شود (پارامتر دوم آن مشخص میکند که schema ساخته شده، نمایش داده شود یا خیر):
SessionSource.BuildSchema(Session, true);
drop table if exists "OrderItem"
drop table if exists "Order"
drop table if exists "Customer"
drop table if exists "Product"
drop table if exists "Employee"
drop table if exists hibernate_unique_key
create table "OrderItem" (
Id INTEGER not null,
Quantity INTEGER not null,
Product_id INTEGER not null,
Order_id INTEGER,
ListIndex INTEGER,
primary key (Id)
)
create table "Order" (
Id INTEGER not null,
OrderDate DATETIME not null,
Employee_id INTEGER not null,
Customer_id INTEGER not null,
primary key (Id)
)
create table "Customer" (
Id INTEGER not null,
FirstName TEXT not null,
LastName TEXT not null,
AddressLine1 TEXT not null,
AddressLine2 TEXT,
PostalCode TEXT not null,
City TEXT not null,
CountryCode TEXT not null,
primary key (Id)
)
create table "Product" (
Id INTEGER not null,
Name TEXT not null,
UnitPrice NUMERIC not null,
Discontinued INTEGER not null,
primary key (Id)
)
create table "Employee" (
Id INTEGER not null,
LastName TEXT,
FirstName TEXT,
primary key (Id)
)
create table hibernate_unique_key (
next_hi INTEGER
)
برای اینکه از دیتابیس اس کیوال سرور استفاده کنیم، در همان متد SetupContext کلاس مذکور، سطر اول را به صورت زیر تغییر دهید (نوع دیتابیس اس کیوال سرور 2008 مشخص شده و سپس رشته اتصالی به دیتابیس ذکر گردیده است):
var cfg = Fluently.Configure().Database(
// SQLiteConfiguration.Standard.ShowSql().InMemory
MsSqlConfiguration
.MsSql2008
.ShowSql()
.ConnectionString("Data Source=(local);Initial Catalog=testdb2009;Integrated Security = true")
);
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3EF88858466CFBF7]') AND parent_object_id = OBJECT_ID('[OrderItem]'))
alter table [OrderItem] drop constraint FK3EF88858466CFBF7
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3EF888589F32DE52]') AND parent_object_id = OBJECT_ID('[OrderItem]'))
alter table [OrderItem] drop constraint FK3EF888589F32DE52
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3117099B1EBA72BC]') AND parent_object_id = OBJECT_ID('[Order]'))
alter table [Order] drop constraint FK3117099B1EBA72BC
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK3117099BB2F9593A]') AND parent_object_id = OBJECT_ID('[Order]'))
alter table [Order] drop constraint FK3117099BB2F9593A
if exists (select * from dbo.sysobjects where id = object_id(N'[OrderItem]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [OrderItem]
if exists (select * from dbo.sysobjects where id = object_id(N'[Order]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Order]
if exists (select * from dbo.sysobjects where id = object_id(N'[Customer]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Customer]
if exists (select * from dbo.sysobjects where id = object_id(N'[Product]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Product]
if exists (select * from dbo.sysobjects where id = object_id(N'[Employee]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Employee]
if exists (select * from dbo.sysobjects where id = object_id(N'hibernate_unique_key') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table hibernate_unique_key
create table [OrderItem] (
Id INT not null,
Quantity INT not null,
Product_id INT not null,
Order_id INT null,
ListIndex INT null,
primary key (Id)
)
create table [Order] (
Id INT not null,
OrderDate DATETIME not null,
Employee_id INT not null,
Customer_id INT not null,
primary key (Id)
)
create table [Customer] (
Id INT not null,
FirstName NVARCHAR(50) not null,
LastName NVARCHAR(50) not null,
AddressLine1 NVARCHAR(50) not null,
AddressLine2 NVARCHAR(50) null,
PostalCode NVARCHAR(10) not null,
City NVARCHAR(50) not null,
CountryCode NVARCHAR(2) not null,
primary key (Id)
)
create table [Product] (
Id INT not null,
Name NVARCHAR(50) not null,
UnitPrice DECIMAL(19,5) not null,
Discontinued BIT not null,
primary key (Id)
)
create table [Employee] (
Id INT not null,
LastName NVARCHAR(50) null,
FirstName NVARCHAR(50) null,
primary key (Id)
)
alter table [OrderItem]
add constraint FK3EF88858466CFBF7
foreign key (Product_id)
references [Product]
alter table [OrderItem]
add constraint FK3EF888589F32DE52
foreign key (Order_id)
references [Order]
alter table [Order]
add constraint FK3117099B1EBA72BC
foreign key (Employee_id)
references [Employee]
alter table [Order]
add constraint FK3117099BB2F9593A
foreign key (Customer_id)
references [Customer]
create table hibernate_unique_key (
next_hi INT
)
الف) جداول مطابق نام کلاسهای ما تولید شدهاند.
ب) نام فیلدها دقیقا مطابق نام خواص کلاسهای ما تشکیل شدهاند.
ج) Id ها به صورت primary key تعریف شدهاند (از آنجائیکه ما در هنگام تعریف نگاشتها، آنها را از نوع identity مشخص کرده بودیم).
د) رشتهها به نوع nvarchar با اندازه 50 نگاشت شدهاند.
ه) کلیدهای خارجی بر اساس نام جدول با پسوند _id تشکیل شدهاند.
ادامه دارد ...
ASP.NET MVC
- ASP.NET MVC #1
- ASP.NET MVC #2
- ASP.NET MVC #3
- ASP.NET MVC #4
- ASP.NET MVC #5
- ASP.NET MVC #6
- ASP.NET MVC #7
- ASP.NET MVC #8
- ASP.NET MVC #9
- ASP.NET MVC #10
- ASP.NET MVC #11
- ASP.NET MVC #12
- ASP.NET MVC #13
- ASP.NET MVC #14
- ASP.NET MVC #15
- ASP.NET MVC #16
- ASP.NET MVC #17
- ASP.NET MVC #18
- ASP.NET MVC #19
- ASP.NET MVC #20
- ASP.NET MVC #21
- ASP.NET MVC #22
- ASP.NET MVC #23
- ASP.NET MVC #24
- نحوه ارتقاء برنامههای موجود MVC3 به MVC4
- تغییرات بوجود آمده در Bundling and Minification -MVC4
- تغییرات بوجود آمده در Mobile Features-MVC4
- تغییرات بوجود آمده در Single Page Application (SPA)-MVC4
- تغییرات بوجود آمده در Razor -MVC4
- Globalization در ASP.NET MVC
- Globalization در ASP.NET MVC - قسمت دوم
- Globalization در ASP.NET MVC - قسمت سوم
- Globalization در ASP.NET MVC - قسمت چهارم
- Globalization در ASP.NET MVC - قسمت پنجم
- Globalization در ASP.NET MVC - قسمت ششم
- Globalization در ASP.NET MVC - قسمت هفتم
- بررسی تغییرات ASP.NET MVC 5 beta1
- مسیریابی (Routing) در ASP.NET MVC 5.x
- Attribute Routing در ASP.NET MVC 5
- قابلیت Attribute Routing در ASP.NET MVC 5
- یک تکنیک جالب در نحوه نام گذاری فیلدهای دیتابیس به منظور استفاده بهینه از فایلهای T4 در MVC 5
- نگاهی به هویت سنجی کاربران در ASP.NET MVC 5
- سفارشی کردن ASP.NET Identity در MVC 5
- افزودن تصدیق ایمیل به ASP.NET Identity در MVC 5
- ایجاد کپچایی (captcha) سریع و ساده در ASP.NET MVC 5
- توزیع یک اپلیکیشن ASP.NET MVC 5 روی Windows Azure
- معماری لایه بندی نرم افزار #1
- معماری لایه بندی نرم افزار #2
- معماری لایه بندی نرم افزار #3
- معماری لایه بندی نرم افزار #4
- CheckBoxList در ASP.NET MVC
- RadioButtonList در ASP.NET MVC
- مدیریت محل اعمال Google analytics در ASP.NET MVC
- استفاده از HttpGet در ASP.NET MVC، آری یا خیر؟!
- اثر وجود سشن بر پردازش موازی در ASP.NET
- استفاده از دکمههای CSS توئیتر در ASP.NET MVC
- نحوه صحیح تولید Url در ASP.NET MVC
- استفاده از OpenID در وب سایت جهت احراز هویت کاربران
- متدهای کمکی مفید در پروژههای asp.net mvc
- T4MVC : یکی از الزامات مدیریت پروژههای ASP.NET MVC
- مقدمه ای بر AutoMapper
- بهبود سرعت نمایش صفحات در ASP.NET MVC با حذف View Engines اضافی
- محدود کردن کاربرها به آپلود فایلهایی خاص در ASP.NET MVC
- ارسال فایل در ASP.NET MVC و اعتبار سنجی سمت کاربر
- فعال سازی قسمت ارسال فایل و تصویر ویرایشگر آنلاین RedActor در ASP.NET MVC
- معرفی پروژه Orchard
- مباحث تکمیلی مدلهای خود ارجاع دهنده در EF Code first
- سازگار کردن لینکهای قدیمی یک سایت با ساختار جدید آن در ASP.NET MVC
- CAPTCHAfa
- نکتهای در استفاده از AutoMapper
- یکپارچه سازی CKEditor با Lightbox
- تهیه خروجی RSS در برنامههای ASP.NET MVC
- ایجاد Helper سفارشی جهت نمایش ویدئو در ASP.NET MVC
- ساخت DropDownListهای مرتبط به کمک jQuery Ajax در MVC
- استفاده از دکمههای CSS توئیتر در ASP.NET MVC - قسمت دوم
- با ASP.MVC چه مزایایی را به دست خواهیم آورد
- نحوه اضافه کردن Auto-Complete به جستجوی لوسین در ASP.NET MVC و Web forms
- مقابله با پسوردهایی که ساده حدس زده میشوند
- غیرفعال کردن کش مرورگر در MVC
- Best Practice هایی برای ASP.NET MVC - قسمت اول
- چک لیست تهیه یک برنامه ASP.NET MVC
- استفاده از FluentValidation در ASP.NET MVC
- نحوه اجباری کردن استفاده از WWW در ASP.NET MVC
- نمایش رکوردها به ترتیب اولویت به کمک jQuery UI sortable در ASP.NET MVC
- کنترل عمومی فایلهای آپلودی در ASP.NET MVC
- هدایت خودکار کاربر به صفحه لاگین در حین اعمال Ajax ایی
- مدیریت سفارشی سطوح دسترسی کاربران در MVC
- بهبود SEO در ASP.NET MVC
- ایجاد قسمتهای Toggle در سایت با jQuery
- آشنایی و بررسی ابزار MiniProfiler
- استفاده از Flash Uploader در ASP.NET MVC
- استایل دهی به ستونهای header در WebGrid
- ELMAH و حملات XSS
- آموزش MEF#2(استفاده از MEF در Asp.Net MVC)
- نحوه استفاده از ViewModel در ASP.NET MVC
- مخفی کردن کوئری استرینگها در ASP.NET MVC توسط امکانات Routing
- توزیع پروژههای ASP.NET MVC بدون ارائه فایلهای View آن
- حذف هدرهای مربوط به وب سرور از طریق برنامه نویسی
- ایجاد helper برای Nivo Slider در Asp.net Mvc
- آشنایی با Fluent Html Helpers در MVC
- نحوه استفاده از افزونه Firebug برای دیباگ برنامههای ASP.NET مبتنی بر jQuery
- عدم امکان تغییر اطلاعات مدل در HTML Helpers پس از Postback در ASP.NET MVC
- اضافه کردن Watermark به تصاویر یک برنامه ASP.NET MVC در صورت لینک شدن در سایتی دیگر
- چگونگی رسیدگی به Null property در AutoMapper
- return File در ASP.NET MVC و نامهای یونیکد
- تولید SiteMap استاندارد و ایجاد یک ActionResult اختصاصی برای Return کردن SiteMap تولید شده
- نحوه ایجاد یک تصویر امنیتی (Captcha) با حروف فارسی در ASP.Net MVC
- الگوی PRG در ASP.NET MVC
- اعتبارسنجی سایتهای چند زبانه در ASP.NET MVC - قسمت اول
- آغاز به کار با Twitter Bootstrap در ASP.NET MVC
- استفاده از Twitter Bootstrap در کارهای روزمره طراحی وب
- نگاهی به اجزای تعاملی Twitter Bootstrap
- اعمال کلاسهای ویژه اعتبارسنجی Twitter bootstrap به فرمهای ASP.NET MVC
- ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap
- استفاده از افزونه Typeahead مجموعه Twitter Bootstrap در ASP.NET MVC
- استفاده از modal dialogs مجموعه Twitter Bootstrap برای گرفتن تائید از کاربر
- Bundling and Minifying Inline Css and Js
- نمایش فرمهای مودال Ajax ایی در ASP.NET MVC به کمک Twitter Bootstrap
- MVC vs 3-Tier Pattern
- پلاگین جستجو با jquery و twitter bootstrap
- نمایش خطاهای اعتبارسنجی سمت کاربر ASP.NET MVC به شکل Tooltip به کمک Twitter bootstrap
- نمایش خطاهای اعتبارسنجی سمت کاربر ASP.NET MVC به شکل Popover به کمک Twitter bootstrap
- ساخت قالبهای نمایشی و ادیتور دکمه سه وضعیتی سازگار با Twitter bootstrap در ASP.NET MVC
- پیاده سازی Open Search در ASP.NET MVC
- Best Practice ی برای تأیید اعتبار کردن کاربران در ASP.NET MVC 4
- هدایت درخواست فایلهای استاتیک در ASP.NET MVC به یک کنترلر
- ModelBinder سفارشی در ASP.NET MVC
- ایجاد لینک با یک تصویر بوسیله Html Helper
- بارگزاری PartialView با استفاده از jQuery در زمان اجرا
- یافتن اکشن متدهای به اشتباه کش شده در ASP.NET MVC
- مروری مقدماتی بر ساخت برنامههای موبایل در MVC4
- اجرای برنامههای ASP.NET توسط Mono در Ubuntu
- اجرای برنامههای ASP.NET به کمک وب سرور Apache توسط Mono در Ubuntu
- فعالسازی استفاده از Session در ASP.NET MVC 4 API Controller ها
- ساخت منوهای چند سطحی در ASP.NET MVC
- طراحی ValidationAttribute دلخواه و هماهنگ سازی آن با ASP.NET MVC
- بهینه سازی برنامههای وب ASP.NET برای موتورهای جستجو (SEO)
- CheckBoxList برای فیلد Enum Flags مدل در ASP.Net MVC
- چطور مسیریابیهای ASP.NET MVC را دیباگ کنیم؟
- ذخیره TreeView ساخته شده توسط KendoUI در Asp.net MVC
- تنظیمات امنیتی Glimpse
- سفارشی سازی Binding یک خصوصیت از طریق Attributes
- Bundle کردن فایلهای LESS در MVC
- TwitterBootstrapMVC
- بررسی خطای Circular References در ASP.NET MVC Json Serialization
- ایجاد یک فیلتر سفارشی جهت تعیین Layout برای کنترلر و یا اکشن متد
- حذف فضاهای خالی در خروجی صفحات ASP.NET MVC
- جلوگیری از درج صفحات سایت در سایتی دیگر از طریق iframeها
- مشکل اعتبار سنجی jQuery validator در Bootstrap tabs
- افزودن هدرهای Content Security Policy به برنامههای ASP.NET
- امکان اعتبارسنجی با تاخیر در ASP.NET 4.5
- معرفی ASP.NET Identity
- متدهای احراز هویت در VS 2013
- توسعه اپلیکیشنهای ASP.NET با Windows Azure Active Directory
- ساخت یک اپلیکیشن ساده ToDo با ASP.NET Identity
- دریافت اطلاعات بیشتر از Social Providerها در VS 2013
- معرفی کتابخانه Postal برای ASP.NET MVC
- استفاده از Web Fonts در اپلیکیشنهای ASP.NET MVC
- استفاده از Awesomium.NET در برنامههای وب
- خواندن اطلاعات از سرور و نمایش آن توسط Angular در ASP.NET MVC
- آموزش Backload (آپلود چندین فایل به طور همزمان با آجاکس )
- یافتن اکشن متدهای Post ایی در ASP.NET MVC که فیلتر CSRF ندارند
- ایجاد سیستم وضعیت آب و هوا مانند گوگل (بخش اول)
- استفاده از #F در پروژههای MVC4
- توسعه کنترلر و مدل در F# MVC4
- تعامل با پایگاه داده با استفاده از EntityFramework در پروژههای F# MVC 4
- انجام کارهای زمانبندی شده در برنامههای ASP.NET توسط DNT Scheduler
- بررسی خروجی IsAjaxRequest در درخواستهای http$ توسط AngularJS
- بارگذاری فایلهای ایستا از پوشهی Views در ASP.NET MVC
- هدایت خودکار آدرسهای یافت نشد در یک سایت ASP.NET MVC به جستجوی سایت
- راههای متفاوت رندر لایهها در ASP.NET MVC
- پروژه Microsoft.AspNet.Mvc.Futures و تولید مسیرهای Strongly typed
- ASP.NET MVC و Identity 2.0 : مفاهیم پایه
- ارسال PingBack در ASP.NET
- Identity 2.0 : تایید حسابهای کاربری و احراز هویت دو مرحله ای
- مدیریت درخواستهای شرطی در ASP.NET MVC
- استفاده از Froala WYSIWYG Editor در ASP.NET
- استفاده از نگارش سوم Google Analytics API در سرویسهای ویندوز یا برنامههای وب
- بهینه سازی فایلهای js و css در برنامههای ASP.NET با استفاده از Combres - قسمت اول
- استفاده از pjax بجای ajax در ASP.NET MVC
- استفاده از افزونهی jsTree در ASP.NET MVC
- تفاوت ViewData و ViewBag و TempData و Session در MVC
- استفاده از چند فرم در کنار هم در ASP.NET MVC
- انجام کارهای پس زمینه در ASP.NET 4.5.2
- نمایش اخطارها و پیامهای بوت استرپ به کمک TempData در ASP.NET MVC
- صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
- فرمت کردن اطلاعات نمایش داده شده به کمک jqGrid در ASP.NET MVC
- فعال سازی و پردازش جستجوی پویای jqGrid در ASP.NET MVC
- استفاده از چند Routing در یک پروژه ASP.NET MVC بدون درد و خونریزی
- فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC
- رمزنگاری خودکار فیلدهای مخفی در ASP.NET MVC
- استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC
- سفارشی سازی عناصر صفحات پویای افزودن و ویرایش رکوردهای jqGrid در ASP.NET MVC
- تهیه خروجی PDF و اکسل از حاصل جستجوی پویای jqGrid به کمک PDF Report
- Ajax.BeginForm و ارسال فایل به سرور در ASP.NET MVC
- آپلود فایل توسط فرمهای پویای jqGrid
- اختصاصی کردن Razor برای #C در MVC با استفاده از Extension Method
- روشی سریع برای ایجاد RSS و Sitemap در ASP.NET MVC
- اعتبارسنجی سفارشی سمت کاربر و سمت سرور در jqGrid
- OutputCache در ASP.NET MVC
- فعال سازی و پردازش Inline Add در jqGrid
- بهینه سازی سرعت یافت ویوها با سفارشی سازی Lookup Caching در Razor View Engine
- گروه بندی اطلاعات در jqGrid
- نمایش Subgrid در jqGrid
- ایجاد زیر گریدهای چند سطحی در jqGrid
- نمایش ساختارهای درختی توسط jqGrid
- سازگارسازی کلاسهای اعتبارسنجی Twitter Bootstrap 3 با فرمهای ASP.NET MVC
- اعتبارسنجی در فرمهای ASP.NET MVC با Remote Validation
- قالبهای سفارشی برای HtmlHelperها
- ارسال ویدیو بصورت Async توسط Web Api
- اعتبار سنجی سمت کاربر wysiwyg-editorها در ASP.NET MVC
- بررسی مقدمات کتابخانهی JSON.NET
- تنظیمات و نکات کاربردی کتابخانهی JSON.NET
- استفاده از JSON.NET در ASP.NET MVC
- LINQ to JSON به کمک JSON.NET
- آشنایی با چالشهای امنیتی در توسعه برنامههای تحت وب، بخش اول
- نمایش بلادرنگ اعلامی به تمام کاربران در هنگام درج یک رکورد جدید
- پیاده سازی Template تو در تو در AngularJS و ASP.NET MVC
- یکپارچه سازی سیستم اعتبارسنجی ASP.NET MVC با Kendo UI validator
- ساخت یک Form Generator ساده در MVC
- آشنایی با WebDav و نحوه استفاده از آن
- قابلیت Templated Razor Delegate
- اعمال تزریق وابستگیها به مثال رسمی ASP.NET Identity
- ثبت جزئیات استثناهای Entity framework توسط ELMAH
- نمایش بلادرنگ اعلامی به تمام کاربران در هنگام درج یک رکورد جدید به صورت notification
- کار با وب سرویس جاوایی تشخیص ایمیلهای موقتی در دات نت
- فراخوانی متدهای Controllerها در Viewهای ASP.NET MVC
- لغو اجرای یک اکشن فیلتر برای یک اکشن خاص در MVC
- کار با اسکنر در برنامههای تحت وب (قسمت اول)
- کار با اسکنر در برنامههای تحت وب (قسمت دوم و آخر)
- تبدیل یک View به رشته و بازگشت آن به همراه نتایج JSON حاصل از یک عملیات Ajax ایی در ASP.NET MVC
- آشنایی با ساختار ViewBag
- مدیریت سشنها در برنامههای وب به کمک تزریق وابستگیها
- خلاصهای از روشهای ارسال دادههای سمت سرور به کدهای جاوا اسکریپتی در ASP.NET MVC
- یکدست کردن "ی" و "ک" در ASP.NET MVC با پیادهسازی یک Model Binder
- حذف پردازش درخواستهای فایلهای استاتیک در متد Application_AuthenticateRequest
- دریافت خطاهای موجود در Viewهای ASP.NET MVC در زمان کامپایل
- پیاده سازی یک متد الحاقی برای تبدیل آدرس فیزیکی به آدرس مجازی (آدرس سرور)
- استفاده از Razor در فایلهای JavaScript و CSS
- عمومی سازی الگوریتمها با استفاده از Reflection
Lazy loading در تزریق وابستگیها به کمک StructureMap
public interface IUnitOfWork { Lazy<IDbSet<TEntity>> LazySet<TEntity>() where TEntity : class; int SaveChanges(); }
Lazy <IDbSet<TEntity>> LazySet<TEntity>() where TEntity : class