یکپارچه سازی اعتبارسنجی EF Code first با امکانات WPF و حذف کدهای تکرای INotifyPropertyChanged
بررسی سطح بالای مکانیزمهای اعتبارسنجی و AOP بکارگرفته شده
در حین کار با قالب پروژه WPF Framework، هنگام طراحی Modelهای خود (تفاوتی نمیکند که Domain model باشند یا صرفا Model متناظر با یک View)، نیاز است دو مورد را رعایت کنید:
[ImplementPropertyChanged] // AOP public class LoginPageModel : DataErrorInfoBase
ب) از کلاس پایه DataErrorInfoBase مشتق گردد
البته اگر به کلاسهای Domain model برنامه مراجعه کنید، صرفا مشتق شدن از BaseEntity را ملاحظه میکنید:
public class User : BaseEntity
[ImplementPropertyChanged] // AOP public abstract class BaseEntity : DataErrorInfoBase //پیاده سازی خودکار سیستم اعتبارسنجی یکپارچه
بررسی جزئیات مکانیزم AOP بکارگرفته شده
بسیار خوب؛ اینها چطور کار میکنند؟!
ابتدا نیاز است مطلب «معرفی پروژه NotifyPropertyWeaver» را یکبار مطالعه نمائید. خلاصهای جهت تکرار نکات مهم آن:
ویژگی ImplementPropertyChanged به ابزار Fody اعلام میکند که لطفا کدهای تکراری INotifyPropertyChanged را پس از کامپایل اسمبلی جاری، بر اساس تزریق کدهای IL متناظر، به اسمبلی اضافه کن. این روش از لحاظ کارآیی و همچنین تمیز نگه داشتن کدهای نهایی برنامه، فوق العاده است.
برای بررسی کارکرد آن نیاز است اسمبلی مثلا Models را دیکامپایل کرد:
همانطور که ملاحظه میکنید، کدهای تکراری INotifyPropertyChanged به صورت خودکار به اسمبلی نهایی اضافه شدهاند.
البته بدیهی است که استفاده از Fody الزامی نیست. اگر علاقمند هستید که این اطلاعات را دستی اضافه کنید، بهتر است از کلاس پایه BaseViewModel قرار گرفته در مسیر MVVM\BaseViewModel.cs پروژه Common استفاده نمائید.
در این کلاس، پیاده سازیهای NotifyPropertyChanged را بر اساس متدهایی که یک رشته را به عنوان نام خاصیت دریافت میکنند و یا متدی که امکان دسترسی strongly typed به نام رشته را میسر ساخته است، ملاحظه میکنید.
/// <summary> /// تغییر مقدار یک خاصیت را اطلاع رسانی خواهد کرد /// </summary> /// <param name="propertyName">نام خاصیت</param> public void NotifyPropertyChanged(string propertyName) /// <summary> /// تغییر مقدار یک خاصیت را اطلاع رسانی خواهد کرد /// </summary> /// <param name="expression">نام خاصیت مورد نظر</param> public void NotifyPropertyChanged(Expression<Func<object>> expression)
public class AlertConfirmBoxViewModel : BaseViewModel { AlertConfirmBoxModel _alertConfirmBoxModel; public AlertConfirmBoxModel AlertConfirmBoxModel { set { _alertConfirmBoxModel = value; NotifyPropertyChanged("AlertConfirmBoxModel"); // ویا .... NotifyPropertyChanged(()=>AlertConfirmBoxModel); } get { return _alertConfirmBoxModel; } }
بررسی جزئیات اعتبارسنجیهای تعریف شده
EF دارای یک سری ویژگی مانند Required و امثال آن است. WPF دارای اینترفیسی است به نام IDataErrorInfo. این دو را باید به نحوی به هم مرتبط ساخت که پیاده سازیهای مرتبط با آنها را در مسیرهای WpfValidation\DataErrorInfoBase.cs و WpfValidation\ValidationHelper.cs پروژه Common میتوانید ملاحظه نمائید.
<TextBox Text="{Binding Path=ChangeProfileData.UserName, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=true, ValidatesOnExceptions=true, ValidatesOnDataErrors=True, TargetNullValue=''}" />
همچنین حالتهای بررسی اعتبارسنجی آن نیز به PropertyChanged تنظیم گردیده است. در این حالت WPF به تعاریف شیء ChangeProfileData مراجعه کرده و برای نمونه اگر این شیء اینترفیس IDataErrorInfo را پیاده سازی کرده بود، نام خاصیت جاری را به آن ارسال و از آن خطاهای اعتبارسنجی متناظر را درخواست میکند. در اینجا وقت خواهیم داشت تا بر اساس ویژگیها و Data annotaions اعمالی، کار اعتبارسنجی را انجام داده و نتیجه را بازگشت دهیم.
خلاصهی تمام این اعمال و کلاسها، در کلاس پایه DataErrorInfoBase این قالب پروژه قرار گرفتهاند. بنابراین تنها کاری که باید صورت گیرد، مشتق کردن کلاس مدل مورد نظر از آن میباشد.
همچنین باید دقت داشت که نمایش اطلاعات خطاهای حاصل از اعتبارسنجی در این قالب پروژه بر اساس امکانات قالب متروی MahApps.Metro انجام میگیرد (این مورد از Silverlight toolkit به ارث رسیده است) و در حالت کلی خودکار نیست؛ اما در اینجا نیازی به کدنویسی اضافهتری ندارد.
به علاوه باید دقت داشت که این مورد ویژه را باید بر اساس آخرین Build کتابخانه MahApps.Metro که بهروزتر است دریافت و استفاده کرد. در اینجا با پارامتر Pre ذکر شده است.
PM> Install-Package MahApps.Metro -Pre
آشنایی با NHibernate - قسمت هشتم
کاری را که شما دارید انجام میدید اشتباه است.
Using statement در سی شارپ به صورت خودکار به try/finally به همراه dispose شیء احاطه شده توسط آن ترجمه میشود. (+)
به عبارتی شما یک try/finally را در یک سطح بالاتر دارید و داخل آن یک try/catch قرار دادهاید. اینکار صحیح نیست. Using را حذف کنید و try/catch/finally را جایگزین تمام موارد اضافه شده کنید.
ضمنا توصیه من این است که فقط try/catch را حذف کنید. Using سرجایش باشد تا هدف اصلی آن یعنی dispose اشیاء مرتبط حتما رخ دهد.
به تمام برنامه نویسها آموزش داده میشود که exception handling چیست اما در پایان فصل به آنها آموخته نمیشود که لطفا تا حد امکان از آن استفاده نکنید! بله، لطفا در استفاده از آن خساست به خرج دهید. چرا؟ چون کرش بر خلاف تصور عمومی چیز خوبی است! زمانیکه شما این try/catch را قرار دادید، flow برنامه متوجه نخواهد شد که در مرحلهی قبل مشکلی رخ داده و از ادامه برنامه و خسارت وارد کردن به سیستم جلوگیری کند. ضمنا این را هم به خاطر داشته باشید که exception ها در دات نت حبابی هستند. یعنی به فراخوان خود منتشر خواهند شد و در یک سطح بالاتر هم قابل catch هستند (با تمام جزئیات).
مدل مورد بررسی
public class User { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<BlogPost> BlogPosts { get; set; } } public class BlogPost { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } [ForeignKey("UserId")] public virtual User User { get; set; } public int UserId { get; set; } }
مشکل 1: بارگذاری تعداد زیادی ردیف
var data = context.BlogPosts.ToList();
راه حل: با استفاده از Skip و Take، مباحث صفحهی بندی را اعمال کنید.
مشکل 2: بازگرداندن تعداد زیادی ستون
var data = context.BlogPosts.ToList();
راه حل: اگر تنها نیاز به خاصیت Content است، از Select و سپس ToList استفاده کنید؛ البته به همراه نکته 1.
var list = context.BlogPosts.Select(x => x.Content).Skip(15).Take(15).ToList();
مشکل 3: گزارشگیریهایی که بیشباهت به حملهی به دیتابیس نیستند
foreach (var post in context.BlogPosts) { Console.WriteLine(post.User.Name); }
این مورد به lazy loading مشهور است و در مواردی که قرار است با یک مطلب و یک نویسنده کار شود، شاید اهمیتی نداشته باشد. اما در حین نمایش لیستی از اطلاعات، بیشباهت به یک حملهی شدید به بانک اطلاعاتی نیست.
راه حل: در گزارشگیریها اگر نیاز به نمایش اطلاعات روابط یک موجودیت وجود دارد، از متد Include استفاده کنید تا Lazy loading لغو شود.
foreach (var post in context.BlogPosts.Include(x=>x.User))
مشکل 4: فعال بودن بیجهت مباحث ردیابی اطلاعات
var data = context.BlogPosts.ToList();
راه حل: در گزاشگیریها، dynamic proxies را توسط متد AsNoTracking غیرفعال کنید:
var data = context.BlogPosts.AsNoTracking().Skip(15).Take(15).ToList();
مشکل 5: باز کردن تعداد اتصالات زیاد به بانک اطلاعاتی در طول یک درخواست
هر Context دارای اتصال منحصربفرد خود به بانک اطلاعاتی است. اگر در طول یک درخواست، بیش از یک Context مورد استفاده قرار گیرد، بدیهی است به همین تعداد اتصال باز شده به بانک اطلاعاتی، خواهیم داشت. نتیجهی آن فشار بیشتر بر بانک اطلاعاتی و همچنین کاهش سرعت برنامه است؛ از این لحاظ که اتصالات TCP برقرار شده، هزینهی بالایی را به همراه دارند.
روش تشخیص:
private void problem5MoreThan1ConnectionPerRequest() { using (var context = new MyContext()) { var count = context.BlogPosts.ToList(); } }
راه حل: برای حل این مساله باید از روشهای تزریق وابستگیها استفاده کرد. یک Context وهله سازی شدهی در طول عمر یک درخواست، باید بین وهلههای مختلف اشیایی که نیاز به Context دارند، زنده نگه داشته شده و به اشتراک گذاشته شود.
مشکل 6: فرق است بین IList و IEnumerable
DataContext = from user in context.Users where user.Id>10 select user;
زمانیکه در حال تهیهی گزارشی هستید، ابزارهای گزارشگیر ممکن است چندین بار از نتیجهی کوئری شما در حین تهیهی گزارش استفاده کنند. بنابراین برخلاف تصور، data binding انجام شده، تنها یکبار سبب اجرای این کوئری نمیشود؛ بسته به ساز و کار درونی گزارشگیر، چندین بار ممکن است این کوئری فراخوانی شود.
راه حل: یک ToList را به انتهای این کوئری اضافه کنید. به این ترتیب از نتیجهی کوئری، بجای اصل کوئری استفاده خواهد شد و در این حالت تنها یکبار رفت و برگشت به بانک اطلاعاتی را شاهد خواهید بود.
مشکل 7: فرق است بین IQueryable و IEnumerable
خروجی IEnumerable، یعنی این عبارت را محاسبه کن. خروجی IQueryable یعنی این عبارت را درنظر داشته باش. اگر نیاز است نتایج کوئریها با هم ترکیب شوند، مثلا بر اساس رابط کاربری برنامه، کاربر بتواند شرطهای مختلف را با هم ترکیب کند، باید از ترکیب IQueryableها استفاده کرد تا سبب رفت و برگشت اضافی به بانک اطلاعاتی نشویم.
مشکل 8: استفاده از کوئریهای Like دار
var list = context.BlogPosts.Where(x => x.Content.Contains("test"))
مشکل 9: استفاده از Count بجای Any
اگر نیاز است بررسی کنید مجموعهای دارای مقداری است یا خیر، از Count>0 استفاده نکنید. کارآیی Any و کوئری SQL ایی که تولید میکند، به مراتب بیشتر و بهینهتر است از Count>0.
مشکل 10: سرعت insert پایین است
ردیابی تغییرات را خاموش کرده و از متد جدید AddRange استفاده کنید. همچنین افزونههایی برای Bulk insert نیز موجود هستند.
مشکل 11: شروع برنامه کند است
میتوان تمام مباحث نگاشتهای پویای کلاسهای برنامه به جداول و روابط بانک اطلاعاتی را به صورت کامپایل شده در برنامه ذخیره کرد. این مورد سبب بالا رفتن سرعت شروع برنامه خصوصا در حالتیکه تعداد جداول بالا است میشود.
ICriteria API در NHibernate پیاده سازی الگوی Query Object است. مشکلی هم که این روش دارد استفاده از رشتهها جهت ایجاد کوئریهای متفاوت است؛ به عبارتی Type safe نیست. ایرادی هم به آن وارد نیست چون پیاده سازی اولیه آن از جاوا صورت گرفته و مباحث Lambda Expressions و Extension Methods هنوز در آن زبان به صورت رسمی ارائه نشده است (در JDK 7 تحت عنوان Closures قرار است اضافه شود). NHibernate 3.0 از ویژگیهای جدید زبانهای دات نتی جهت ارائهی محصور کنندهای Type safe حول ICriteria API استاندارد به نام QueryOver API سود جسته است. این پیاده سازی بسیار شبیه به عبارات LINQ است اما نباید با آن اشتباه گرفته شود زیرا LINQ to NHibernate یک ویژگی دیگر جدید، یکپارچه و استاندارد NHibernate 3.0 به شمار میرود.
برای نمونه در یک ICriteria query متداول، فراخوانیهای ذیل متداول است:
.Add(Expression.Eq("Name", "Smith"))
.Where<Person>(p => p.Name == "Smith")
مزیتهای این روش (strongly-typed fluent API) به شرح زیر است:
- خبری از رشتهها جهت استفاده از یک خاصیت وجود ندارد. برای مثال در اینجا خاصیت Name کلاس Person تحت کنترل کامپایلر قرار میگیرد و اگر در کلاس Person تغییراتی حاصل شود، برای مثال Name به LName تغییر کند، برنامه دیگر کامپایل نخواهد شد. اما در حالت ICriteria API یا باید به نتایج حاصل از Unit testing مراجعه کرد یا باید به نتایج بازخورد کاربران برنامه مانند: "باز برنامه رو تغییر دادی، یکجای دیگر از کار افتاد!" دقت نمود!
- اگر در حین ویرایش کلاس Person از ابزارهای Refactoring استفاده شود، تغییرات حاصل به صورت خودکار به تمام برنامه نیز اعمال خواهد شد. بدیهی است این اعمال تغییرات تنها در صورتی میسر است که خاصیت مورد نظر به صورت رشته معرفی نگردیده و ارجاعات به اشیاء تعریف شده به سادگی قابل parse باشند.
- در این حالت امکان بررسی نوع خواص تغییر کرده نیز توسط کامپایلر به سادگی میسر است و اگر ارجاعات تعریف شده به نحو صحیحی از این نوع جدید استفاده نکنند باز هم برنامه تا رفع این مشکلات کامپایل نخواهد شد که این هم مزیت مهمی در نگهداری سادهتر یک برنامه است.
- با بکارگیری Extension methods و پیاده سازی Fluent API جدید، مدت زمان یادگیری این روش نیز به شدت کاهش یافته، زیرا Intellisense موجود در VS.NET بهترین راهنمای استفاده از امکانات فراهم شده است. برای مثال جهت استفاده از ویژگی جدید QueryOver به سادگی میتوان پس از ساختن یک session جدید به صورت زیر عمل نمود:
IList<Cat> cats = session.QueryOver<Cat>().Where(c => c.Name == "Max").List();
جهت مشاهدهی معرفی کامل آن میتوان به مستندات NHibernate 3.0 مراجعه کرد.
تعدادی از فناوریهایی که توسط این کتابخانه پشتیبانی میشوند در زیر آمده است:
Web Service: این فناوری اجازهی ارسال و دریافت پیامهای تحت شبکه را به خصوص بر روی اینترنت، فراهم میکند و باعث ارتباط جامعتر بین برنامهها و فناوریهای مختلف میگردد. در انواع جدیدتر WCF و Web Api نیز به بازار ارائه شدهاند.
webform و MVC : فناوریهای تحت وب که باعث سهولت در ساخت وب سایتها میشوند که وب فرم رفته رفته به سمت منسوخ شدن پیش میرود و در صورتی که قصد دارید طراحی وب را آغاز کنید توصیه میکنم از همان اول به سمت MVC بروید.
Rich Windows GUI Application : برای سهولت در ایجاد برنامههای تحت وب حالا چه با فناوری WPF یا فناوری قدیمی و البته منسوخ شده Windows Form.
Windows Console Application: برای ایجاد برنامههای ساده و بدون رابط گرافیکی.
Windows Services: شما میتوانید یک یا چند سرویس تحت ویندوز را که توسط Service Control Manager یا به اختصار SCM کنترل میشوند، تولید کنید.
Database stored Procedure: نوشتن stored procedure بر روی دیتابیسهایی چون sql server و اوراکل و ... توسط فریم ورک دات نت مهیاست.
Component Libraray: ساخت اسمبلیهای واحدی که میتوانند با انواع مختلفی از موارد بالا ارتباط برقرار کنند.
Portable Class Libary : این نوع پروژهها شما را قادر میسازد تا کلاسهایی با قابلیت انتقال پذیری برای استفاده در سیلور لایت، ویندوز فون و ایکس باکس و فروشگاه ویندوز و ... تولید کنید.
ازآنجا که یک کتابخانه شامل زیادی نوع میگردد سعی شده است گروه بندیهای مختلفی از آن در قالبی به اسم فضای نام namespace تقسیم بندی گردند که شما آشنایی با آنها دارید. به همین جهت فقط تصویر زیر را که نمایشی از فضای نامهای اساسی و مشترک و پرکاربرد هستند، قرار میدهم.
در CLR مفهومی به نام Common Type System یا CTS وجود دارد که توضیح میدهد نوعها باید چگونه تعریف شوند و چگونه باید رفتار کنند که این قوانین از آنجایی که در ریشهی CLR نهفته است، بین تمامی زبانهای دات نت مشترک میباشد. تعدادی از مشخصات این CTS در زیر آورده شده است ولی در آینده بررسی بیشتری روی آنان خواهیم داشت:
- فیلد
- متد
- پراپرتی
- رویدادها
CTS همچنین شامل قوانین زیادی در مورد وضعیت کپسوله سازی برای اعضای یک نوع دارد:
- private
- public
- Family یا در زبانهایی مثل سی ++ و سی شارپ با نام protected شناخته میشود.
- family and assembly: این هم مثل بالایی است ولی کلاس مشتق شده باید در همان اسمبلی باشد. در زبانهایی چون سی شارپ و ویژوال بیسیک، چنین امکانی پیاده سازی نشدهاست و دسترسی به آن ممکن نیست ولی در IL Assembly چنین قابلیتی وجود دارد.
- Assembly یا در بعضی زبانها به نام internal شناخته میشود.
- Family Or Assembly: که در سی شارپ با نوع Protected internal شناخته میشود. در این وضعیت هر عضوی در هر اسمبلی قابل ارث بری است و یک عضو فقط میتواند در همان اسمبلی مورد استفاده قرار بگیرد.
موارد دیگری که تحت قوانین CTS هستند مفاهیم ارث بری، متدهای مجازی، عمر اشیاء و .. است.
یکی دیگر از ویژگیهای CTS این است که همهی نوعها از نوع شیء Object که در فضای نام system قرار دارد ارث بری کردهاند. به همین دلیل همهی نوعها حداقل قابلیتهایی را که یک نوع object ارئه میدهد، دارند که به شرح زیر هستند:
- مقایسهی دو شیء از لحاظ برابری.
- به دست آوردن هش کد برای هر نمونه از یک شیء
- ارائهای از وضعیت شیء به صورت رشته ای
- دریافت نوع شیء جاری
وجود COMها به دلیل ایجاد اشیاء در یک زبان متفاوت بود تا با زبان دیگر ارتباط برقرار کنند. در طرف دیگر CLR هم بین زبانهای برنامه نویسی یکپارچگی ایجاد کرده است. یکپارچگی زبانهای برنامه نویسی علل زیادی دارند. اول اینکه رسیدن به هدف یا یک الگوریتم خاص در زبان دیگر راحتتر از زبان پایه پروژه است. دوم در یک کار تیمی که افراد مختلف با دانش متفاوتی حضور دارند و ممکن است زیان هر یک متفاوت باشند.
برای ایجاد این یکپارچگی، مایکروسافت سیستم CLS یا Common Language Specification را راه اندازی کرد. این سیستم برای تولیدکنندگان کامپایلرها جزئیاتی را تعریف میکند که کامپایلر آنها را باید با حداقل ویژگیهای تعریف شدهی CLR، پشتیبانی کند.
CLR/CTS مجموعهای از ویژگیها را شامل میشود و گفتیم که هر زبانی بسیاری از این ویژگیها را پشتیبانی میکند ولی نه کامل. به عنوان مثال برنامه نویسی که قصد کرده از IL Assembly استفاده کند، قادر است از تمامی این ویژگیهایی که CLR/CTS ارائه میدهند، استفاده کند ولی تعدادی دیگر از زبانها مثل سی شارپ و فورترن و ویژوال بیسیک تنها بخشی از آن را استفاده میکنند و CLS حداقل ویژگی که بین همه این زبانها مشترک است را ارائه میکند.
شکل زیر را نگاه کنید:
یعنی
اگر شما دارید نوع جدیدی را در یک زبان ایجاد میکنید که قصد دارید در یک
زبان دیگر استفاده شود، نباید از امتیازات ویژهای که آن زبان در اختیار شما میگذارد و به بیان بهتر CLS آنها را پشتیبانی نمیکند، استفاده کنید؛ چرا
که کد شما ممکن است در زبان دیگر مورد استفاده قرار نگیرد.
using System; // Tell compiler to check for CLS compliance [assembly: CLSCompliant(true)] namespace SomeLibrary { // Warnings appear because the class is public public sealed class SomeLibraryType { // Warning: Return type of 'SomeLibrary.SomeLibraryType.Abc()' // is not CLScompliant public UInt32 Abc() { return 0; } // Warning: Identifier 'SomeLibrary.SomeLibraryType.abc()' // differing only in case is not CLScompliant public void abc() { } // No warning: this method is private private UInt32 ABC() { return 0; } } }
دومین اخطار اینکه دو متد یکسان وجود دارند که در حروف بزرگ و کوچک تفاوت دارند. ولی زبان هایی چون ویژوال بیسیک نمیتوانند تفاوتی بین دو متد abc و ABC بیابند.
نکتهی جالب اینکه اگر شما کلمه public را از جلوی نام کلاس بردارید تمامی این اخطارها لغو میشود. به این خاطر که اینها اشیای داخلی آن اسمبلی شناخته شده و قرار نیست از بیرون به آن دسترسی صورت بگیرد. عضو خصوصی کد بالا را ببینید؛ کامنت بالای آن میگوید که چون خصوصی است هشداری نمیگیرد، چون قرار نیست در زبان مقصد از آن به طور مستقیم استفاده کند.
برای دیدن قوانین CLS به این صفحه مراجعه فرمایید.
در بالا در مورد یکپارچگی و سازگاری کدهای مدیریت شده توسط CLS صحبت کردیم ولی در مورد ارتباط با کدهای مدیریت نشده چطور؟
مایکروسافت موقعیکه CLR را ارئه کرد، متوجه این قضیه بود که بسیاری از شرکتها توانایی اینکه کدهای خودشون را مجددا طراحی و پیاده سازی کنند، ندارند و خوب، سورسهای مدیریت نشدهی زیادی هم موجود هست که توسعه دهندگان علاقه زیادی به استفاده از آنها دارند. در نتیجه مایکروسافت طرحی را ریخت که CLR هر دو قسمت کدهای مدیریت شده و نشده را پشتیبانی کند. دو نمونه از این پشتیبانی را در زیر بیان میکنیم:
یک. کدهای مدیریت شده میتوانند توابع مدیریت شده را در قالب یک dll صدا زده و از آنها استفاده کنند.
دو. کدهای مدیریت شده میتوانند از کامپوننتهای COM استفاده کنند: بسیاری از شرکتها از قبل بسیاری از کامپوننتهای COM را ایجاد کرده بودند که کدهای مدیریت شده با راحتی با آنها ارتباط برقرار میکنند. ولی اگر دوست دارید روی آنها کنترل بیشتری داشته باشید و آن کدها را به معادل CLR تبدیل کنید؛ میتوانید از ابزار کمکی که مایکروسافت همراه فریم ورک دات نت ارائه کرده است استفاده کنید. نام این ابزار TLBIMP.exe میباشد که از Type Library Importer گرفته شده است.
سه. اگر کدهای مدیریت نشدهی زیادتری دارید شاید راحتتر باشد که برعکس کار کنید و کدهای مدیریت شده را در در یک برنامهی مدیریت نشده اجرا کنید. این کدها میتوانند برای مثال به یک Activex یا shell Extension تبدیل شده و مورد استفاده قرار گیرند. ابزارهای TLBEXP .exe و RegAsm .exe برای این منظور به همراه فریم ورک دات نت عرضه شده اند.
سورس کد Type Library Importer را میتوانید در کدپلکس بیابید.
در ویندوز 8 به بعد مایکروسافت API جدید را تحت عنوان WinsowsRuntime یا winRT ارائه کرده است . این api یک سیستم داخلی را از طریق کامپوننتهای com ایجاد کرده و به جای استفاده از فایلهای کتابخانهای، کامپوننتها api هایشان را از طریق متادیتاهایی بر اساس استاندارد ECMA که توسط تیم دات نت طراحی شده است معرفی میکنند.
زیبایی این روش اینست که کد نوشته شده در زبانهای دات نت میتواند به طور مداوم با apiهای winrt ارتباط برقرار کند. یعنی همهی کارها توسط CLR انجام میگیرد بدون اینکه لازم باشد از ابزار اضافی استفاده کنید. در آینده در مورد winRT بیشتر صحبت میکنیم.
سخن پایانی: ممنون از دوستان عزیز بابت پیگیری مطالب تا بدینجا. تا این قسمت فصل اول کتاب با عنوان اصول اولیه CLR بخش اول مدل اجرای CLR به پایان رسید.
ادامهی مطالب بعد از تکمیل هر بخش در دسترس دوستان قرار خواهد گرفت.
لازم به ذکر است که انواع دادهای در جاوا اسکریپت شامل 2 نوع میباشند:
1- نوع داده اولیه (Primitive) که شامل Boolean ، Number و Strings میباشند.
2- نوع داده Object که طبق تعریف هر Object مجموعهای از خواص و متدها است.
نوع دادهای اولیه، از نوع Value Type و نوع داده ای Object، از نوع Refrence Type میباشد.
برای تعریف یک شیء (Object) در جاوا اسکریپت، 3 راه وجود دارد:
1 - تعریف و ایجاد یک نمونه مستقیم از یک شیء ( direct instance of an object )
2 – استفاده از function برای تعریف و سپس نمونه سازی از یک شیء ( Object Constructor )
3 – استفاده از متد Object.Create
روش اول :
در روش اول دو راه برای ایجاد اشیاء استفاده میگردد که با استفاده از دو مثال ذیل، این دو روش توضیح داده شدهاند:
مثال اول : (استفاده از new )
<script type=”text/javascript”> var person = new Object(); person.firstname = “John”; person.lastname = “Doe”; person.age = 50; person.eyecolor = “blue”; document.write(person.firstname + “ is “ + person.age + “ years old.”); </script>
result : John is 50 years old.
مثال دوم (استفاده از literal notation )
<script type=”text/javascript”> var obj = { var1: “text1”, var2: 5, Method: function () { alert(this.var1); } }; obj.Method(); </script>
Result : text1
هر دو مثالهای 1 و 2 در روش اول برای ایجاد اشیاء بکار میروند. امکان گسترش دادن اشیاء در این روش و اضافه کردن خواص و متد در آینده نیز وجود دارد. بعنوان مثال میتوان نوشت :
Obj.var3 = “text3”;
حال در این مثال اگر مقدار شی obj را برابر یک شیء دیگر قرار دهیم به نحو زیر :
var newobj = obj; newobj.var1 = "other text"; alert(obj.var1);// other text alert(newobj.var1);// other text
میبینیم که مقدار هر دو متغیر در خروجی یکسان میباشد و این موضوع با ماهیت شیء گرایی که در آن همهی اشیایی که از روی یک الگو نمونه سازی میگردند مشخصههایی یکسان، ولی مقادیر متفاوتی دارند، متفاوت است. البته این موضوع از آنجا ناشی میگردد که اشیاء ایجاد شده در جاوا اسکریپت ذاتا type refrence هستند و به همین منظور برای پیاده سازی الگویی (کلاسی) که بتوان رفتار شیء گرایی را از آن انتظار داشت از روش زیر استفاده میکنیم. برای درک بهتر اسم این الگو را کلاس مینامیم که در روش دوم به آن اشاره میکنیم.
روش دوم :
<script type=”text/javascript”> function Person(firstname, lastname, age, eyecolor) { this.firstname = firstname; this.lastname = lastname; this.age = age; this.eyecolor = eyecolor; } var myFather = new Person("John", "Doe", 50, "blue"); document.write(myFather.firstname + " is " + myFather.age + " years old."); result : John is 50 years old. var myMother=new person("Sally","Rally",48,"green"); document.write(myMother.firstname + " is " + myFather.age + " years old."); result : Sally is 48 years old. </script>
var Person = function (firstname, lastname, age, eyecolor) { this.firstname = firstname; this.lastname = lastname; this.age = age; this.eyecolor = eyecolor; } var myFather = new Person("John", "Doe", 50, "blue"); document.write(myFather.firstname + " is " + myFather.age + " years old."); result : John is 50 years old. var myMother=new person("Sally","Rally",48,"green"); document.write(myMother.firstname + " is " + myFather.age + " years old."); result : Sally is 48 years old.
در اینجا با استفاده از کلمه کلیدی function و در داخل {} کلیه خواص و متدهای لازم را به شیء مورد نظر اضافه میکنیم. استفاده از کلمه this در داخل function به این معنی است که هر کدام از نمونههای object مورد نظر، مقادیر متفاوتی خواهند داشت .
یک مثال دیگر :
<script type="text/javascript"> function cat(name) { this.name = name; this.talk = function() { alert( this.name + " say meeow!" ) } } cat1 = new cat("felix") cat1.talk() //alerts "felix says meeow!" cat2 = new cat("ginger") cat2.talk() //alerts "ginger says meeow!" </Script>
روش سوم :استفاده از متد Object.Create
var myObjectLiteral = { property1: "one", property2: "two", method1: function() { alert("Hello world!"); }} var myChild = Object.create(myObjectLiteral); myChild.method1(); // will alert "Hello world!"
حال برای اضافه کردن متدها و خاصیتهایی به کلاس جاوا اسکریپتی مورد نظر، به طوریکه همهی نمونههایی که از این کلاس ایجاد میشوند بتوانند به این متدها و خاصیتها دسترسی داشته باشند، از مفهومی به اسم prototype استفاده میکنیم. برای مثال کلاس زیر را در نظر بگیرید:
این کلاس یک سیستم ساده امتحانی (quiz ) را پیاده میکند که در آن اطلاعات شخص که شامل نام و ایمیل میباشد گرفته شده و سه تابع، شامل ذخیره نمرات، تغییر ایمیل و نمایش اطلاعات شخص به همراه نمرات نیز به آن اضافه میشود.
function User (theName, theEmail) { this.name = theName; this.email = theEmail; this.quizScores = []; this.currentScore = 0; }
User.prototype = { saveScore:function (theScoreToAdd) { this.quizScores.push(theScoreToAdd) }, showNameAndScores:function () { var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet"; return this.name + " Scores: " + scores; }, changeEmail:function (newEmail) { this.email = newEmail; return "New Email Saved: " + this.email; } }
// A User firstUser = new User("Richard", "Richard@examnple.com"); firstUser.changeEmail("RichardB@examnple.com"); firstUser.saveScore(15); firstUser.saveScore(10); document.write(firstUser.showNameAndScores()); //Richard Scores: 15,10 document.write('<br/>'); // Another User secondUser = new User("Peter", "Peter@examnple.com"); secondUser.saveScore(18); document.write(secondUser.showNameAndScores()); //Peter Scores: 18
وراثت (Inheritance) در جاوا اسکریپت :
در بسیاری از مواقع لازم است عملکردی (Functionality) که در یک کلاس تعریف میگردد، در کلاسهای دیگر نیز در دسترس باشد. بدین منظور از مفهوم وراثت استفاده میشود. در نتیجه کلاسها میتوانند از توابع خود و همچنین توابعی که کلاسهای والد در اختیار آنها میگذارند استفاده کنند. برای این منظور چندین راه حل توسط توسعه دهندگان ایجاد شده است که در ادامه به چند نمونه از آنها اشاره میکنیم.
سادهترین حالت ممکن از الگویی شبیه زیر است:
<script type="text/javascript"> function Base() { this.color = "blue"; } function Sub() { } Sub.prototype = new Base(); Sub.prototype.showColor = function () { alert(this.color); } var instance = new Sub(); instance.showColor(); //"blue" </Script>
راه حل دیگری نیز برای اینکار وجود دارد که الگویی است بنام Parasitic Combination :
در این الگو براحتی و با استفاده از متد Object.create که در بالا توضیح داده شد، هر کلاسی که ایجاد میکنیم، با انتساب آن به یک شیء جدید، کلیه خواص و متدهای آن نیز توسط شیء جدید قابل استفاده میشود.
<script language="javascript" type="text/javascript"> if (typeof Object.create !== 'function') { Object.create = function (o) { ایجاد یک کلاس خالی که قرار است خواص کلاس دریافتی توسط آرگومان کلاس پایه را به ارث ببرد// function F() { } با ارث برده شود F باعث میشویم کلیه خواص و متدهای دریافتی توسط Prototype توسط خصوصیت F با انتساب آرگومان دریافتی که یک شی است به کلاس F.prototype = o; return new F(); }; } var cars = { type: "sedan", wheels: 4 }; // We want to inherit from the cars object, so we do: var toyota = Object.create(cars); // now toyota inherits the properties from cars document.write(toyota.type); </script> output :sedan
برای مطالعه بیشتر :
http://eloquentjavascript.net/chapter8.html
http://phrogz.net/JS/classes/OOPinJS2.html
ASP.NET MVC #4
بررسی نحوه ارتباطات بین اجزای مختلف الگوی MVC در ASP.NET MVC
اینبار برخلاف قسمت قبل، قالب پروژه خالی ASP.NET MVC را در VS.NET انتخاب کرده (ASP.NET MVC 3 Web Application و بعد انتخاب قالب Empty نمایش داده شده) و سپس پروژه جدیدی را شروع میکنیم. View Engine را هم Razor در نظر خواهیم گرفت.
پس از ایجاد ساختار اولیه پروژه، بدون اعمال هیچ تغییری، برنامه را اجرا کنید. بلافاصله با پیغام The resource cannot be found یا 404 یافت نشد، مواجه خواهیم شد.
همانطور که در پایان قسمت دوم نیز ذکر شد، پردازشها در ASP.NET MVC از کنترلرها شروع میشوند و نه از صفحات وب. بنابراین برای رفع این مشکل نیاز است تا یک کلاس کنترلر جدید را اضافه کنیم. به همین جهت بر روی پوشه استاندارد Controllers کلیک راست کرده، از منوی ظاهر شده قسمت Add، گزینهی Controller را انتخاب کنید:
در صفحه بعدی که ظاهر میشود، نام HomeController را وارد کنید (با توجه به اینکه مطابق قراردادهای ASP.NET MVC، نام کنترلر باید به کلمه Controller ختم شود). البته لازم به ذکر است که این مراحل را به همان شکل متداول مراجعه به منوی Project و انتخاب Add Class و سپس ارث بری از کلاس Controller نیز میتوان انجام داد و طی این مراحل الزامی نیست. کلاسی که به صورت خودکار از طریق منوی Add Controller یاد شده ایجاد میشود، به شکل زیر است:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
سؤال:
در قسمت دوم عنوان شد که کنترلر باید کلاسی باشد که اینترفیس IController را پیاده سازی کرده است، اما در اینجا ارث بری از کلاس Controller را شاهد هستیم. جریان چیست؟!
سلسله مراتبی که بکارگرفته شده به صورت زیر است:
public abstract class ControllerBase : IController
public abstract class Controller : ControllerBase
ControllerBase، اینترفیس IController را پیاده سازی کرده و سپس کلاس Controller از کلاس ControllerBase مشتق شده است. شاید بپرسید که این همه پیچ و تاب برای چیست؟!
مشکلی که با اینترفیس خالص وجود دارد، عدم نگارش پذیری آن است. به این معنا که اگر متدی یا خاصیتی در نگارش بعدی به این اینترفیس اضافه شد، هیچکدام از پروژههای قدیمی دیگر کامپایل نخواهند شد و باید ابتدا این متد یا خاصیت جدید را نیز لحاظ کنند. اینجا است که کار کلاسهای abstract شروع میشود. در یک کلاس abstract میتوان پیاده سازی پیش فرضی را نیز ارائه داد. به این ترتیب مصرف کننده نهایی کلاس Controller متوجه این تغییرات نخواهد شد.
اگر برنامه نویس «من» باشم، شما رو وادار خواهم کرد که متدهای جدید اینترفیس تعریفیام را پیاده سازی کنید! همینه که هست! اما اگر طراح مایکروسافت باشد، بلافاصله انبوهی از جماعت ایرادگیر که بالای 100 تا از کنترلرهای اونها الان فقط در یک پروژه از کار افتاده، ممکن است جلوی دفتر مایکروسافت دست به خود سوزی بزنند! اینجا است که مایکروسافت مجبور است تا این پیچ و تابها را اعمال کند که اگر روزی متدی در اینترفیس IController بنابر نیازهای جدید درنظر گرفته شد، بتوان سریع یک پیاده سازی پیش فرض از آنرا در کلاسهای abstract یاد شده قرار داد (یکی از تفاوتهای مهم کلاسهای abstract با اینترفیسها) تا جماعت ایرادگیر و نقزن متوجه تغییری نشوند و باز هم پروژههای قدیمی بدون مشکل کامپایل شوند. تابحال به فلسفه وجودی کلاسهای abstract از این دیدگاه فکر کرده بودید؟!
بعد از این توضیحات و کارها، اگر اینبار برنامه را اجرا کنیم، خطای زیر نمایش داده میشود:
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
همانطور که ملاحظه میکنید چون نام کنترلر ما Home است، فریم ورک در پوشه استاندارد Views در زیر پوشهای به همان نام Home، به دنبال یک سری فایل میگردد. فایلهای aspx مربوط به View Engine ایی به همین نام بوده و فایلهای cshtml و vbhtml مربوط به View Engine دیگری به نام Razor هستند.
بنابراین نیاز است تا یکی از این فایلها را در مکانهای یاد شده ایجاد کنیم. برای این منظور حداقل دو راه وجود دارد. یا دستی اینکار را انجام دهیم یا اینکه از ابزار توکار خود VS.NET برای ایجاد یک View جدید استفاده کنیم.
برای ایجاد View ایی مرتبط با متد Index (در ASP.NET MVC نام دیگر متدهای قرار گرفته در یک کنترلر، Action نیز میباشد)، روی خود متد کلیک راست کرده و گزینه Add View را انتخاب کنید:
در صفحه بعدی ظاهر شده، پیش فرضها را پذیرفته و بر روی دکمه Add کلیک نمائید. اتفاقی که رخ خواهد داد شامل ایجاد فایل Index.cshtml، با محتوای زیر است (با توجه به اینکه زبان پروژه سی شارپ است و View Engine انتخابی Razor میباشد، cshtml تولید گردید و گرنه vbhtml ایجاد میشد):
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
مجددا برنامه را اجرا کنید. اینبار بدون خطایی کلمه Index را در صفحه تولیدی میتوان مشاهده کرد. نکته جالب این فایلهای View جدید، عدم مشاهده ویژگیهای runat=server و سایر موارد مشابه است.
چند سؤال مهم:
در حین ایجاد اولین کنترلر جهت نمایش صفحه پیش فرض برنامه، نام HomeController انتخاب شد. چرا مثلا نام TestController وارد نشد؟ برنامه از کجا متوجه شد که باید حتما این کنترلر را پردازش کند. نقش متد Index چیست؟ آیا حتما باید Index باشد و در اینجا نام دیگری را نمیتوان وارد کرد؟ «قرارداد» پردازشی اینها کجا تنظیم میشود؟ فریم ورک، این سیم کشیها را چگونه انجام میدهد؟
پاسخ به تمام این سؤالها، در ویژگی «مسیریابی یا Routing» نهفته است. فایل Global.asax.cs برنامه را باز کنید. تعاریف مرتبط با مسیریابی پیش فرض را در متد RegisterRoutes آن میتوان مشاهده کرد:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
پروسه هدایت یک درخواست HTTP به یک کنترلر، در اینجا مسیریابی یا Routing نامیده میشود. این قابلیت در فضای نام System.Web.Routing تعریف شده است و باید دقت داشت که جزو ASP.NET MVC نیست. این امکانات جزو ASP.NET Runtime است و به همراه دات نت 3.5 سرویس پک یک برای اولین بار ارائه شد. بنابراین جهت استفاده در ASP.NET Web forms نیز مهیا است. در ASP.NET MVC از این امکانات برای ارسال درخواستها به کنترلرها استفاده میشود.
در متد routes.MapRoute فراخوانی شدهای که در کدهای بالا ملاحظه میکنید، کار نگاشت یک URL به Actionهای یک کنترلر صورت میگیرد (یا همان متدهای تعریف شده در کنترلرها). همچنین از این URLها پارامترهای این متدها یا اکشنها نیز قابل استخراج است.
در متد MapRoute، اولین پارامتر تعریف شده، یک نام پیش فرض است و در ادامه اگر آدرسی را به فرم «یک چیزی اسلش یک چیزی اسلش یک چیزی» یافت، اولین قسمت آنرا به عنوان نام کنترلر تفسیر خواهد کرد، دومین قسمت آن، نام متد عمومی موجود در کنترلر فرض شده و سومین قسمت به عنوان پارامتر ارسالی به این متد پردازش میشود.
برای مثال از آدرس زیر اینطور میتوان دریافت که:
http://hostname/home/about
Home نام کنترلی است که فریم ورک به دنبال آن خواهد گشت تا این درخواست رسیده را پردازش کند و about نام متدی عمومی در این کلاس است که به صورت خودکار فراخوانی میگردد. در اینجا پارامتر id ایی هم وجود ندارد. در یک برنامه امکان تعریف چندین و چند مسیریابی وجود دارد.
پارامتر سوم متد routes.MapRoute، یک سری پیش فرض را تعریف میکند و این مورد همانجایی است که از اطلاعات آن جهت تعریف کنترلر پیش فرض استفاده کردیم. برای مثال به چهار آدرس زیر دقت نمائید:
http://localhost/
http://localhost/home
http://localhost/home/about
http://localhost/process/list
در حالت http://localhost/، هر سه مقدار پیش فرض مورد استفاده قرار خواهند گرفت چون سه جزئی ({controller}/{action}/{id}) که موتور مسیریابی به دنبال آنها میگردد، در این آدرس وجود خارجی ندارد. بنابراین نام کنترلر پیش فرض در این حالت همان Home مشخص شده در پارامتر سوم متد routes.MapRoute خواهد بود و متد پیش فرض نیز Index و پارامتری هم به آن ارسال نخواهد شد.
در حالت http://localhost/home نام کنترلر مشخص است اما دو جزء دیگر ذکر نشدهاند، بنابراین مقادیر آنها از پیش فرضهای صریح ذکر شده در متد routes.MapRoute گرفته میشود. یعنی نام متد اکشن مورد پردازش، Index خواهد بود به همراه هیچ آرگومان خاصی.
به علاوه باید خاطرنشان کرد که این مقادیر case sensitive یا حساس به بزرگی و کوچکی حروف نیستند. بنابراین مهم نیست که http://localhost/HoMe باشد یا http://localhost/HOMe یا هر ترکیب دیگری.
یا اگر آدرس رسیده http://localhost/process/list باشد، این مسیریابی پیش فرض تعریف شده قادر به پردازش آن میباشد. به این معنا که کنترلری به نام Process وهله سازی و سپس متدی به نام List در آن فراخوانی خواهند شد.
یک نکته:
ترتیب routes.MapRouteهای تعریف شده در اینجا مهم است و اگر اولین URL رسیده با الگوی تعریف شده مطابقت داشت، کار را تمام خواهد کرد و به سایر تعاریف نخواهد رسید. مثلا اگر در اینجا یک مسیریابی دیگر را به نام Process تعریف کنیم:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Process", // Route name
"Process/{action}/{id}", // URL with parameters
new { controller = "Process", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
آدرسی به فرم http://localhost/Process، به صورت خودکار به کنترلر Process و متد عمومی List آن نگاشت خواهد شد و کار به مسیریابی بعدی نخواهد رسید.
به طور مثال در کلاس بالا یک کارمند میتواند فروشنده یا مهندس باشد. پیاده سازی بالا این مورد را با استفاده از دو فیلد نشان دادهاست که در صورت true بودن، مقدار هریک از آنها، نوع کلاس متناظر با آن خواهد بود.
مثلا اگر IsSalesman مقدار true داشته باشد، شیء ما کارمندی با نقش Salesman است و در صورتی که IsEngineer مقدار true داشته باشد ، شیء کارمند، نقش مهندس دارد. بماند که حالتهای دیگری نیز برای مقادیر این فیلدها وجود دارند!
تا اینجا مشکل خاصی وجود ندارد؛ بجز کمی ناخوانا شدن کد و کثیف کاری. اما مشکل اساسی زمانی پیش میآید که کلاس Employee نیاز به پیاده سازی رفتارهایی مختص به هر یک از این انواع را پیدا کند. به طور مثال شرایط کاری و امکانات مورد نیاز یک مهندس، با فروشنده متفاوت است و مثلا هنگام ثبت حکم یک فرد، نیاز به بررسی شرایط متفاوتی نسبت به نوع یک کارمند، وجود داشته باشد.
اگر با همین فرمان کدنویسی را ادامه دهیم احتمالا با کلاسی روبرو خواهیم شد که پر است از گذارههای if else ، switch و یا مواردی از این دست؛ که ابدا شرایط دلپذیری برای دوستانی که قصد نگهداری از کد ما را دارند، نیست!
زمانیکه با چنین موردی مواجه میشوید. ابتدا به ارتباط معنایی نوعهای به کار رفتهی در کلاس توجه کنید. در صورتیکه میتوان این انواع را به صورت polymorphic طراحی مجدد کرد، حتما این کار را انجام دهید. البته به ندرت مشاهده کردهام چنین چیزی امکان نداشته باشد. در صورتیکه ارتباط معنایی خاصی وجود نداشته باشد، میتوانید با استفاده از دیگر بازسازیهای کد، کلاسها را جدا کرده و دو کلاس مجزا را ایجاد نمایید. یا با استفاده از دیگر بازسازیهای کد که در آینده خواهم گفت، به طریق دیگری کد را تغییر دهید که خدا را هم خوش بیاید. به طور مثال طراحی زیر میتواند نتیجه بازسازی کلاس بالا با روش ذکر شده باشد.
مراحل انجام این بازسازی کد
- اگر type کد درونی کلاس از طریق سازنده به کلاس ارسال شده است، این سازنده را با یک متد سازنده (Factory method) جایگزین کنید.
- به ازای هر مقدار از type کدهای درونی کلاس، یک زیر کلاس جدید بسازید.
- تمامی استفادهها از type کدهای درونی کلاس را بازسازی کرده و به کلاسهای مربوط به خود منتقل کنید (احتمالا تمامی پیاده سازیهایی که if else یا switch ای بر روی مقدار type کدها دارند).
- از کلاس پایه، type کد را حذف کنید.
- در صورت وجود تمامی استفادهها از سازنده، کلاس اولیه را به استفاده از متد سازنده تغییر دهید.
- کد را کامپایل و تست نمایید.
شما در ASP.NET Core امکان کار با هر دو فریم ورک یاد شده را دارید و این دو به هم وابستگی ندارند. به عبارتی چندین target را دراینجا میتوانید معرفی و استفاده کنید. اگر دات نت 4.6 را هم استفاده کردید، برنامه فقط قابلیت چندسکویی خودش را از دست خواهد داد. برای مثال شما هم اکنون میتوانید EF 6.x را با ASP.NET Core 1.0 استفاده کنید (اگر نمیخواهید تا زمان تکمیل نهایی EF Core صبر کنید). فقط در این حالت باید دقت داشته باشید که کدهای شما بر روی لینوکس اجرا نخواهند شد (چون EF 6.x مبتنی بر دات نت 4x است).