یکی از بهترین کاربردهای React، ترکیب کردن چند کامپوننت برای ساخت یک کامپوننت است. کامپوننت ساخته شده به این روش هر چند مشخص است چه کاری انجام میدهد، اما خود کامپوننت تگها را نمیسازد و شبیه یک لگو با قطعات مختلف است. مثلا در جایی از سایت میخواهیم اطاعات کاربری را نمایش دهیم. این اطلاعات به زیر مجموعههایی تقسیم میشوند؛ مثل اطلاعات شخصی، اطلاعات مربوط به شغل، اطلاعات تماس و عکس. به جای جمع کردن همه موارد در یک کامپوننت بهتر است هر بخش ویژه را در یک کامپوننت جدا و مستقل نمایش داده و ایجاد کنیم. با این کار از هر کامپوننت میشود به صورت جداگانه در قسمتهای دیگر سایت استفاده کرد. مثلا نمایش عکس کاربر به تنهایی در بخشهای دیگر سایت. در نهایت با این روش حجم کدها هم کمتر میشود. کامپوننتی که از اجزای کوچکتر ساخته میشود، مالک کامپوننتهای زیر مجموعه خود است.
کامپوننت مالک میتواند دادههای ورودی را برای اجزای سازنده خود به صورت یک سویه (one-way data binding) فراهم کند. گاهی دادههای مورد نیاز فرزندان، به طور مستقیم به مالک و از مالک به طور غیر مستقیم به فرزندان ارسال میشود. اما ممکن است لازم باشد که دادهها در اثر محاسبه در کامپوننت مالک، ایجاد و نتیجه به فرزندان ارسال شود. به هر صورت باید دقت داشته باشیم که در هر دو حالت اگر منبع داده و یا محسابه تغییر کنند، در به روز رسانیها، توسط React بازتاب آن را خواهیم داشت. در مثال زیر کامپوننت DisplayAllInfos تشکیل شده از چهار کامپوننت دیگر است.
class DisplayAllInfos extends React.Component{ render(){ return ( <div classID="something" className="something"> <ProfilePhoto src={} /> <UserPersonalInfo userInfo={}/> <UserJobInfo jobInfo={}/> <UserContactInfo contactInfo={}/> </div> ) } }
حتی میشود تگ <div> در مثال بالا را بصورت یک کامپوننت درآورد و سایر کامپوننتها را به عنوان فرزند به آن کامپوننت معرفی کرد. روش کار به صورت زیر است.
class Container extends React.Component{ render(){ <div classID="something" className="something"> {this.props.children} </div> } } class DisplayAllInfos extends React.Component{ render(){ return ( <Container> <ProfilePhoto src={} /> <UserPersonalInfo userInfo={}/> <UserJobInfo jobInfo={}/> <UserContactInfo contactInfo={}/> </Container> ) } }
توسط خاصیت this.proprs.children در کامپوننت Container فرزندانی که برای این کامپوننت در نظر گرفته شده را نمایش میدهیم و به آنها دسترسی داریم.
روش دیگر فرزند خواندگی یک کامپوننت مالک، از طریق ایجاد آرایهای از کامپوننتهای فرزند است که در مثال نوشیدنیها آورده شد. این روش میتواند دچار اشکال شود. اگر عضوی از این آرایه حذف شود، یا اعضای آن درون آرایه تغییر مکان دهند و مسائلی از این دست که برای آرایهها پیش میآید، React قادر به تشخیص ترتیب فرزندها نیست و نمیتواند آنها را دوباره فراخوانی و ایجاد کند. برای رفع چنین مشکلی باید برای هر فرزندی که به یک مالک اضافه میکنیم، یک کلید در نظر بگیریم. از این پس وقتی آرایهای از فرزندها دچار تغییر شدند، React از روی کلیدهای منحصر به فرد آنها میتواند تغییرات را تشخیص داده و دوباره کامپوننت فرزند را به درستی بسازد. به مثال زیر توجه کنید.
var hotDrinks = [ {id: 1, item: "Tea", price: "7000" }, {id: 2, item: "Espresso", price: "10000" }, {id: 3, item: "Hot Chocolate", price: "12000" } ]; // {this.state.menuList.map(item => <MenuItem {...item} />)} var Menu = React.createClass({ render: function () { return ( <div className="row"> <div className="col-md-4"> <ul className="list-group"> {this.state.menuList.map(item => <MenuItem key={item.id} {...item} />)} </ul> </div> </div> ) } });
آن خط کدی که به صورت کامنت گذاشته شده همان روش قبل در مثال نوشیدنیهاست. در حالت اصلاح شده برای هر یک از MenuItemها یک id در نظر گرفته شده. باید توجه داشته باشیم که کلیدها حتما یگانه باشند و نکته دیگر اینکه این کلیدها فقط در زمان معرفی کامپوننت استفاده میشوند و نمیتوانیم داخل خود کامپوننت آنها را داشته باشیم. برای مثال در یک کامپوننت MenuItem مقدار this.props.key قابل استفاده نیست. هیچگاه از اندیس خود اعضای آرایه به عنوان کلید استفاده نکنید، چرا که با حذف یک مورد، در عمل وضعیت کلیدها را بهم ریختهاید و شاهد رفتاری غیرقابل پیشبینی خواهید شد.
در قسمت بعدی اعتبارسنجی را در کتابخانه React بررسی میکنیم.
در طول روند تولید یک برنامه، چه به صورت تیمی و یا حتی انفرادی، بارها برای برنامه نویسان این نیاز پیش میآید که به نسخههای قدیمیتر فایلهای خود دسترسی داشته باشند تا بتوانند آنچه را که در قبل نوشتهاند مورد بازبینی قرار دهند. شاید کسانی که با سیستمهای مدیریت نسخه آشنایی ندارند، این کار را با استفاده از copy و paste کردن فایلها در پوشههای جداگانه انجام دهند؛ اما روند توسعه یک برنامه در محیط عملی، امکان استفاده از چنین روشی را به ما نمیدهد. زیرا مدیریت این فایلها علی الخصوص در پروژههای تیمی، بعد از مدتی بسیار دشوار خواهد شد. بنابراین نیاز به سیستمی احساس میشود که بتواند این کار را به صورت خودکار انجام دهد.
Git توسط سازنده سیستم عامل لینوکس یعنی آقای Linus Torvalds و برای مدیریت کدهای آن ساخته شد که بعدها توسط Linux-BitKeeper ارتقا یافت. BitKeeper یک سیستم مدیریت کد توزیع شده است که البته رایگان نیست. تیم BitKeeper در ابتدا پروژه لینوکس را به صورت رایگان پشتیبانی میکرد اما در سال 2005 این حمایت را قطع کرد. در این هنگام تیم توسعه لینوکس تصمیم گرفت که خود یک سیستم مدیریت کد توزیع شده ایجاد کند. آنها این سیستم را با Perl و C نوشتند و آن را برای اجرا شدن بر روی انواع سیستم عاملها نظیر لینوکس ویندوز و حتی مک آماده کردند اهداف اصلی Git عبارتند از:
سیستمهای کنترل نسخه را میتوان بر اساس خصوصیات مختلف در دستههای متفاوتی قرار داد اما از نظر معماری سیستم, به دو دستهی زیر تقسیم میشوند :
مخزن یا همان Repository محلی است که یک سیستم مدیریت نسخه از آن برای نگهداری تغییرات فایلها استفاده میکند. در سیستمهای VCS این مخزن به صورت متمرکز یا اصطلاحا Centralized Repository میباشد. به این معنا که یک Repository بر روی یک ماشین، خواه سیستم خود برنامه نویس(در پروژههای انفرادی) و خواه یک سرور قرار دارد (در پروژههای تیمی) و برنامه نویسان تغییرات فایلهای خود را به سمت این سرور میفرستند و این سرور وظیفه نگهداری تمامی نسخهها و اطلاعات مربوطه از برنامه نویسان مختلف را به عهده دارد. اشکال این روش در این است که برنامه نویس تنها به نسخه جاری که بر روی سیستم خود است دسترسی دارد و اگر بنا به دلیلی بخواهد از نسخههای پیشین استفاده کند باید آن را از سرور بخواهد که این کار مشکل دیگری ایجاد میکند و آن این است که ممکن است برنامه نویس همیشه در موقعیتی نباشد که بتواند به سرور دسترسی داشته باشد. به همین دلیل این روش وابستگی زیادی برای برنامه نویس ایجاد میکند اما پیاده سازی این روش آسانتر از مدل توزیع شده است.
پاسخ به این سوال بسیار ساده است: هر آنچه برای ما مهم است که این شامل فایلهای کد, فایلهای پیکربندی, خروجیهای نظیر dll و غیره است. البته در این بین استثنائاتی نظیر فایلهای EXE و یا پکیجهای نصب شده وجود دارد که در بسیاری از موارد نیازی به پیگیری نسخههای آنها نیست اما تمامی اینها وابسته به نظر برنامه نویس است.
تعریف نیازمندیهای اپلیکیشن
- تنها کاربران احراز هویت شده قادر خواهند بود تا لیست ToDoهای خود را ببینند، آیتمهای جدید ثبت کنند یا دادههای قبلی را ویرایش و حذف کنند.
- کاربران نباید آیتمهای ایجاد شده توسط دیگر کاربران را ببینند.
- تنها کاربرانی که به نقش Admin تعلق دارند باید بتوانند تمام ToDoهای ایجاد شده را ببینند.
Database.SetInitializer<MyDbContext>(new MyDbInitializer());
ایجاد نقش مدیر و کاربر جدیدی که به این نقش تعلق دارد
public class MyDbInitializer : DropCreateDatabaseAlways<MyDbContext> { protected override void Seed(MyDbContext context) { var UserManager = new UserManager<MyUser>(new UserStore<MyUser>(context)); var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context)); string name = "Admin"; string password = "123456"; //Create Role Admin if it does not exist if (!RoleManager.RoleExists(name)) { var roleresult = RoleManager.Create(new IdentityRole(name)); } //Create User=Admin with password=123456 var user = new MyUser(); user.UserName = name; var adminresult = UserManager.Create(user, password); //Add User Admin to Role Admin if (adminresult.Succeeded) { var result = UserManager.AddToRole(user.Id, name); } base.Seed(context); } }
public class MyUser : IdentityUser { public string HomeTown { get; set; } public virtual ICollection<ToDo> ToDoes { get; set; } } public class ToDo { public int Id { get; set; } public string Description { get; set; } public bool IsDone { get; set; } public virtual MyUser User { get; set; } }
public class MyDbContext : IdentityDbContext<MyUser> { public MyDbContext() : base("DefaultConnection") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { public System.Data.Entity.DbSet<AspnetIdentitySample.Models.ToDo> ToDoes { get; set; } }
تنها کاربران احراز هویت شده باید قادر به اجرای عملیات CRUD باشند
[Authorize] public class ToDoController : Controller
private MyDbContext db; private UserManager<MyUser> manager; public ToDoController() { db = new MyDbContext(); manager = new UserManager<MyUser>(new UserStore<MyUser>(db)); }
اکشن متد Create را بروز رسانی کنید
public async Task<ActionResult> Create ([Bind(Include="Id,Description,IsDone")] ToDo todo) { var currentUser = await manager.FindByIdAsync (User.Identity.GetUserId()); if (ModelState.IsValid) { todo.User = currentUser; db.ToDoes.Add(todo); await db.SaveChangesAsync(); return RedirectToAction("Index"); } return View(todo); }
اکشن متد List را بروز رسانی کنید
public ActionResult Index() { var currentUser = manager.FindById(User.Identity.GetUserId()); return View(db.ToDoes.ToList().Where( todo => todo.User.Id == currentUser.Id)); }
تنها مدیران سایت باید بتوانند تمام ToDoها را ببینند
[Authorize(Roles="Admin")] public async Task<ActionResult> All() { return View(await db.ToDoes.ToListAsync()); }
نمایش جزئیات کاربران از جدول ToDo ها
@model IEnumerable<AspnetIdentitySample.Models.ToDo> @{ ViewBag.Title = "Index"; } <h2>List of ToDoes for all Users</h2> <p> Notice that we can see the User info (UserName) and profile info such as HomeTown for the user as well. This was possible because we associated the User object with a ToDo object and hence we can get this rich behavior. 12: </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.Description) </th> <th> @Html.DisplayNameFor(model => model.IsDone) </th> <th>@Html.DisplayNameFor(model => model.User.UserName)</th> <th>@Html.DisplayNameFor(model => model.User.HomeTown)</th> </tr> 25: 26: @foreach (var item in Model) 27: { 28: <tr> 29: <td> 30: @Html.DisplayFor(modelItem => item.Description) 31: </td> 32: <td> @Html.DisplayFor(modelItem => item.IsDone) </td> <td> @Html.DisplayFor(modelItem => item.User.UserName) </td> <td> @Html.DisplayFor(modelItem => item.User.HomeTown) </td> </tr> } </table>
صفحه Layout را بروز رسانی کنید تا به ToDoها لینک شود
<li>@Html.ActionLink("ToDo", "Index", "ToDo")</li> <li>@Html.ActionLink("ToDo for User In Role Admin", "All", "ToDo")</li>
ساخت یک ToDo بعنوان کاربر عادی
پس از ساختن یک ToDo میتوانید لیست رکوردهای خود را مشاهده کنید. دقت داشته باشید که رکوردهایی که کاربران دیگر ثبت کرده اند برای شما نمایش داده نخواهند شد.
مشاهده تمام ToDoها بعنوان مدیر سایت
User = Admin Password = 123456
در اینجا، یک سری نکات را در طول برنامه نویسی، متذکر میشوم تا مدیریت کدهای شما را در اندروید راحتتر کند.
یک نکتهی دیگر را که باید متذکر شوم این است که همه اصطلاحاتی که در این مقاله استفاده میشوند بر اساس اندروید استادیو و مستندات رسمی گوگل است است؛ به عنوان نمونه عبارتهای ماژول و پروژه آن چیزی هستند که ما در اندروید استادیو به آنها اشاره میکنیم، نه آنچه که کاربران Eclipse به آن اشاره میکنند.
یک. برای هر تکه کد و یا متدی که مینویسید مستندات کافی قرار دهید و اگر این متد نیاز به مجوز خاصی دارد مانند نمونه زیر، آن را حتما ذکر کنید:
/** * * <p> * check network is available or not <br/> * internet connection is not matter,for check internet connection refer to IsInternetConnected() Method in this class * </p> * <p> * Required Permission : <b>android.permission.ACCESS_NETWORK_STATE</b> * </p> * @param context * @return returns true if a network is available */ public boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); }
public class ProjectSettings { public static NotificationsId=new NotificationsId(); public static UrlAddresss=new UrlAddresss(); public static SdPath=new SdPath(); ...... }
ProjectSettings.NotificationsId.UpdateNotificationId
سه. حداکثر استفاده از اینترفیس را به خصوص برای UI انجام بدهید:
به عنوان نمونه، بسیاری نمایش یک toast را به شکل زیر انجام میدهند:
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
public interface IMessageUI { void ShowToast(Context context,String message); } public class MessageUI impelement IMessageUI { public void ShowToast(Context context,string message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } }
چهار. اگر برای اولین بار است وارد اندروید میشوید، خوب چرخههای یک شیء، چون اکتیویتی یا فراگمنت را یاد بگیرید تا در آینده با مشکلات خاصی روبرو نشوید.
چون اکتیویتی در حالت stop بوده و بعد از آن به حالت Resume رفته و تا موقعی که این اکتیویتی از حافظه خارج نشود یا گوشی چرخش نداشته باشد، واکشی دیتاها صورت نخواهد گرفت. پس بهترین مکان در این حالت، رویداد OnStart است که در هر دو وضعیت صدا زده میشود؛ یا اینکه در OnRestatr روی آداپتور تغییرات جدید را اعمال کنید تا نیازی به واکشی مجدد دادهها نباشد.
تا بدینجا اکتیویتی مشکلی ندارد و میتواند به عملیات پاسخ دهد ولی اگر قسمتی از اکتیویتی در زیر لایهای از UI پنهان شود، به عنوان مثال دیالوگی باز شود که قسمتی از اکتیویتی را بپوشاند و یا منویی همانند تلگرام قسمتی از صفحه را بپوشاند، اکتیویتی اصطلاحا در حالت Pause قرار گرفته و بدین ترتیب رویداد OnPause اجرا میگردد. اگر همین دیالوگ بسته شود و مجددا اکتیویتی به طور کامل نمایان گردد مجددا رویداد OnResume اجرا میگردد.
از رویداد Onresume میتوانید برای کارهایی که بین زمان آغاز اکتیویتی و برگشت اکتیویتی مشترکند استفاده کرد. اگر به هر نحوی اکتیویتی به طور کامل پنهان شود٬، به این معناست که شما به اکتیویتی دیگری رفتهاید رویداد OnStop اجرا شدهاست و در صورت بازگشت، رویداد OnRestart اجرا خواهد شد. ولی اگر مدت طولانی از رویداد OnStop بگذرد احتمال اینکه سیستم مدیریت منابع اندروید، اکتیویتی شما را از حافظه خارج کند زیاد است و رویداد OnDestroy صورت خواهد گرفت. در این حالت دفعه بعد، مجددا همه عملیات از ابتدا آغاز میگردند.
شش. اگر برنامه شما قرار است در چندین حالت مختلفی که اتفاق میافتد، یک کار خاصی را انجام دهد، برای برنامهتان یک Receiver بنویسید و در آن کدهای تکراری را نوشته و در محلهای مختلف وقوع آن رویدادها، رسیور را صدا بزنید. برای نمونه برنامه تلگرام یک سرویس پیام رسان پشت صحنه دارد که در دو رویداد قرار است اجرا شوند. یکی موقعی که گوشی بوت خود را تکمیل کرده است و در حال آغاز فرایندهای سیستم عامل است و دیگر زمانی است که برنامه اجرا میشود. در اینجا تلگرام از یک رسیور سیستمی برای آگاهی از بوت شدن و یک رسیور داخل برنامه جهت آگاهی از اجرای برنامه استفاده میکند و هر دو به یک کلاس از جنس BroadcastReceiver متصلند:
<receiver android:name=".AppStartReceiver" android:enabled="true"> <intent-filter> <action android:name="org.telegram.start" /> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
public class AppStartReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { ApplicationLoader.startPushService(); } }); } }
برنامه تلگرام حتی برای حالتهای پخش هم رسیورها استفاده کرده است که در همین رسیور وضعیت تغییر پلیر مشخص میشود:
<receiver android:name=".MusicPlayerReceiver" > <intent-filter> <action android:name="org.telegram.android.musicplayer.close" /> <action android:name="org.telegram.android.musicplayer.pause" /> <action android:name="org.telegram.android.musicplayer.next" /> <action android:name="org.telegram.android.musicplayer.play" /> <action android:name="org.telegram.android.musicplayer.previous" /> <action android:name="android.intent.action.MEDIA_BUTTON" /> <action android:name="android.media.AUDIO_BECOMING_NOISY" /> </intent-filter> </receiver>
هفت. اگر از یک ORM برای لایه دادهها استفاده میکنید (قبلا در سایت جاری در مورد ORMهای اندروید صحبت کردهایم و ORMهای خوش دستی که خودم از آنها استفاده میکنم ActiveAndroid و CPORM هستند که هم کار کردن با آنها راحت است و هم اینکه امکانات خوبی را عرضه میکنند) در این نوع ORMها شما نباید انتظار چیزی مانند EF را داشته باشید و در بعضی موارد باید کمی خودتان کمک کنید. به عنوان مثال در Active Android برای ایجاد یک inner join باید به شکل زیر بنویسید:
From query= new Select() .from(Poem.class) .innerJoin(BankPoemsGroups.class) .on("poems.id=bank_poems_groups.poem") .where("BankGroup=?", String.valueOf(groupId)); return query.execute();
@Table(name="poems") public class Poem extends Model { public static String tableName="poems"; public static String codeColumn="code"; public static String titleColumn="title"; public static String bookColumn="book"; ...... @Column(name="code",index = true) public int Code; @Column(name="title") public String Title; @Column(name="book") public Book Book; .....}
From query= new Select() .from(Poem.class) .innerJoin(BankPoemsGroups.class) .on(Poem.TableName+"."+ Poem.IdColumn+"="+ BankPoemsGroups.TableName+"."+ BankPoemsGroups.PoemColumn) .where(Poem.BankGroupColumn+"=?", String.valueOf(groupId)); return query.execute();
public class QueryConcater { public String GetInnerJoinQuery(String table1,String field1,String table2,String field2) { String query=table1 +"." +field1+"="+table2+"."+field2; return query; } ...... }
return new Select() .from(Color.class) .innerJoin(ProductItem.class) .on(queryConcater.GetInnerJoinQuery(ProductItem.TableName, ProductItem.ColorColumn, Color.TableName)) .where(queryConcater.WhereConditionQuery (ProductItem.TableName, ProductItem.ProductColumn), productId) .execute();
هشت. سعی کنید همیشه از یک سیستم گزارش خطا در اپلیکیشن خود استفاده کنید. در حال حاضر معروفترین سیستم گزارش خطا Acra است که میتوانید backend آن را هم از اینجا تهیه کنید و اگر هم نخواستید، سایت Tracepot امکانات خوبی را به رایگان برای شما فراهم میکند. از این پس با سیستم آکرا شما به یک سیستم گزارش خطا متصلید که خطاهای برنامه شما در گوشی کاربر به شما گزارش داده خواهد شد. این گزارشها شامل:
- وضعیت گوشی در حین باز شدن برنامه و در حین خطا چگونه بوده است.
- مشخصات گوشی
- این خطا به چه تعداد رخ داده است و برای چه تعداد کاربر
- گزارش گیری بر اساس اولین تاریخ رخداد خطا و آخرین تاریخ، نسخه سیستم عامل اندروید، ورژن برنامه شما و...
نه. آکرا همانند Elmah نمیتواند خطاهای catch شده را دریافت کند. برای حل این مشکل عبارت زیر را در catchها بنویسید:
ACRA.getErrorReporter().handleException(caughtException);
نمونه اشتباه:
public void CopyFile(String source,String destination,CopyFileListener copyFileListener) { try { InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(destination); long fileLength=new File(source).length(); // Transfer bytes from in to out byte[] buf = new byte[64*1024]; int len; long total=0; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); total+=len; copyFileListener.PublishProgress(fileLength,total); } in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } }
public void CopyFile(String source,String destination,CopyFileListener copyFileListener) throws IOException { InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(destination); long fileLength=new File(source).length(); // Transfer bytes from in to out byte[] buf = new byte[64*1024]; int len; long total=0; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); total+=len; copyFileListener.PublishProgress(fileLength,total); } in.close(); out.close(); }
کدهای متورم (Bloaters)
- استفاده از متغیرهای اولیه بجای ساختارهای کوچک برای کارهای اولیه مانند Currency, DateTime, PhoneNumber
- استفاده از constantها برای کد کردن اطلاعات مانند USER_ADMIN_ROLE = 1
- استفاده از constantهای رشتهای به عنوان نام فیلدها در آرایههای داده
بد استفاده کنندگان از شیء گرایی (Object orientation abusers)
جلوگیری کنندگان از تغییر(Change preventers)
کدهای غیر ضروری (Dispensables)
کدهایی بیش از اندازه وابسته به هم (Couplers)
خطای هم نامی خواص راهبری
بله، در حالت تعریف خواص یک کلاس که قرار است امکان تعریف قالبهای متعددی توسط کاربر را داشته باشد، از اینترفیسها هم ممکن است استفاده شود. در خود PdfReport در بسیاری از موارد از این روش استفاده شده. مثلا تعریف فونت آن یا بسیاری از سفارشی سازیهای دیگر.
اما کار با ORMها یک سری اصول عمومی پذیرفته شده دارد و جهت تعریف خواص تو در توی کلاسهای آن، استفاده از اینترفیسها عمومیت ندارد.
شاید عنوان کنید که DI من کار تزریق وهله مورد نظر رو انجام میده ولی .... خود ORM یک سربار است. DI هم سربار دوم. آیا کارآیی اینجا باید فدای طراحی نادرستی شود؟ استفاده از اینترفیسها الزاما به معنای حرفهای بودن یک طراحی نیست. بستگی داره کجا استفاده شدند. آیا قابلیت سفارشی سازی و افزونه پذیری رو ارائه میکنه؟ یا خیر؟
آیا یک توسعه دهندهی وب میتواند یک طراح وب هم باشد؟
شما در ASP.NET Core امکان کار با هر دو فریم ورک یاد شده را دارید و این دو به هم وابستگی ندارند. به عبارتی چندین target را دراینجا میتوانید معرفی و استفاده کنید. اگر دات نت 4.6 را هم استفاده کردید، برنامه فقط قابلیت چندسکویی خودش را از دست خواهد داد. برای مثال شما هم اکنون میتوانید EF 6.x را با ASP.NET Core 1.0 استفاده کنید (اگر نمیخواهید تا زمان تکمیل نهایی EF Core صبر کنید). فقط در این حالت باید دقت داشته باشید که کدهای شما بر روی لینوکس اجرا نخواهند شد (چون EF 6.x مبتنی بر دات نت 4x است).