Earlier this week, Microsoft published the roadmap for .NET Core 2.1, ASP.NET Core 2.1 and EF Core 2.1, expected to be out in the first quarter of 2018. The team also talked about several new features with this new release. This release is more of a feedback-oriented release based on .NET Core 2.0 release. The.NET Core 2.0 is a huge success and already more than half a million developers are now using .NET Core 2.0. All thanks to .NET Standard 2.0 release . In this post find out about the new features of .NET Core 2.1.
سری آموزشی Blazor C# Tutorials
Blazor C# Tutorials
30 videos
In this playlist, I am going through all the fundamentals and sharing my journey to be a full stack Blazor developer. This is the future of web development in ASP.NET world. If you want to learn Blazor this is the best place to start.
1. Build Your First App - EP01
2. Getting Started - EP02
3. #Routing - EP03
4. Dependency #Injection - EP04
5. Forms & #Validations - EP05
6. JavaScript #Interop - EP06
7. #Razor #Components | Re-usability - EP07
8. Razor Components | #Lifecycle Methods - EP08
9. Razor Component #Libraries - EP09
10. Call #REST #API - #CRUD Methods - EP10
11. #Authentication | Out of the box- EP11
12. Custom AuthenticationStateProvider - EP12
13. Layouts | Login Pages - EP13
14. HttpClient | Login User
15. IHttpClientFactory | Login User
16. Sending JWT token & Request Middleware
17. Handling Exceptions
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fa-IR"); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("fa-IR");
[Required(ErrorMessageResourceName = "ResourceKeyName", ErrorMessageResourceType = typeof(<SolutionName>.Resources.<ResourceClassName>))]
public class LocalizationDisplayNameAttribute : DisplayNameAttribute { private readonly DisplayAttribute _display; public LocalizationDisplayNameAttribute(string resourceName, Type resourceType) { _display = new DisplayAttribute { ResourceType = resourceType, Name = resourceName }; } public override string DisplayName { get { try { return _display.GetName(); } catch (Exception) { return _display.Name; } } } }
public class LocalizationDisplayNameAttribute : DisplayNameAttribute { private readonly PropertyInfo nameProperty; public LocalizationDisplayNameAttribute(string displayNameKey, Type resourceType = null) : base(displayNameKey) { if (resourceType != null) nameProperty = resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public); } public override string DisplayName { get { if (nameProperty == null) base.DisplayName; return (string)nameProperty.GetValue(nameProperty.DeclaringType, null); } } }
[LocalizationDisplayName("ResourceKeyName", typeof(<SolutionName>.Resources.<ResourceClassName>))]
public class BaseController : Controller { private const string LanguageCookieName = "MyLanguageCookieName"; protected override void ExecuteCore() { var cookie = HttpContext.Request.Cookies[LanguageCookieName]; string lang; if (cookie != null) { lang = cookie.Value; } else { lang = ConfigurationManager.AppSettings["DefaultCulture"] ?? "fa-IR"; var httpCookie = new HttpCookie(LanguageCookieName, lang) { Expires = DateTime.Now.AddYears(1) }; HttpContext.Response.SetCookie(httpCookie); } Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang); base.ExecuteCore(); } }
public class LocalizationActionFilterAttribute : ActionFilterAttribute { private const string LanguageCookieName = "MyLanguageCookieName"; public override void OnActionExecuting(ActionExecutingContext filterContext) { var cookie = filterContext.HttpContext.Request.Cookies[LanguageCookieName]; string lang; if (cookie != null) { lang = cookie.Value; } else { lang = ConfigurationManager.AppSettings["DefaultCulture"] ?? "fa-IR"; var httpCookie = new HttpCookie(LanguageCookieName, lang) { Expires = DateTime.Now.AddYears(1) }; filterContext.HttpContext.Response.SetCookie(httpCookie); } Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang); base.OnActionExecuting(filterContext); } }
<select id="langs" onchange="languageChanged()"> <option value="fa-IR">فارسی</option> <option value="en-US">انگلیسی</option> </select> <script type="text/javascript"> function languageChanged() { setCookie("MyLanguageCookieName", $('#langs').val(), 365); window.location.reload(); } document.ready = function () { $('#langs').val(getCookie("MyLanguageCookieName")); }; function setCookie(name, value, exdays, path) { var exdate = new Date(); exdate.setDate(exdate.getDate() + exdays); var newValue = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString()) + ((path == null) ? "" : "; path=" + path) ; document.cookie = name + "=" + newValue; } function getCookie(name) { var i, x, y, cookies = document.cookie.split(";"); for (i = 0; i < cookies.length; i++) { x = cookies[i].substr(0, cookies[i].indexOf("=")); y = cookies[i].substr(cookies[i].indexOf("=") + 1); x = x.replace(/^\s+|\s+$/g, ""); if (x == name) { return unescape(y); } } } </script>
GET https://www.dntips.ir HTTP/1.1 ... Accept-Language: fa-IR,en-US;q=0.5 ...
Accept-Language: fa-IR,fa;q=0.8,en-US;q=0.5,ar-BH;q=0.3
<system.web> <globalization enableClientBasedCulture="true" uiCulture="auto" culture="auto"></globalization> </system.web>
var langs = filterContext.HttpContext.Request.UserLanguages;
routes.MapRoute( "Localization", // Route name "{lang}/{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults );
public class BaseController : Controller { protected override void ExecuteCore() { var lang = RouteData.Values["lang"]; if (lang != null && !string.IsNullOrWhiteSpace(lang.ToString())) { Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang.ToString()); } base.ExecuteCore(); } }
public class BaseController : Controller { protected override void OnActionExecuted(ActionExecutedContext context) { var view = context.Result as ViewResultBase; if (view == null) return; // not a view var viewName = view.ViewName; view.ViewName = GetGlobalizationViewName(viewName, context); base.OnActionExecuted(context); } private static string GetGlobalizationViewName(string viewName, ControllerContext context) { var cultureName = Thread.CurrentThread.CurrentUICulture.Name; if (cultureName == "en-US") return viewName; // default culture if (string.IsNullOrEmpty(viewName)) return context.RouteData.Values["action"] + "." + cultureName; // "Index.fa" int i; if ((i = viewName.IndexOf('.')) > 0) // ex: Index.cshtml return viewName.Substring(0, i + 1) + cultureName + viewName.Substring(i); // "Index.fa.cshtml" return viewName + "." + cultureName; // "Index" ==> "Index.fa" } }
public sealed class RazorGlobalizationViewEngine : RazorViewEngine { protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return base.CreatePartialView(controllerContext, GetGlobalizationViewPath(controllerContext, partialPath)); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return base.CreateView(controllerContext, GetGlobalizationViewPath(controllerContext, viewPath), masterPath); } private static string GetGlobalizationViewPath(ControllerContext controllerContext, string viewPath) { //var controllerName = controllerContext.RouteData.GetRequiredString("controller"); var request = controllerContext.HttpContext.Request; var lang = request.Cookies["MyLanguageCookie"]; if (lang != null && !string.IsNullOrEmpty(lang.Value) && lang.Value != "en-US") { var localizedViewPath = Regex.Replace(viewPath, "^~/Views/", string.Format("~/Views/Globalization/{0}/", lang.Value)); if (File.Exists(request.MapPath(localizedViewPath))) viewPath = localizedViewPath; } return viewPath; }
protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new RazorGlobalizationViewEngine()); }
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema ... --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> ... </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="RightToLeft" xml:space="preserve"> <value>false</value> <comment>RightToleft is false in English!</comment> </data> </root>
مثال 1: تعداد روزهای هر ماه سال 2012 را محاسبه کنید.
ستونهای این گزارش باید از سه مقدار عددی Year, Month, DaysInMonth تشکیل شوند.
var items = context.Bookings .Where(booking => booking.StartTime.Year == 2012) .Select(booking => new { booking.StartTime.Year, booking.StartTime.Month, DaysInMonth = EF.Functions.DateDiffDay( booking.StartTime.Date.AddDays(1 - booking.StartTime.Date.Day), booking.StartTime.Date.AddDays(1 - booking.StartTime.Date.Day).AddMonths(1) ) }) .Distinct() .OrderBy(r => r.Year) .ThenBy(r => r.Month) .ToList();
سپس این نکات در مورد کار با تاریخ و زمان در اینجا قابل مشاهده هستند:
1) خاصیت StartTime.Year به DATEPART(year, [b].[StartTime]) ترجمه میشود.
2) خاصیت StartTime.Month به DATEPART(month, [b].[StartTime]) ترجمه میشود.
3) برای یافتن عدد اختلاف تعداد روز بین دو تاریخ، میتوان از تابع کمکی استاندارد EF.Functions.DateDiffDay استفاده کرد که در نهایت به DATEDIFF ترجمه خواهد شد.
4) اگر میخواهید از قسمت زمان یک تاریخ صرفنظر کنید، از خاصیت Date آن مانند StartTime.Date استفاده کنید که به CONVERT(date, [b].[StartTime]) ترجمه میشود.
5) امکان استفادهی از متدهای استانداردی مانند AddDays و AddMonths در کوئرهای LINQ to Entities وجود دارد که به تابع DATEADD ترجمه میشوند.
مثال 2: لیست زمان شروع و پایان آخرین 10 مورد از رزروها را تهیه کنید.
var items = context.Bookings .Select(x => new { x.StartTime, EndTime = x.StartTime.AddMinutes(x.Slots * 30) }) .OrderByDescending(x => x.EndTime) .ThenByDescending(x => x.StartTime) .Take(10) .ToList();
مثال 3: لیست تعداد رزروهای هر ماه موجود را تهیه کنید.
var items = context.Bookings .GroupBy(x => new { x.StartTime.Year, x.StartTime.Month }) .Select(x => new { x.Key.Year, x.Key.Month, Count = x.Count() }) .OrderBy(x => x.Year) .ThenBy(x => x.Month) .ToList();
مثال 4: در هر ماه، چند درصد از امکانات موجود مورد استفاده قرار گرفتهاند؟
زمان شروع به کار، 8 صبح و زمان خاتمهی کار 8:30 شب است. بنابراین یک روز کاری از 25 slot نیم ساعته تشکیل میشود. هر ماه را هم میتوانید کامل درنظر بگیرید و مهم نیست که در این بین تعطیلی وجود دارد. بنابراین فرمول محاسبهی درصد استفادهی از امکانات موجود به صورت زیر است که نیاز است نتیجهی حاصل نیز round شود:
Round(100 * Sum(Slots) / (decimal)(25 * DaysInMonth), 1)
var items = context.Bookings .Select(booking => new { booking.Facility.Name, booking.StartTime.Year, booking.StartTime.Month, booking.Slots, DaysInMonth = EF.Functions.DateDiffDay( booking.StartTime.Date.AddDays(1 - booking.StartTime.Date.Day), booking.StartTime.Date.AddDays(1 - booking.StartTime.Date.Day).AddMonths(1) ) }) .GroupBy(b => new { b.Name, b.Year, b.Month, b.DaysInMonth }) .Select(g => new { g.Key.Name, g.Key.Year, g.Key.Month, Utilization = SqlDbFunctionsExtensions.SqlRound( 100 * g.Sum(b => b.Slots) / (decimal)(25 * g.Key.DaysInMonth), 1) }) .OrderBy(r => r.Name) .ThenBy(r => r.Year) .ThenBy(r => r.Month) .ToList();
در این کوئری، از متد SqlDbFunctionsExtensions.SqlRound نیز استفاده شدهاست. روش تعریف این نوع متدها را در مطلب «امکان تعریف توابع خاص بانکهای اطلاعاتی در EF Core» پیشتر بررسی کردهایم که برای مثال در اینجا چنین شکلی را پیدا میکند:
namespace EFCorePgExercises.Utils { public static class SqlDbFunctionsExtensions { public static decimal SqlRound(decimal value, int precision) => throw new InvalidOperationException($"{nameof(SqlRound)} method cannot be called from the client side."); private static readonly MethodInfo _sqlRoundMethodInfo = typeof(SqlDbFunctionsExtensions) .GetRuntimeMethod( nameof(SqlDbFunctionsExtensions.SqlRound), new[] { typeof(decimal), typeof(int) } ); public static void AddCustomSqlFunctions(this ModelBuilder modelBuilder) { modelBuilder.HasDbFunction(_sqlRoundMethodInfo) .HasTranslation(args => { return SqlFunctionExpression.Create("ROUND", args, _sqlRoundMethodInfo.ReturnType, typeMapping: null); }); } } }
namespace EFCorePgExercises.DataLayer { public class ApplicationDbContext : DbContext { // ... protected override void OnModelCreating(ModelBuilder modelBuilder) { // ... modelBuilder.AddCustomSqlFunctions(); // ... } } }
کدهای کامل این قسمت را در اینجا میتوانید مشاهده کنید.
تدارک ساختار ابتدایی این مطلب
در اینجا اینترفیسی را که بیانگر ساختار شیء شخص است، به صورت ذیل ایجاد میکنیم:
export interface Person { name: string; age: number; }
export class LinqTestsComponent { people: Person[] = [ { name: "User 4", age: 27 }, { name: "User 5", age: 42 }, { name: "User 6", age: 8 }, { name: "User 1", age: 20 }, { name: "User 2", age: 35 }, { name: "User 3", age: 78 } ]; }
همچنین سه متد ذیل را نیز برای لاگ کردن عنوان آزمایش، نمایش محتوای آرایهی اصلی و نمایش نتیجهی آزمایش، به این کلاس اضافه میکنیم:
logTitle(title: string) { console.log(`%c${title}`, "background: #222; color: #bada55"); } logOriginalArray() { console.log(`original this.people:${JSON.stringify(this.people)}`); } logResult(message: string, result: any) { console.log(`${message}:${JSON.stringify(result)}`); }
معادل متد Where در TypeScript
متد filter که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Where، جهت فیلتر کردن عناصر بر اساس یک خاصیت، یا چندین خاصیت باشد:
equivalentToWhere() { const youngerThan40 = this.people.filter(person => person.age < 40); // ECMAScript 5 this.logResult("People younger than 40", youngerThan40); // Filtering on Multiple Criteria const youngsters = this.people.filter( person => person.age < 40 && person.name.toLocaleLowerCase().indexOf("user") !== -1); this.logResult("Users younger than 40", youngsters); }
People younger than 40:[ {"name":"User 4","age":27}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35} ] Users younger than 40:[ {"name":"User 4","age":27}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35} ]
معادل متد Any در TypeScript
متد some که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Any باشد. اگر یکی از عناصر آرایه، بر اساس شرط تعیین شده یافت شود، این متد مقدار true را باز میگرداند:
equivalentToAny() { const anyUnder40 = this.people.some(person => person.age < 40); // ECMAScript 5 this.logResult("Are any people under 40?", anyUnder40); // true // Filtering on Criteria Matching any Object Properties const filterBy = "user"; const anyUsers = this.people.filter(person => Object.keys(person).some(property => { let value = (<any>person)[property]; if (typeof value === "string") { value = value.toLocaleLowerCase(); } return value.toString().indexOf(filterBy) !== -1; }) ); this.logResult("anyUsers", anyUsers); }
Are any people under 40?:true anyUsers:[ {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78} ]
در مثال دوم، جستجویی بر روی تمام خواص شیء شخص انجام شدهاست. در اینجا توسط متد Object.keys، لیست خواص شیء یافت شدهاست. سپس بر روی این لیست توسط متد some، بررسی شدهاست که آیا خاصیت رشتهای وجود دارد که مساوی عبارت filterBy باشد؟ حاصل این بررسی به عنوان شرط متد filter جهت بازگشت آرایهی متناظری از اشخاص یافت شده، استفاده شدهاست.
معادل متد Contains در TypeScript
متد includes که جزو متدهای توکار ES7 است، میتواند معادلی برای متد Contains باشد و کار آن بررسی وجود عنصری در یک لیست است:
equivalentToContains() { const searchElement: Person = { name: "User 4", age: 27 }; const containsUser4 = this.people.includes(searchElement); // ECMAScript 2016 = ECMAScript 7 this.logResult("Contains searchElement", containsUser4); // false -> only compares by reference and not by value. const indexOfUser4 = this.people.indexOf(searchElement); // ECMAScript 5 this.logResult("indexOfUser4", indexOfUser4); // -1 -> only compares by reference and not by value. const stringifiedObj = JSON.stringify(searchElement); const includesUser4 = this.people.some(person => JSON.stringify(person) === stringifiedObj); this.logResult("includesUser4", includesUser4); // true -> compares by by value. }
یکی از روشهای مقایسهی بر اساس تمام مقادیر خواص یک شیء، استفاده از متد JSON.stringify است که اگر آنرا با متد some ترکیب کنیم، میتوان به نتیجهی مطلوبی رسید:
Contains searchElement:false indexOfUser4:-1 includesUser4:true
معادل متد All در TypeScript
متد every که جزو متدهای توکار ES5 است، میتواند معادلی برای متد All باشد و کار آن بررسی صحت شرط اعمالی، بر روی تک تک عناصر لیست است. اگر این بررسی با موفقیت صورت گرفت، مقدار true را بازگشت میدهد:
equivalentToAll() { const allUnder30 = this.people.every(person => person.age < 30); // ECMAScript 5 this.logResult("Are all people under 30?", allUnder30); // false }
Are all people under 30?:false
معادل متدهای First و FirstOrDefault در TypeScript
میتوان از متدهای filter و یا find بومی ES5 و ES 6 برای شبیه سازی متدهای First (یافتن اولین عنصر درخواستی در یک لیست) و FirstOrDefault استفاده کرد:
equivalentToFirstOrDefault() { const vahidOrDefault = this.people.filter(item => item.name === "Vahid")[0] || null; // ECMAScript 5 this.logResult("vahidOrDefault", vahidOrDefault); const user1OrDefault = this.people.find(item => item.name === "User 1") || null; // ECMAScript 2015 = ECMAScript 6 this.logResult("user1OrDefault", user1OrDefault); }
معادل متد FindIndex در TypeScript
متد findIndex که جزو متدهای توکار ES6 است، میتواند معادلی برای متد FindIndex در جهت یافتن ایندکس عنصری در یک لیست، بر اساس شرط درخواستی، باشد.
equivalentToFindIndex() { const index = this.people.findIndex(person => person.age === 8); // ECMAScript 2015 = ECMAScript 6 this.logResult("index of the user with age 8", index) }
index of the user with age 8:2
معادل متد Select در TypeScript
متد map که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Select، برای تغییر شکل نهایی خروجی یک لیست باشد:
equivalentToSelect() { const names = this.people.map(person => person.name); // ECMAScript 5 this.logResult("Selected the names of people", names); }
Selected the names of people:["User 4","User 5","User 6","User 1","User 2","User 3"]
معادل متد Aggregate در TypeScript
متد reduce که جزو متدهای توکار ES5 است، میتواند شبیه به متد Aggregate عمل کند و لیستی از عناصر را به یک مقدار کاهش دهد:
equivalentToAggregate() { // ECMAScript 5 const aggregate = this.people.reduce((person1, person2) => { return { name: "", age: person1.age + person2.age }; }); this.logResult("Aggregate age", aggregate.age); // { age: 210 } }
Aggregate age:210
معادل متد ForEach در TypeScript
متد forEach که جزو متدهای توکار ES5 است، میتواند معادلی برای متد ForEach باشد که روشی functional برای پیمودن عناصر یک لیست است:
equivalentToForEach() { // ECMAScript 5 this.people.forEach(person => { this.logResult("person", person); }); }
person:{"name":"User 4","age":27} person:{"name":"User 5","age":42} person:{"name":"User 6","age":8} person:{"name":"User 1","age":20} person:{"name":"User 2","age":35} person:{"name":"User 3","age":78}
معادل متد OrderBy در TypeScript
متد sort که جزو متدهای توکار ES5 است، میتواند معادلی برای متد OrderBy باشد که جهت مرتب سازی عناصر یک لیست از آن استفاده میشود:
// ECMAScript 5 let orderedByAgeAscending = this.people.sort((person1, person2) => { const a = person1.age; const b = person2.age; return a > b ? 1 : -1; }); this.logResult("Ordered by age ascending", orderedByAgeAscending);
- مساوی صفر باشد، تغییری را به وجود نمیآورد.
- کمتر از صفر باشد، اولین عنصر را قبل از دومین عنصر قرار میدهد.
- بیشتر از صفر باشد، دومین عنصر را پس از اولین عنصر قرار میدهد.
منطق مقایسهی فوق را به صورت ذیل نیز میتوان خلاصه کرد:
orderedByAgeAscending = this.people.sort((person1, person2) => person1.age - person2.age); this.logResult("Ordered by age ascending", orderedByAgeAscending);
Ordered by age ascending:[ {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 4","age":27}, {"name":"User 2","age":35}, {"name":"User 5","age":42}, {"name":"User 3","age":78} ]
const orderedByName = this.people.sort((person1, person2) => { // name1.localeCompare(name2) // is case insensitive // or use toUpperCase() to ignore character casing const name1 = person1.name.toUpperCase(); const name2 = person2.name.toUpperCase(); return name1 > name2 ? 1 : -1; }) this.logResult("Ordered by name", orderedByName); this.logOriginalArray();
Ordered by name:[ {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ] original this.people:[ {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ]
امکان ترکیب زنجیروار متدهای کار بر روی لیستها در TypeScript
همانند LINQ، در اینجا نیز میتوان متدهای فوق را به صورت زنجیروار بر روی یک لیست فراخوانی و اجرا کرد:
chainFunctionCalls() { const namesOfPeopleOver30OrderedDesc = this.people .filter(person => person.age > 30) .map(person => person.name) .sort((name1, name2) => { // name1.localeCompare(name2) // is case insensitive // or use toUpperCase() to ignore character casing name1 = name1.toUpperCase(); name2 = name2.toUpperCase(); return name2 > name1 ? 1 : -1; }); this.logResult("the names of all people over 30 ordered by name descending", namesOfPeopleOver30OrderedDesc); }
the names of all people over 30 ordered by name descending:["User 5","User 3","User 2"]
معادل متد Skip در TypeScript
متد splice که جزو متدهای توکار ES5 است، میتواند شبیه به متد Skip عمل کند. این متد آرایهای را بازگشت میدهد که حاوی عناصری است که پس از تعداد ذکر شده، در آرایهی اصلی وجود دارند:
equivalentToSkip() { const skip2 = this.people.splice(2); // ECMAScript 5 this.logResult("skip2 -> the deleted elements", skip2); this.logOriginalArray(); }
skip2 -> the deleted elements:[ {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ] original this.people:[ {"name":"User 1","age":20}, {"name":"User 2","age":35} ]
تغییرات مهم ASP.NET 5 و MVC 6
(JSON (JavaScript Object Notation یک راه مناسب برای نگهداری اطلاعات است و از لحاظ ساختاری شباهت زیادی به XML، رقیب قدیمی خود دارد.
وب سرویس و آجاکس برای انتقال اطلاعات از این روش استفاده میکنند و بعضی از پایگاههای داده مانند RavenDB بر مبنای این تکنولوژی پایه گذاری شده اند.
هیچ چیزی نمیتواند مثل یک مثال؛ خوانایی ، سادگی و کم حجم بودن این روش را نشان دهد :
اگر یک شئ با ساختار زیر در سی شارپ داشته باشید :
class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
ساختار JSON متناظر با آن ( در صورت این که مقدار دهی شده باشد ) به صورت زیر است:
{ "Id":1, "FirstName":"John", "LastName":"Doe" }
و در یک مثال پیچیدهتر :
class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Car Car { get; set; } public IEnumerable<Location> Locations { get; set; } } class Location { public int Id { get; set; } public string Address { get; set; } public int Zip { get; set; } } class Car { public int Id { get; set; } public string Model { get; set; } }
{ "Id":1, "FirstName":"John", "LastName":"Doe", "Car": { "Id":1, "Model":"Nissan GT-R" }, "Locations":[ { "Id":1, "Address":"30 Mortensen Avenue, Salinas", "Zip":93905 }, { "Id":2, "Address":"65 West Alisal Street, #210, Salinas", "Zip":95812 } ] }
ساختار JSON را مجموعه ای از ( نام - مقدار ) تشکیل میدهد. ساختار مشابه آن در زبان سی شارپ KeyValuePair است.
مشاهده این تصاویر، بهترین درک را از ساختار JSON به شما میدهد.
Json.net یکی از بهترین کتابخانه هایی است که برای کار با این تکنولوژی در net. ارائه شده است. بهترین روش اضافه نمودن آن به پروژه NuGet است.برای این کار دستور زیر را در Package Manager Console وارد کنید.
PM> Install-Package Newtonsoft.Json
با استفاده از کد زیر میتوانید یک Object را به فرمت JSON تبدیل کنید.
var customer = new Customer { Id = 1, FirstName = "John", LastName = "Doe", Car = new Car { Id = 1, Model = "Nissan GT-R" }, Locations = new[] { new Location { Id = 1, Address = "30 Mortensen Avenue, Salinas", Zip = 93905 }, new Location { Id = 2, Address = "65 West Alisal Street, #210, Salinas", Zip = 95812 }, } };
var data = Newtonsoft.Json.JsonConvert.SerializeObject(customer);
خروجی تابع SerializeObject رشته ای است که محتوی آن را در چهارمین بلاک کد که در بالاتر آمده است، میتوانید مشاهده کنید.
برای Deserialize کردن (Cast اطلاعات با فرمت JSON به کلاس موردنظر) از روش زیر بهره میگیریم :
var customer = Newtonsoft.Json.JsonConvert.DeserializeObject<Customer>(data);
آشنایی با این تکنولوژی، پیش درآمدی برای چشیدن طعم NoSQL و معرفی کارآمدترین روشهای آن است که در آینده خواهیم آموخت...
خوشحال میشوم اگر نظرات شما را در باره این موضوع بدانم.
در این مقاله مروری سریع و کاربردی خواهیم داشت بر تواناییهای مقدماتی LINQ to XML .
فایل Employee.XML را با محتویات زیر در نظر بگیرید:
<Employees>
<Employee>
<Name>Vahid</Name>
<Phone>11111111</Phone>
<Department>IT</Department>
<Age>52</Age>
</Employee>
<Employee>
<Name>Farid</Name>
<Phone>124578963</Phone>
<Department>Civil</Department>
<Age>35</Age>
</Employee>
<Employee>
<Name>Mehdi</Name>
<Phone>1245788754</Phone>
<Department>HR</Department>
<Age>30</Age>
</Employee>
</Employees>
1- چگونه یک فایل XML را جهت استفاده توسط LINQ بارگذاری کنیم؟
قبل از شروع، اسمبلی System.Xml.Linq باید به ارجاعات برنامه اضافه شود. سپس:
using System.Xml.Linq;
XDocument xDoc = XDocument.Load("Employee.xml");
2- اگر محتویات XML دریافتی به صورت رشته بود (مثلا از یک دیتابیس دریافت شد)، اکنون چگونه باید آنرا بارگذاری کرد؟
اینکار را با استفاده از یک StringReader به صورت زیر میتوان انجام داد:
// loading XML from string
StringReader sr = new StringReader(stringXML);
XDocument xDoc = XDocument.Load(sr);
3- چگونه یک کوئری ساده شامل تمامی رکوردهای Employee مجموعه Employees را تهیه کنیم؟
using System.Collections;
IEnumerable<XElement> empList = from e in xDoc.Root.Elements("Employee") select e;
اکنون که مجموعه کارکنان توسط متغیر empList در اختیار ما است، دسترسی به محتویات آن به سادگی زیر خواهد بود:
foreach (XElement employee in empList)
{
foreach (XElement e in employee.Elements())
{
Console.WriteLine(e.Name + " = " + e.Value);
}
}
4- کوئری بنویسید که اطلاعات تمامی کارکنان بخش HR را باز گرداند.
IEnumerable<XElement> hrList = from e in xDoc.Root.Elements("Employee")
where e.Element("Department").Value == "HR"
select e;
همانطور که ملاحظه میکنید همانند عبارات SQL ، در تمامی عناصر متعلق به کارکنان، عناصری که دپارتمان آنها مساوی HR است بازگشت داده میشود.
5- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را ارائه دهد.
IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
select e;
چون حاصل e.Element("Age").Value یک رشته است، برای اعمال فیلترهای عددی باید این رشتهها تبدیل به عدد شوند. به همین جهت از int.Parse استفاده شده است.
6- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را مرتب شده بر اساس نام باز گرداند.
IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
orderby e.Element("Name").Value
select e;
7- تبدیل نتیجهی یک کوئری LINQ به لیستی از اشیاء
مفهومی به سی شارپ 3 اضافه شده است به نام anonymous types . برای مثال:
توسط این قابلیت میتوان یک شیء را بدون نیاز به تعریف ابتدایی آن ایجاد کرد و حتی از intelliSense موجود در IDE نیز بهره مند شد. این نوعهای ناشناس توسط واژههای کلیدی new و var تولید میشوند. کامپایلر به صورت خودکار برای هر anonymous type یک کلاس ایجاد میکند.
دقیقا از همین توانایی در LINQ نیز میتوان استفاده نمود:
var empList = from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
foreach (var employee in empList)
{
Console.WriteLine("Name = " + employee.Name);
Console.WriteLine("Dep = " + employee.Department);
Console.WriteLine("Phone = " + employee.Phone);
Console.WriteLine("Age = " + employee.Age);
}
public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
برای مثال اگر بخواهیم لیست دریافتی را به صورت یک لیست جنریک بازگشت دهیم خواهیم داشت:
public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
List<Employee> Get()
{
XDocument xDoc = XDocument.Load("Employee.xml");
var items =
from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new Employee
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
return items.ToList();
}
وضعیت توسعهی برنامههای وب، پیش از ارائهی Blazor
عموما برای توسعهی برنامههای وب، در سمت سرور آنها از زبانهایی مانند C#، Java و Python و امثال آنها استفاده میشود؛ اما این وضعیت در سمت کلاینت فرق میکند. در سمت کلاینت، عموما از فریمورکها و کتابخانههای جاوا اسکریپتی مانند Angular ،React ،Vue.js ،jQuery و غیره استفاده میشود.
همانطور که مشاهده میکنید، فراگیری و اجرای این دو گروه متفاوت از زبانها، مشکل و وقتگیر است. بنابراین چقدر خوب میشد اگر امکان تهیهی هر دو قسمت برنامههای وب، تنها با یک زبان میسر میشد. با استفاده از Blazor، این آرزو میسر شدهاست.
با استفاده از Blazor میتوان کدهای تعاملی UI را بجای استفاده از زبان جاوا اسکریپت، با کمک زبان #C تهیه کرد. به این ترتیب با استفاده از یک زبان میتوان کدهای سمت سرور و سمت کلاینت را پیاده سازی کرد. البته شاید این سؤال مطرح شود که مرورگرها تنها قادر به درک کدهای HTML و جاوا اسکریپت هستند و نه #C، بنابراین چگونه میتوان از زبان #C در مرورگرها نیز استفاده کرد؟ پاسخ به آن، به فناوری جدید «وب اسمبلی» بر میگردد. Blazor با استفاده از «وب اسمبلی» است که میتواند کدهای #C را درون مرورگر اجرا کند.
حالتهای مختلف هاست و ارائهی برنامههای مبتنی بر Blazor
برنامههای مبتنی بر Blazor، به دو روش مختلف قابل ارائه هستند:
الف) Blazor Server
Blazor Server، در اساس یک برنامهی استاندارد ASP.NET Core است که در آن تمام قابلیتهای سمت سرور، مانند کار با EF-Core، میسر است و امکان دسترسی به این امکانات به صورت یکپارچهای در سراسر برنامه وجود دارد. در این حالت، کامپوننتهای Blazor، بجای اجرای بر روی مرورگر کاربر، در سمت سرور اجرا میشوند. این تعاملات و به روز رسانیهای UI، توسط یک اتصال دائم SignalR مدیریت میشوند.
همانطور که مشاهده میکنید، در حالت هاست سمت سرور، همه چیز، منجمله کامپوننتهای Blazor، در همان سمت سرور قرار دارند و این اتصال پشت صحنهی SignalR است که کار تبادل اطلاعات ارسالی و رندر شده را بر عهده میگیرد.
ب) Blazor web assembly
در این حالت با استفاده از فناوری جدید «وب اسمبلی»، تمام کدهای یک برنامهی مبتنی بر Blazor به کمک NET Runtime.، داخل مرورگر اجرا میشود. به Blazor web assembly باید همانند فریمورکهای SPA (تک صفحهای وب)، مانند Angular و React نگاه کرد؛ با یک تفاوت مهم: در اینجا بجای استفاده از جاو اسکریپت برای نوشتن برنامهی SPA، از #C استفاده میشود. اگر به تصویر فوق دقت کنید، در حالت اجرای برنامههای Blazor web assembly، تنها به مرورگر کاربر نیاز است و همه چیز داخل آن قرار میگیرد. در اینجا دیگر خبری از یک اتصال دائم SignalR با سرور وجود ندارد.
البته باید دقت داشت که از فناوری وب اسمبلی، در تمام مرورگرهای جدید پشتیبانی میشود؛ منهای IE 11. در این حالت مرورگر کل برنامهی Blazor را دریافت میکند (همانند دریافت کل کدهای یک برنامهی Angular و یا React) و بدون استفاده از رندر سمت سرور حالت الف، قابلیت تعامل با کاربر را دارد.
بدیهی است با توجه به اینکه Blazor web assembly مستقیما داخل مرورگر اجرا میشود، دیگر همانند حالت الف، امکان دسترسی مستقیم به فناوریها و امکانات سمت سرور، مانند کار مستقیم با EF-Core را نخواهد داشت. برای این منظور دقیقا همانند روش کار با سایر فریم ورکهای SPA، نیاز به تهیهی یک ASP.NET Core Web API جهت تعامل با سرور خواهد بود.
مزایا و معایب حالتهای مختلف هاست برنامههای Blazor
الف) Blazor Server
مزایا:
- حجم دریافتی توسط مرورگر در این حالت بسیار کم است.
- امکان دسترسی به تمام امکانات سمت سرور را دارد؛ مانند تمام کتابخانههای سمت سرور و همچنین امکان دیباگ آن نیز همانند سایر برنامههای سمت سرور است.
- بر روی مرورگرهای قدیمی نیز قابل اجرا است؛ چون بدون نیاز به فناوری جدید «وب اسمبلی» کار میکند.
معایب:
- رندر شدن UI آن نسبت به حالت ب، کندتر است. از این جهت که تمام تعاملات UI آن، توسط اتصال SignalR به سمت سرور ارسال شده و سپس نتیجهی نهایی رندر شده، به سمت کلاینت بازگشت داده میشود.
- پشتیبانی از اجرای offline آن وجود ندارد. اگر اتصال SignalR موجود قطع شود، دیگر نمیتوان از برنامه استفاده کرد.
- با توجه به نیاز به استفادهی از یک اتصال دائم SignalR به ازای هر کاربر، مقیاس پذیری این نوع برنامهها کمتر است. البته اگر تعداد کاربران برنامههای شما در یک شبکهی اینترانت داخلی شرکتی محدود است، این مورد مشکل خاصی نخواهد بود. از دیدگاهی دیگر اگر تعداد کاربران برنامهی شما بسیار زیاد است، استفاده از Blazor Server توصیه نمیشود. البته باید دقت داشت که سروری با 4GB RAM، میتواند 5000 کاربر همزمان SignalR را مدیریت کند.
ب) Blazor web assembly یا به اختصار Blazor WASM
مزایا:
- هیچ نوع وابستگی به سمت سرور ندارد. همینقدر که برنامه توسط مرورگر دریافت شد، قابل اجر است.
- برای هاست آن الزاما نیازی به یک سرور IIS و یا یک وب سرور ASP.NET Core نیست.
- امکان ارائهی آن توسط یک CDN نیز وجود دارد.
- چون در این حالت کل برنامه توسط مرورگر دریافت میشود، قابلیت اجرای آفلاین را نیز پیدا میکند.
- برای کار، نیازی به اتصال دائم SignalR را ندارد؛ به همین جهت مقیاس پذیری آن بیشتر است.
معایب:
- حتما نیاز به استفادهی از مرورگرهای جدید با پشتیبانی از web assembly را دارد؛ برای مثال نیاز به کروم نگارش 57 به بعد و فایرفاکس نگارش 52 به بعد را دارد و بر روی IE اجرا نمیشود.
- چون کل برنامه در این حالت توسط مرورگر دریافت میشود، حجم ابتدایی دریافت آن کمی بالا است.
- میدان دید و عملکرد آن همانند سایر برنامههای SPA، محدود است به امکاناتی که مرورگر، در اختیار برنامه قرار میدهد.
ایجاد پروژههای خالی Blazor Server و Blazor web assembly
یا میتوانید از ویژوال استودیوی کامل و منوی افزودن پروژهی آن برای اینکار استفاده کنید و یا اگر به خروجی دستور dotnet new --list مراجعه کنیم، SDK دات نت 5، به همراه دو قالب مرتبط زیر نیز هست:
C:\Users\Vahid>dotnet new --list Templates Short Name Language Tags -------------------------------------------- ------------------- ------------ ---------------------- Blazor Server App blazorserver [C#] Web/Blazor Blazor WebAssembly App blazorwasm [C#] Web/Blazor/WebAssembly
در قسمت بعد، این دو پروژهی خالی فوق را ایجاد کرده و ساختار آنها را بررسی میکنیم. همچنین نکاتی را هم که در این قسمت در مورد نحوهی هاست این برنامهها عنوان شد، بر روی این پروژهها مشاهده خواهیم کرد.