مقدمه ای کاربردی بر Machine Learning
مقاله درباره ViewModel
Use ViewModels to manage data & organize code in ASP.NET MVC applications
The concept of the ViewModel isn't just for ASP.NET MVC, as you'll see references to ViewModels throughout the web in articles and blog posts about the MVC, MVP, and MVVM patterns. Those posts and articles can center around any number of technologies such as ASP.NET, Silverlight, WPF, or MVC... This post will investigate ViewModels as they apply to the world of ASP.NET MVC.
Globalization در ASP.NET MVC
به نظر میرسه که طریقه استفاده از ExceuteCore تغییر کرده ولی من هر چقدر که سرچ کردم در این رابطه چیزی ندیدم و احتمال میدم که کنترلری که از اینترفیس IAsyncController ارث بری کرده کنترولر AsyncController نیستش .
به هرحال من واسه این مشکل یه راه حلی پیدا کردم که میتونه کاری کنه که MVC4 رفتارهای وزژنهای قبلی رو انجام بده
کد زیر رو به کلاس BaseController اضافه کنید.
protected override bool DisableAsyncSupport { get { return true; } }
MVC4 به کنترلر اجازه میده الگوهای غیر همزمان (asynchronous patterns ) رو پشتیبانی کنه
واین یعنی اینکه ExecuteCore کلاسهای مشتق شده صدا زده نمیشه
در عوض کلاسهای مشتق شده نیاز داشته باشند که ExecuteCore رو صدا یزنند میتونند این فلگ رو بازنویسی(Override) کنند و مقدار اون رو به True تغییر بدهند.
1- من دقیقا متوجه نشدم منظور شما از decoupling اول مقاله چیه؟ منظورتون تفکیک Domain از DAL هست؟ اگر اینطوره چه ربطی به UoW و انواع پیاده سازی اون داره؟
اگر منظورشما انفکاک بین EFContext و Repository هست، توجه شما رو به این نکته جلب میکنم که StudentRepository که در اول مقاله آورده شده در حقیقت یک پیاده سازی برپایه EF هست به عبارتی EFStudentRepository اسم مناسبتری میتونه باشه. بنابراین تزریق Context با هیچ اصلی مغایر نیست. چرا که این Repository یک پیاده سازی خاص از IStudentRepository است.
2- وجود متد Save در Repository؟ نه تنها قابل قبول نیست که اصلا اگر قرار باشه هر Repository مستقلا Save رو صدا بزنه که مفهوم Transaction از بین میره یا حداقل سخت میشه بهش رسید.
3- با شما موافقم که Generic Repository ایده خوبی نیست. البته فقط تا اینجا موافقم که این الگو برای Expose کردن Interface یک Repository مناسب نیست. چه بسا Repository هایی که فقط SELECT میکنند. ولی اگر پیاده سازی خاصی از یک Repository مد نظر دارید (مثلا پیاده سازی برپایه EF یا NHibernate) اونوقت دقیقا چیزی که به کمک شما میاد همین Generic Repository برای جلوگیری از کدهای تکراریه.
4- اصولا Repository برای اینکه منطق برنامه (یا به قول شما منطق تجاری) رو پیاده سازی کنه نیست. در حقیقت لایه ای که استفاده کننده مستقیم از Repository است میداند که چه موقع به چه Repository فرمانهای CRUD بده تا منطق برنامه پیاده سازی بشه.
5- در واقع استفاده از امکانات هر ORM تا حد بینهایتی امکان پذیره به شرطی که ORM و توانمندیهاشو در همون لایه DAL محصور کنید مثلا IQueriable و Cachable و گرنه Leaky Abstraction به طور خزنده و ساکتی کل برنامه رو مثل سرطان در خودش میکشه.
نهایتا اینکه نمیشه یک پیاده سازی مشکل دار از مفهوم Repository + UoW رو بدون درنظر گرفتن مفاهیم مهمی مثل Service Layer و Domain Model نقد کرد و بعدا نتیجه گرفت که این الگوها صحیح نیستند. ضمن اینکه این موضوع بسته به تجربه و نظر هر برنامه نویس و معماری میتونه پیاده سازی خاص خودشو داشته باشه که من شخصا هنوز موارد جالب و جدیدی که یک برنامه نویس باهوش برداشت کرده رو میبینم و نتیجه میگیرم که مفهوم Repository + UoW در بین ماها هنوز به یک تعریف جهانشمول نرسیده.
با توجه به اینکه الگوهای طراحی زیادی وجود دارند، چگونه میتوانید مناسبترین الگوی طراحی را برای حل مسئله خود انتخاب کنید و مهمتر اینکه چگونه آن را اعمال نمایید؟ برای پاسخ به این سوال، رهنمودهای زیر را همیشه در نظر داشته باشید:
- شما نمیتوانید یک الگو را به کار بگیرید مگر آنکه آن را به خوبی فهمیده باشید. بنابراین در اولین گام باید اصول و الگوهای طراحی را هم به شکل انتزاعی و هم به شکل واقعی خوانده و تمرین کنید. دقت کنید که یک الگو را به شکلهای مختلفی میتوان پیاده سازی کرد. هر چه پیاده سازیهای بیشتری ببینید، به هدف و چگونگی استفاده از آن بهتر مسلط میشوید.
- آیا میخواهید با استفاده از یک الگوی طراحی، برنامه خود را پیچیدهتر کنید؟ این معمول است که توسعه دهندگان برای حل هر مسئله از الگوهای طراحی استفاده میکنند. ابتدا هزینه و فایده پیاده سازی یک الگو را ارزیابی کرده، سپس اقدام به استفاده کنید. همیشه قاعده KISS را به خاطر داشته باشید.
- مسئله خود را تعمیم دهید. پیامدهای مسئله خود را با دید انتزاعی و سطح بالا بررسی کنید. به یاد داشته باشید که الگوهای طراحی، راه حلهای سطح بالا برای مسائل سطح بالا هستند. بنابراین روی پیامدهای جزئی یا وابسته به دامین مسئله خود تمرکز نکنید.
- به الگوهای مشابه و هم گروه نگاه کنید. اگر قبلا از یک الگو استفاده کردهاید بدین معنی نیست برای هر مسئلهای آن الگو درست است.
- هر چیزی که تغییر میکند را بسته بندی کنید. ببینید که چه چیزی در برنامه کاربردی شما ممکن است تغییر کند. اگر شما میدانید که یک الگوریتم اعمال تخفیف ممکن است به مرور زمان تغییر کند، به دنبال الگویی باشید که تغییرات در آن الگوریتم را بدون تاثیر بر سایر قسمتهای برنامه کاربردی انجام دهید.
- وقتی که یک الگو را انتخاب کردید، از زبان الگو در کنار زبان دامین برای نام گذاری کلاسها استفاده کنید. برای مثال اگر ازالگوی Strategy استفاده میکنید تا هزینه حمل و نقل کالا توسط شرکت FedEx را محاسبه کند، از نام FedExShippingCostStrategy استفاده کنید. با استفاده از زبان مشترک بین الگو طراحی و مدل دامین، کد برنامه برای شما و دیگران خواناتر و قابل فهمتر میگردد.
- همیشه منظور هر الگو را در ذهن خود مرور کنید و هنگام برخورد با یک مسئله به دنبال مناسبترین الگو بگردید. یک تمرین یادگیری عالی شناسایی الگوهای طراحی در فریم ورک .Net است. برای مثال، ASP.Net Cache از الگوی Singleton استفاده میکند و کلاس Guid از الگوی Factory بهره میبرد.
تا به حال شما باید هدف و الگوریتم استفاده از الگوهای طراحی را درک کرده باشید. در ادامه با لایه بندی برنامه کاربردی آشنا میشوید و سپس نحوه استفاده از این الگوها در لایههای مختلف را فرا خواهید گرفت.
بررسی کارآیی کوئریها در SQL Server - قسمت هشتم - بررسی عملگرهای Merge Join و Sort در یک Query Plan
بررسی عملگر merge join
ابتدا در management studio از منوی Query، گزینهی Include actual execution plan را انتخاب میکنیم. سپس کوئریهای زیر را اجرا میکنیم:
USE [WideWorldImporters]; GO SET STATISTICS IO ON; GO SELECT [p].[PurchaseOrderID], [pl].[PurchaseOrderLineID] FROM [Purchasing].[PurchaseOrders] [p] JOIN [Purchasing].[PurchaseOrderLines] [pl] ON [p].[PurchaseOrderID] = [pl].[PurchaseOrderID]; GO
در اینجا یک merge join انجام شده، چون اطلاعات رسیدهی به آن، از پیش مرتب شدهاست. از این جهت که جدول PurchaseOrders دارای یک clustered index تعریف شدهی بر روی PurchaseOrderID است:
ALTER TABLE [Purchasing].[PurchaseOrders] ADD CONSTRAINT [PK_Purchasing_PurchaseOrders] PRIMARY KEY CLUSTERED ( [PurchaseOrderID] ASC )
CREATE NONCLUSTERED INDEX [FK_Purchasing_PurchaseOrderLines_PurchaseOrderID] ON [Purchasing].[PurchaseOrderLines] ( [PurchaseOrderID] ASC )
اما بهینه سازی کوئریهای SQL Server، همیشه در یک چنین شرایطی، از merge join استفاده نمیکند. برای مثال کوئری زیر نیز دقیقا از لحاظ تعریف ایندکس بر روی OrderID، وضعیت مشابهی با کوئری قبلی دارد:
SELECT [o].[OrderID], [ol].[OrderLineID] FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID]; GO
اگر به میزان ضخامت پیکانهای این پلن، با پلن قبلی دقت کنید، مشاهده میکنید که ضخامت آنها در اینجا افزایش یافتهاست. این افزایش ضخامت پیکانها، بیانگر افزایش میزان اطلاعات ارسالی به قسمتهای مختلف است (حدود 231 هزار ردیف) به همراه اسکن بالایی بر روی ایندکس [FK_Sales_Orders_SalespersonPersonID] است (بر روی PersonID بجای OrderID) و دومی بر روی [NCCX_Sales_OrderLines]. چون ایندکس OrderID سنگین است و تعداد ردیف زیادی را شامل میشود، بهینه ساز ترجیح دادهاست تا از ایندکس دیگری استفاده کند که I/O کمتری را به همراه دارد. در اینحالت دیگر merger join میسر نبوده و از hash match استفاده کردهاست.
اگر OrderID انتخاب شده را از جدول OrderLines تهیه کنیم، چه اتفاقی رخ میدهد؟ (در کوئری قبلی، OrderID از جدول Orders انتخاب شده بود)
SELECT [ol].[OrderID], [ol].[OrderLineID] FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID];
یک بازنویسی ساده و دریافت دو ستون از یک جدول سبب شدهاست تا بهینه سازی کوئری، join تشکیل شده را غیرضروری دانسته و مستقیم عمل کند.
اهمیت مرتب شده بودن اطلاعات در تشکیل Joinهای بهینه
کوئری زیر را در نظر بگیرید که در آن یک select * را داریم (که یک ضد الگو است):
SELECT * FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID]; GO
جدول OrderLines دارای یک non-clustered index، فقط بر روی ستون OrderID است؛ اما با select * نوشته شده، تمام ستونهای آنرا درخواست کردهایم (و نه فقط OrderID را)؛ به همین جهت اطلاعات آن پیش از ارسال به merge join باید توسط عملگر sort مرتب شود و همانطور که مشاهده میکنید، هزینهی این عملگر در این پلن، 82 درصد کل است.
تاثیر order by بر روی کوئری پلن تشکیل شده
دو کوئری زیر را در نظر بگیرید که تفاوت دومی با اولی، در داشتن یک ORDER BY است:
SELECT TOP 1000 * FROM [Sales].[OrderLines]; GO SELECT TOP 1000 * FROM [Sales].[OrderLines] ORDER BY [Description]; GO
اولی، تمام clustered index را اسکن نمیکند و جائیکه 1000 ردیف را از آن بازگشت میدهد، متوقف میشود.
اما در دومی چون نیاز به مرتب سازی اطلاعات بر اساس یک ستون بودهاست، عملگر sort مشاهده میشود. اسکن آن نیز بر روی کل اطلاعات است (پیکان مرتبط با آن، نسبت به پلن قبلی ضخیمتر است) و سپس آنها را مرتب میکند.
برای بهبود این وضعیت، تعداد ستونهای بازگشت داده شده را محدود کرده و سپس بر اساس آنها، ایندکس صحیحی را طراحی میکنیم:
بنابراین اینبار بجای select *، تعداد مشخصی از ستونها را بازگشت میدهیم:
SELECT [CustomerID], [OrderDate], [ExpectedDeliveryDate] FROM [Sales].[Orders] ORDER BY [CustomerID]; GO
CREATE NONCLUSTERED INDEX [IX_Sales_Orders_CustomerID_Dates] ON [Sales].[Orders]( [CustomerID] ASC ) INCLUDE ( [OrderDate], [ExpectedDeliveryDate] ) ON [USERDATA]; GO