EF Code First #4
Update-Database -StartUpProjectName "...name..." -ConnectionString "...data..." -ConnectionProviderName "System.Data.SqlClient"
- «بازسازی جدول MigrationHistory با کد نویسی در EF Code first»
آشنایی با الگوی MVP
احتمالا 3 tier را شنیدهاید که به این صورت مطرح کردید.
n-tier نوعی معماری است که به شما در تهیه برنامههای توزیع شده کمک میکند و مهمترین مزیت آن قابلیت بسط پذیری سیستم است. Tiering در مورد تخصیص منابع و نحوهی توزیع آنها بحث میکند. برای مثال دیتابیس سرور شما جدا است، منطق برنامه در سروری دیگر توسط یک وب سرویس قابل دسترسی است و سروری دیگر کار دریافت و ارائه این اطلاعات را به عهده خواهد داشت.
MVC که در ابتدا پدید آمد و بعد از آن MVP ، یک نوع الگوی برنامه نویسی شیءگرا هستند که به شما کمک خواهند کرد تا برنامهی n-tier ایی با حداقل گره خوردگی و به هم پیچیدگی که اصطلاحا به آن Loosely coupled نیز گفته میشود، تولید کنید.
- بررسی ابزار Advanced Group Policy Management (قسمت سوم) | (رضا علیخانی) | www.iransec.ir
- برطرف کردن مشکل افزونه Regionerate در Visual Studio | محمد صاحب | www.dotnetdev.info
- برنامه نویسی تجاری و چندلایه در دات نت | ebook.veyq.ir
- تغییر روش | (Afshar Mohebbi) | blog.afsharm.com
- چرا از استیو جابزها خوششان نمیاید؟ | علی مختاری | www.mywindows.ir
- Paint.NET v3.5.10 منتشر شد | blog.getpaint.net
- SQL-CE Toolbox 2.4 منتشر شد | sqlcetoolbox.codeplex.com
اگر مدتها کارتان برنامه نویسی WinForms بوده و اکنون احساس کردهاید که دیگر WinForms آنچنان توسعه و بسط نخواهد یافت و اکنون WPF تبدیل به انتخاب اصلی شرکتهای بزرگ شده است و همچنین از پرسه زدن در فورومهای وارز جهت یافتن فلان کامپوننت خاص برای زیباسازی ظاهر برنامههای خود خسته شدهاید و نیاز به معادل بهتری که اساسا در جهت حذف این بازار سیاه تهیه شده است، احساس میکنید، بهترین گزینهی موجود WPF خواهد بود که با کمی دقت، میتوان پروژههای آنرا تبدیل به پروژههای وب نیز نمود. مطلب 54 صفحهای ذیل، خلاصهی کاربردی سریعی را جهت ارتقاء برنامه نویسهای WinForms به WPF ارائه میدهد:
ماخذ
دالویک در برابر مونو
مقایسه جدیدتر اینجا هست.
البته برای چند روز پیش نیست...
یک قانون مهم در موفقیت استارتاپ ها
یکی از API های کاربردی و جدید در دنیای وب، BroadcastChannel است که امکان ارسال اطلاعات بین windowها ، Tabها و iframeهای مختلف را که در یک دامنه هستند، فراهم میکند. برای
مثال اگر شما در مرورگری در پنجرههای مختلف یک سایت را باز کرده باشید،
با تغییر در یکی از این پنجرهها، قادر خواهید بود سایر پنجرها را هم مطلع کنید تا در
صورت نیاز، مجددا بارگذاری شوند.
چرا از این API استفاده کنیم؟
یکی از
وب سایتهای مورد علاقهی خود را در مرورگر باز کنید. مثلا یوتیوب و لاگین کنید. حالا در
پنجرهی جدیدی، همین وب سایت را مجددا باز کرده و لاگین کنید. حالا در یکی از پنجرهها، از یوتیوب Logout کنید. خب شما حالا در یکی از پنجرهها لاگین هستید و در یکی دیگر Logout کردهاید. حالا
پنجرههای مرورگر شما دارای دو وضعیت متفاوت هستند. Logged-in در برابر Logged-out و این گاهی باعث دردسر خواهد شد.
این وضعیت حتی میتواند باعث خطرهای امنیتی نیز بشود. تصور کنید که کاربری در یک فضای عمومی مثل یک کافی شاپ وارد سایت شما شدهاست و داشبرد مخصوص به خود را باز کردهاست. بنا به دلایلی کاربر قصد ترک محل را کرده و طبیعتا از برنامه شما Logout خواهد کرد . در این حالت اگر این کاربر برنامه شما را در صفحات مختلف مرورگر باز کرده باشد و لاگین نیز کرده باشد، هر کسی که بعد از او قصد استفاده از این کامپیوتر را داشته باشد ، میتواند به اطلاعات کاربر مورد نظر در آن صفحات دسترسی پیدا کند؛ چه این اطلاعات روی صفحه باشد و چه مثلا اطلاعات یک JWT token. چون کاربر فراموش کرده در صفحات دیگر هم Logout کند.
کد نویسی BroadcastChannel API
در نگاه اول، استفاده از این API ممکن است سخت به نظر برسد؛ ولی در واقع خیلی راحت است. برای نمونه قطعه کد زیر را درنظر بگیرید:
<!DOCTYPE html> <body> <!-- The title will change to greet the user --> <h1 id="title">Hey</h1> <input id="name-field" placeholder="Enter Your Name"/> </body> <script> var bc = new BroadcastChannel('gator_channel'); (()=>{ const title = document.getElementById('title'); const nameField = document.getElementById('name-field'); const setTitle = (userName) => { title.innerHTML = 'Hey ' + userName; } bc.onmessage = (messageEvent) => { // If our broadcast message is 'update_title' then get the new title from localStorage if (messageEvent.data === 'update_title') { // localStorage is domain specific so when it changes in one window it changes in the other setTitle(localStorage.getItem('title')); } } // When the page loads check if the title is in our localStorage if (localStorage.getItem('title')) { setTitle(localStorage.getItem('title')); } else { setTitle('please tell us your name'); } nameField.onchange = (e) => { const inputValue = e.target.value; // In the localStorage we set title to the user's input localStorage.setItem('title', inputValue); // Update the title on the current page setTitle(inputValue); // Tell the other pages to update the title bc.postMessage('update_title'); } })() </script>
این کد شامل یک Input باکس و یک title است. وقتی کاربر نام خود را در Input باکس وارد میکند، برنامه آن را در Localstorage با کلیدی به نام userName ذخیره میکند و بعد title صفحه جاری را به سلام + userName تغییر میدهد. مثلا اگر کاربر در Input باکس، عبارت بابک را وارد کند، title صفحه به سلام بابک تغییر داده میشود.
بدون BroadcastChannel، چنانچه کاربر در پنجرههای مختلف مرورگر، برنامه را باز کرده باشد، تغییری در Title آن صفحات داده نخواهد شد؛ مگر اینکه مجددا توسط کاربر بارگذاری شود.
در کد فوق ما یک وهله از BroadcastChannel را به نام gator_channel ایجاد کردهایم و بعد onmessage را مساوی متدی با یک آرگومان جهت دریافت پیام قرار دادهایم. در این متد چک شده که اگر نام پیام، مساوی update_title باشد، متغیر ذخیره شدهی در LocalStorage خوانده شود.
هربار که متد postMessage ، از BroadcastChannel را فراخوانی میکنیم، این متد، باعث اجرای متد onmessage در سایر پنجرهها میشود. پس اگر در پنجرهی جاری در Input باکس، کلمه فرهاد را بنویسیم، متد bc.postMessage('update_title') در پنجره جاری اجرا شده و باعث اجرای متد onmessage در سایر پنجرههایی که سایتمان در آن باز است میشود.
این API در چه حالتهایی کار میکند
برخلاف سایر APIها مثل window.postMessage، شما لازم نیست چیزی در مورد اینکه چند تا صفحه از سایتتان بر روی مرورگر جاری باز شده را بدانید. (توجه کنید که روی عبارت «مرورگر جاری» تاکید میکنم. چون اگر برنامه روی دو مرورگر مثلا Chrome و Firefox به صورت همزمان باز باشد، این API فقط روی صفحات باز مرورگر جاری فراخوانی خواهد شد و نه مرورگر دوم؛ توضیحات بیشتر در ادامه داده شده است)
BroadcastChannel فقط روی مرورگر جاری و صفحاتی از یک دامنه، اجرا خواهد شد. این به این معنا است که شما میتوانید پیامهایتان را از دامنه مثلا : https://alligator.io به دامنه https://alligator.io/js/broadcast_channels ارسال کنید. تنها نکتهای که لازم است تا رعایت شود این است که آبجکت BroadcastChannel در هر دو صفحه، از یک نام برای channel استفاده کرده باشند:
const bc = new BroadcastChannel('alligator_channel'); bc.onmessage = (eventMessage) => { // do something different on each page }
در حالتهای زیر این API کار نخواهد کرد:
هاستهای متفاوت:
https://alligator.io
https://www.aligator.io
پورتهای متفاوت:
https://alligator.io
https://alligator.io :8080
پروتکلهای متفاوت:
https://alligator.io
http://alligator.io
و یا برای مثال اگر مثلا در مرورگر Chrome یکی از صفحات به صورت Incognito باز شده باشد.
سازگاری این API با مرورگرهای مختلف
با توجه به اطلاعات سایت caniuse.com، این API در 75.6% مرورگرها پشتیبانی میشود. ولی مرورگرهای Safari و Internet Explorer از این API پشتیبانی نمیکنند. همچنین امکان استفاده از این API توسط کتابخانه sysend.js نیز فراهم شدهاست.
چه نوع پیامهایی را میتوانید به کمک این API ارسال کنید
- تمامی تایپها (Boolean,Null, Undefined,Number,BigInt, String) به غیر از symbol
- آبجکتهای Boolean و String
- Dates
- Regular Expressions
- Blobs
- Files, File Lists
- Array Buffer, ArrayBufferViews
- ImageBitmaps, ImageDates
- Arrays,Objects,Maps and Sets
قطعه کد زیر، بجای string، یک object را ارسال میکند:
bc.onmessage = (messageEvent) => { const data = messageEvent.data // If our broadcast message is 'update_title' then get the new title from localStorage switch (data.type) { case 'update_title': if (data.title){ setTitle(data.title); } else setTitle(localStorage.getItem('title')); break default: console.log('we received a message') } }; // ... Skipping Code bc.postMessage({type: 'update_title', title: inputValue});
چه کارهایی را میتوانید به کمک این API انجام دهید
چیزهای زیادی را میتوان مجسم کرد. محتملترین گزینه، به اشتراک گذاری state جاری برنامه است. برای مثال اگه از کتابخانههای flux یا redux برای مدیریت state برنامه استفاده میکنید، به کمک این API میتوانید state جاری را در تمامی صفحات باز برنامه، بروز رسانی کنید. حتی میتوانید به چیزی شبیه به machine state فکر کنید.
یا مثلا آخرین وضعیت سبد خرید کالای مشتری و یا موجودی کالاها، در یک سایت خرید آنلاین. همچنین به اشتراک گذاری فایلهای حجیم مثل عکس و غیره جهت جلوگیری از دانلود مجدد آنها در سایر صفحات.
به کمک دستور ()bc.close در هر زمانی میتوانید channel باز شده را ببندید و مجددا بسته به وضعیت برنامه، آن را باز کنید.
معنای لغوی intent : هدف، قصد، نیت و امثالهم...
Intent.ACTION_VIEW
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(https://github.com);
برای فراخوانی باید کد زیر در برنامه درج شود:
startActivity(intent);
نکته: برای کنترل اینکه کدام برنامهها میتوانند intent خاصی را دریافت کنند باید یک مجوز (permission) را قبل از فراخوانی به آن ارسال کنیم.
بررسی مجوزها
استفاده از Self-Defined Permissions
اندروید به توسعه دهندگان اجازه میدهد تا مجوزهای خود را ساخته و آنها را اجرا کنند. همانند مجوزهای سیستمی که پلتفرم آنها را بررسی میکند، شما باید خواص و برچسبهای مورد نیاز را در فایل AndroidManifest.xml اعلام کنید. اگر برنامهای مینویسید که یک نوعِ خاص از قابلیت دسترسی توسط توسعه دهندگان را فراهم میکند، شما میتوانید برای حفاظت از توابع با مجوزهای سفارشی خود، مانع دسترسیهای غیرمجاز شوید. به کدی که در فایل AndroidMainfest درج شده دقت نمایید:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.zenconsult.mobile.testapp" > <permission android:name="net.zenconsult.mobile.testapp.permission.PURGE_DATABASE" android:label="@string/label_purgeDatabase" android:description="@string/description_purgeDatabase" android:protectionLevel="dangerous" /> ... </manifest>
android:name <attribute>
android:label android:description
<string name=" label_purgeDatabase ">purge the application database </string> <string name="permdesc_callPhone">Allows the application to purge the core database of the information store. Malicious applications may be able to wipe your entire application information store.</string>
مشخصه android:protectionLevel که در چند خط قبل در فایل تنظیمات درج شده است، مورد نیاز است و باید فراخوانده شود. همچنین میتوانید یک مشخصه به نام android:permissionGroup را تعریف کنید تا خواص این مجوز را در برگیرد. اجازه بدهید تا مجوزهای سفارشی شما با مجوزهای سیستمی ارتباط برقرار کنند. لذا این ارتباط باعث بروز افزایش امنیت خواهد شد.
android:permissionGroup=" android.permission-group.STORAGE"
تقریبا تمام اعمال کار با شبکه در Silverlight از مدل asynchronous programming پیروی میکنند؛ از فراخوانی یک متد وب سرویس تا دریافت اطلاعات از وب و غیره. اگر در سایر فناوریهای موجود در دات نت فریم ورک برای مثال جهت کار با یک وب سرویس هر دو متد همزمان و غیرهمزمان در اختیار برنامه نویس هستند اما اینجا خیر. اینجا فقط روشهای غیرهمزمان مرسوم هستند و بس. خیلی هم خوب. یک چارچوب کاری خوب باید روش استفادهی صحیح از کتابخانههای موجود را نیز ترویج کند و این مورد حداقل در Silverlight اتفاق افتاده است.
برای مثال فراخوانیهای زیر را در نظر بگیرید:
private int n1, n2;
private void FirstCall()
{
Service.GetRandomNumber(10, SecondCall);
}
private void SecondCall(int number)
{
n1 = number;
Service.GetRandomNumber(n1, ThirdCall);
}
private void ThirdCall(int number)
{
n2 = number;
// etc
}
private void FetchNumbers()
{
int n1 = Service.GetRandomNumber(10);
int n2 = Service.GetRandomNumber(n1);
}
private void FetchNumbers()
{
int n1, n2;
Service.GetRandomNumber(10, result =>
{
n1 = result;
Service.GetRandomNumber(n1, secondResult =>
{
n2 = secondResult;
});
});
}
به عبارتی میخواهیم کل اعمال انجام شده در متد FetchNumbers هنوز Async باشند (ترد اصلی برنامه را قفل نکنند) اما پی در پی انجام شوند تا مدیریت آنها سادهتر شوند (هر لحظه دقیقا بدانیم که کجا هستیم) و همچنین کدهای تولیدی نیز خواناتر باشند.
روش استانداری که توسط الگوهای برنامه نویسی برای حل این مساله پیشنهاد میشود، استفاده از الگوی coroutines است. توسط این الگو میتوان چندین متد Async را در حالت معلق قرار داده و سپس در هر زمانی که نیاز به آنها بود عملیات آنها را از سر گرفت.
دات نت فریم ورک حالت ویژهای از coroutines را توسط Iterators پشتیبانی میکند (از C# 2.0 به بعد) که در ابتدا نیاز است از دیدگاه این مساله مروری بر آنها داشته باشیم. مثال بعد یک enumerator را به همراه yield return ارائه داده است:
using System;
using System.Collections.Generic;
using System.Threading;
namespace CoroutinesSample
{
class Program
{
static void printAll()
{
foreach (int x in integerList())
{
Console.WriteLine(x);
}
}
static IEnumerable<int> integerList()
{
yield return 1;
Thread.Sleep(1000);
yield return 2;
yield return 3;
}
static void Main()
{
printAll();
}
}
}
کامپایلر سی شارپ در عمل یک state machine را برای پیاده سازی این عملیات به صورت خودکار تولید خواهد کرد:
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
Thread.Sleep(0x3e8);
this.<>2__current = 2;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;
case 3:
this.<>1__state = -1;
break;
}
return false;
}
در حین استفاده از یک IEnumerator ابتدا در وضعیت شیء Current آن قرار خواهیم داشت و تا زمانیکه متد MoveNext آن فراخوانی نشود هیچ اتفاق دیگری رخ نخواهد داد. هر بار که متد MoveNext این enumerator فرخوانی گردد (برای مثال توسط یک حلقهی foreach) اجرای متد integerList ادامه خواهد یافت تا به yield return بعدی برسیم (سایر اعمال تعریف شده در حالت تعلیق قرار دارند) و همینطور الی آخر.
از همین قابلیت جهت مدیریت اعمال Async پی در پی نیز میتوان استفاده کرد. State machine فوق تا پایان اولین عملیات تعریف شده صبر میکند تا به yield return برسد. سپس با فراخوانی متد MoveNext به عملیات بعدی رهنمون خواهیم شد. به این صورت دیدگاهی پی در پی از یک سلسه عملیات غیرهمزمان حاصل میگردد.
خوب ما الان نیاز به یک کلاس داریم که بتواند enumerator ایی از این دست را به صورت خودکار مرحله به مرحله آن هم پس از پایان واقعی عملیات Async قبلی (یا مرحلهی قبلی)، اجرا کند. قبل از اختراع چرخ باید متذکر شد که دیگران اینکار را انجام دادهاند و کتابخانههای رایگان و یا سورس بازی برای این منظور موجود است.
ادامه دارد ...