Tuples with C# 7.0
Deconstruction In C# 7.0
C#7: Tuples
C#7 - Taking a second look at Tuples
Tackling Tuples: Understanding the New C# 7 Value Type
Tuples and Generics in C#7
C# Tuples. How tuples are related to ValueTuple
C# Tuples. Why mutable structs
C# 7.0 : Tuples to the Extreme
پیش از ارائه نهایی مطلب، تمام کدهای آن با VS 2017 RTM آزمایش و بررسی شوند.
Bootstrap 4.5.1 منتشر شد
The 18.6 release is the second major release of SSMS in 2020 and packs several high impact changes , including a fix for crashes in database diagram s. Key fixes include:
- Save to XEL file error fix.
- B acpac file import error fix.
- Database diagrams crash fix.
- Addressed sources o f three common application hangs.
دات نت 4 و کلاس Lazy
Addison-Wesley Essential C# 4.0 (978 Pages)
Wrox Professional C# 4 and .NET 4 (1852 Pages)
OReilly C# 4.0 in a Nutshell 4th Edition Feb 2010 (1056 Pages)
Sams C# 4.0 How To Feb 2010 (669 Pges)
Effective C# 50 Specific Ways to Improve Your C# Second Edition (342 Pges)
عدم سازگاری با EF
با سلام و احترام
ممنون از پاسخ شما
این تست کرده بودم میتونید خالی نبودن متغیر فوق ببینید اینم فیلدهای اصلی این کلاس
#region Properties /// <summary> /// Gets or sets the product variant identifier /// </summary> public int ProductVariantId { get; set; } /// <summary> /// Gets or sets the product identifier /// </summary> public int ProductId { get; set; } /// <summary> /// Gets or sets the name /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the SKU /// </summary> public string SKU { get; set; } /// <summary> /// Gets or sets the description /// </summary> public string Description { get; set; } /// <summary> /// Gets or sets the admin comment /// </summary> public string AdminComment { get; set; } /// <summary> /// Gets or sets the manufacturer part number /// </summary> public string ManufacturerPartNumber { get; set; } /// <summary> /// Gets or sets a value indicating whether the product variant is gift card /// </summary> public bool IsGiftCard { get; set; } /// <summary> /// Gets or sets the gift card type /// </summary> public int GiftCardType { get; set; } /// <summary> /// Gets or sets a value indicating whether the product variant is download /// </summary> public bool IsDownload { get; set; } /// <summary> /// Gets or sets the download identifier /// </summary> public int DownloadId { get; set; } /// <summary> /// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times /// </summary> public bool UnlimitedDownloads { get; set; } /// <summary> /// Gets or sets the maximum number of downloads /// </summary> public int MaxNumberOfDownloads { get; set; } /// <summary> /// Gets or sets the number of days during customers keeps access to the file. /// </summary> public int? DownloadExpirationDays { get; set; } /// <summary> /// Gets or sets the download activation type /// </summary> public int DownloadActivationType { get; set; } /// <summary> /// Gets or sets a value indicating whether the product variant has a sample download file /// </summary> public bool HasSampleDownload { get; set; } /// <summary> /// Gets or sets the sample download identifier /// </summary> public int SampleDownloadId { get; set; } /// <summary> /// Gets or sets a value indicating whether the product has user agreement /// </summary> public bool HasUserAgreement { get; set; } /// <summary> /// Gets or sets the text of license agreement /// </summary> public string UserAgreementText { get; set; } /// <summary> /// Gets or sets a value indicating whether the product variant is recurring /// </summary> public bool IsRecurring { get; set; } /// <summary> /// Gets or sets the cycle length /// </summary> public int CycleLength { get; set; } /// <summary> /// Gets or sets the cycle period /// </summary> public int CyclePeriod { get; set; } /// <summary> /// Gets or sets the total cycles /// </summary> public int TotalCycles { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity is ship enabled /// </summary> public bool IsShipEnabled { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity is free shipping /// </summary> public bool IsFreeShipping { get; set; } /// <summary> /// Gets or sets the additional shipping charge /// </summary> public decimal AdditionalShippingCharge { get; set; } /// <summary> /// Gets or sets a value indicating whether the product variant is marked as tax exempt /// </summary> public bool IsTaxExempt { get; set; } /// <summary> /// Gets or sets the tax category identifier /// </summary> public int TaxCategoryId { get; set; } /// <summary> /// Gets or sets a value indicating how to manage inventory /// </summary> public int ManageInventory { get; set; } /// <summary> /// Gets or sets the stock quantity /// </summary> public int StockQuantity { get; set; } /// <summary> /// Gets or sets a value indicating whether to display stock availability /// </summary> public bool DisplayStockAvailability { get; set; } /// <summary> /// Gets or sets a value indicating whether to display stock quantity /// </summary> public bool DisplayStockQuantity { get; set; } /// <summary> /// Gets or sets the minimum stock quantity /// </summary> public int MinStockQuantity { get; set; } /// <summary> /// Gets or sets the low stock activity identifier /// </summary> public int LowStockActivityId { get; set; } /// <summary> /// Gets or sets the quantity when admin should be notified /// </summary> public int NotifyAdminForQuantityBelow { get; set; } /// <summary> /// Gets or sets a value indicating whether to allow orders when out of stock /// </summary> public int Backorders { get; set; } /// <summary> /// Gets or sets the order minimum quantity /// </summary> public int OrderMinimumQuantity { get; set; } /// <summary> /// Gets or sets the order maximum quantity /// </summary> public int OrderMaximumQuantity { get; set; } /// <summary> /// Gets or sets the warehouse identifier /// </summary> public int WarehouseId { get; set; } /// <summary> /// Gets or sets a value indicating whether to disable buy button /// </summary> public bool DisableBuyButton { get; set; } /// <summary> /// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price /// </summary> public bool CallForPrice { get; set; } /// <summary> /// Gets or sets the price /// </summary> public decimal Price { get; set; } /// <summary> /// Gets or sets the old price /// </summary> public decimal OldPrice { get; set; } /// <summary> /// Gets or sets the product cost /// </summary> public decimal ProductCost { get; set; } /// <summary> /// Gets or sets a value indicating whether a customer enters price /// </summary> public bool CustomerEntersPrice { get; set; } /// <summary> /// Gets or sets the minimum price entered by a customer /// </summary> public decimal MinimumCustomerEnteredPrice { get; set; } /// <summary> /// Gets or sets the maximum price entered by a customer /// </summary> public decimal MaximumCustomerEnteredPrice { get; set; } /// <summary> /// Gets or sets the weight /// </summary> public decimal Weight { get; set; } /// <summary> /// Gets or sets the length /// </summary> public decimal Length { get; set; } /// <summary> /// Gets or sets the width /// </summary> public decimal Width { get; set; } /// <summary> /// Gets or sets the height /// </summary> public decimal Height { get; set; } /// <summary> /// Gets or sets the picture identifier /// </summary> public int PictureId { get; set; } /// <summary> /// Gets or sets the available start date and time /// </summary> public DateTime? AvailableStartDateTime { get; set; } /// <summary> /// Gets or sets the shipped end date and time /// </summary> public DateTime? AvailableEndDateTime { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity is published /// </summary> public bool Published { get; set; } /// <summary> /// Gets or sets a value indicating whether the entity has been deleted /// </summary> public bool Deleted { get; set; } /// <summary> /// Gets or sets the display order /// </summary> public int DisplayOrder { get; set; } /// <summary> /// Gets or sets the date and time of instance creation /// </summary> public DateTime CreatedOn { get; set; } /// <summary> /// Gets or sets the date and time of instance update /// </summary> public DateTime UpdatedOn { get; set; } /// <summary> /// Gets or sets CouponCreated /// </summary> public bool? CouponetCreated { get; set; } /// <summary> /// Gets or sets the date and time of CouponCreated /// </summary> public DateTime? CouponetCreatedOn { get; set; } #endregion
سیستم ASP.NET Membership بهمراه ASP.NET 2.0 در سال 2005 معرفی شد، و از آن زمان تا بحال تغییرات زیادی در چگونگی مدیریت احزار هویت و اختیارات کاربران توسط اپلیکیشنهای وب بوجود آمده است. ASP.NET Identity نگاهی تازه است به آنچه که سیستم Membership هنگام تولید اپلیکیشنهای مدرن برای وب، موبایل و تبلت باید باشد.
پیش زمینه: سیستم عضویت در ASP.NET
- الگوی پایگاه داده آن برای SQL Server طراحی شده است، و قادر به تغییرش هم نیستید. میتوانید اطلاعات پروفایل را اضافه کنید، اما تمام دادهها در یک جدول دیگر ذخیره میشوند، که دسترسی به آنها نیز مشکلتر است، تنها راه دسترسی Profile Provider API خواهد بود.
- سیستم تامین کننده (Provider System) امکان تغییر منبع دادهها را به شما میدهد، مثلا میتوانید از بانکهای اطلاعاتی MySQL یا Oracle استفاده کنید. اما تمام سیستم بر اساس پیش فرض هایی طراحی شده است که تنها برای بانکهای اطلاعاتی relational درست هستند. میتوانید تامین کننده (Provider) ای بنویسید که دادههای سیستم عضویت را در منبعی به غیر از دیتابیسهای relational ذخیره میکند؛ مثلا Windows Azure Storage Tables. اما در این صورت باید مقادیر زیادی کد بنویسید. مقادیر زیادی هم System.NotImplementedException باید بنویسید، برای متد هایی که به دیتابیسهای NoSQL مربوط نیستند.
- از آنجایی که سیستم ورود/خروج سایت بر اساس مدل Forms Authentication کار میکند، سیستم عضویت نمیتواند از OWIN استفاده کند. OWIN شامل کامپوننت هایی برای احراز هویت است که شامل سرویسهای خارجی هم میشود (مانند Microsoft Accounts, Facebook, Google, Twitter). همچنین امکان ورود به سیستم توسط حسابهای کاربری سازمانی (Organizational Accounts) نیز وجود دارد مانند Active Directory و Windows Azure Active Directory. این کتابخانه از OAuth 2.0، JWT و CORS نیز پشتیبانی میکند.
- ذخیره دادههای سیستم عضویت در بانکهای اطلاعاتی non-relational مشکل است.
- نمی توانید از آن در کنار OWIN استفاده کنید.
- با فراهم کنندههای موجود ASP.NET Membership بخوبی کار نمیکند. توسعه پذیر هم نیست.
ASP.NET Universal Providers
ASP.NET Universal Providers برای ذخیره سازی اطلاعات سیستم عضویت در Windows Azure SQL Database توسعه پیدا کردند. با SQL Server Compact هم بخوبی کار میکنند. این تامین کنندهها بر اساس Entity Framework Code First ساخته شده بودند و بدین معنا بود که دادههای سیستم عضویت را میتوان در هر منبع داده ای که توسط EF پشتیبانی میشود ذخیره کرد. با انتشار این تامین کنندهها الگوی دیتابیس سیستم عضویت نیز بسیار سبکتر و بهتر شد. اما این سیستم بر پایه زیر ساخت ASP.NET Membership نوشته شده است، بنابراین محدودیتهای پیشین مانند محدودیتهای SqlMembershipProvider هنوز وجود دارند. به بیان دیگر، این سیستمها همچنان برای بانکهای اطلاعاتی relational طراحی شده اند، پس سفارشی سازی اطلاعات کاربران و پروفایلها هنوز مشکل است. در آخر آنکه این تامین کنندهها هنوز از مدل احراز هویت فرم استفاده میکنند.
ASP.NET Identity
- یک سیستم هویت واحد (One ASP.NET Identity system)
- سیستم ASP.NET Identity میتواند در تمام فریم ورکهای مشتق از ASP.NET استفاده شود. مانند ASP.NET MVC, Web Forms, Web Pages, Web API و SignalR
- از این سیستم میتوانید در تولید اپلیکیشنهای وب، موبایل، استور (Store) و یا اپلیکیشنهای ترکیبی استفاده کنید.
- سادگی تزریق دادههای پروفایل درباره کاربران
- روی الگوی دیتابیس برای اطلاعات کاربران و پروفایلها کنترل کامل دارید. مثلا میتوانید به سادگی یک فیلد، برای تاریخ تولد در نظر بگیرید که کاربران هنگام ثبت نام در سایت باید آن را وارد کنند.
- کنترل ذخیره سازی/واکشی اطلاعات
- بصورت پیش فرض ASP.NET Identity تمام اطلاعات کاربران را در یک دیتابیس ذخیره میکند. تمام مکانیزمهای دسترسی به دادهها توسط EF Code First کار میکنند.
- از آنجا که روی الگوی دیتابیس، کنترل کامل دارید، تغییر نام جداول و یا نوع داده فیلدهای کلیدی و غیره ساده است.
- استفاده از مکانیزمهای دیگر برای مدیریت دادههای آن ساده است، مانند SharePoint, Windows Azure Storage Table و دیتابیسهای NoSQL.
- تست پذیری
- ASP.NET Identity تست پذیری اپلیکیشن وب شما را بیشتر میکند. میتوانید برای تمام قسمت هایی که از ASP.NET Identity استفاده میکنند تست بنویسید.
- تامین کننده نقش (Role Provider)
- تامین کننده ای وجود دارد که به شما امکان محدود کردن سطوح دسترسی بر اساس نقوش را میدهد. بسادگی میتوانید نقشهای جدید مانند "Admin" بسازید و بخشهای مختلف اپلیکیشن خود را محدود کنید.
- Claims Based
- ASP.NET Identity از امکان احراز هویت بر اساس Claims نیز پشتیبانی میکند. در این مدل، هویت کاربر بر اساس دسته ای از اختیارات او شناسایی میشود. با استفاده از این روش توسعه دهندگان برای تعریف هویت کاربران، آزادی عمل بیشتری نسبت به مدل Roles دارند. مدل نقشها تنها یک مقدار منطقی (bool) است؛ یا عضو یک نقش هستید یا خیر، در حالیکه با استفاده از روش Claims میتوانید اطلاعات بسیار ریز و دقیقی از هویت کاربر در دست داشته باشید.
- تامین کنندگان اجتماعی
- به راحتی میتوانید از تامین کنندگان دیگری مانند Microsoft, Facebook, Twitter, Google و غیره استفاده کنید و اطلاعات مربوط به کاربران را در اپلیکیشن خود ذخیره کنید.
- Windows Azure Active Directory
- برای اطلاعات بیشتر به این لینک مراجعه کنید.
- یکپارچگی با OWIN
- ASP.NET Identity بر اساس OWIN توسعه پیدا کرده است، بنابراین از هر میزبانی که از OWIN پشتیبانی میکند میتوانید استفاده کنید. همچنین هیچ وابستگی ای به System.Web وجود ندارد. ASP.NET Identity یک فریم ورک کامل و مستقل برای OWIN است و میتواند در هر اپلیکیشنی که روی OWIN میزبانی شده استفاده شود.
- ASP.NET Identity از OWIN برای ورود/خروج کاربران در سایت استفاده میکند. این بدین معنا است که بجای استفاده از Forms Authentication برای تولید یک کوکی، از OWIN CookieAuthentication استفاده میشود.
- پکیج NuGet
- ASP.NET Identity در قالب یک بسته NuGet توزیع میشود. این بسته در قالب پروژههای ASP.NET MVC, Web Forms و Web API که با Visual Studio 2013 منتشر شدند گنجانده شده است.
- توزیع این فریم ورک در قالب یک بسته NuGet این امکان را به تیم ASP.NET میدهد تا امکانات جدیدی توسعه دهند، باگها را برطرف کنند و نتیجه را بصورت چابک به توسعه دهندگان عرضه کنند.
ASP.NET Identity در قالب پروژههای ASP.NET MVC, Web Forms, Web API و SPA که بهمراه Visual Studio 2013 منتشر شده اند استفاده میشود. در ادامه به اختصار خواهیم دید که چگونه ASP.NET Identity کار میکند.
- یک پروژه جدید ASP.NET MVC با تنظیمات Individual User Accounts بسازید.
-
پروژه ایجاد شده شامل سه بسته میشود که مربوط به ASP.NET Identity هستند:
- Microsoft.AspNet.Identity.EntityFramework این بسته شامل پیاده سازی ASP.NET Identity با Entity Framework میشود، که تمام دادههای مربوطه را در یک دیتابیس SQL Server ذخیره میکند.
- Microsoft.AspNet.Identity.Core این بسته محتوی تمام interfaceهای ASP.NET Identity است. با استفاده از این بسته میتوانید پیاده سازی دیگری از ASP.NET Identity بسازید که منبع داده متفاوتی را هدف قرار میدهد. مثلا Windows Azure Storage Table و دیتابیسهای NoSQL.
- Microsoft.AspNet.Identity.OWIN این بسته امکان استفاده از احراز هویت OWIN را در اپلیکیشنهای ASP.NET فراهم میکند. هنگام تولید کوکیها از OWIN Cookie Authentication استفاده خواهد شد.
هنگامیکه بر روی دکمهی Register کلیک شود، کنترلر Account، اکشن متد Register را فراخوانی میکند تا حساب کاربری جدیدی با استفاده از ASP.NET Identity API ساخته شود.
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser() { UserName = model.UserName }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } else { AddErrors(result); } } // If we got this far, something failed, redisplay form return View(model); }
اگر حساب کاربری با موفقیت ایجاد شود، کاربر توسط فراخوانی متد SignInAsync به سایت وارد میشود.
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser() { UserName = model.UserName }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } else { AddErrors(result); } } // If we got this far, something failed, redisplay form return View(model); }
private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync( user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn( new AuthenticationProperties() { IsPersistent = isPersistent }, identity); }
از آنجا که ASP.NET Identity و OWIN Cookie Authentication هر دو Claims-based هستند، فریم ورک، انتظار آبجکتی از نوع ClaimsIdentity را خواهد داشت. این آبجکت تمامی اطلاعات لازم برای تشخیص هویت کاربر را در بر دارد. مثلا اینکه کاربر مورد نظر به چه نقش هایی تعلق دارد؟ و اطلاعاتی از این قبیل. در این مرحله میتوانید Claimهای بیشتری را به کاربر بیافزایید.
کلیک کردن روی لینک Log off در سایت، اکشن متد LogOff در کنترلر Account را اجرا میکند.
// POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(); return RedirectToAction("Index", "Home"); }
همانطور که مشاهده میکنید برای ورود/خروج کاربران از AuthenticationManager استفاده میشود که متعلق به OWIN است. متد SignOut همتای متد FormsAuthentication.SignOut است.
کامپوننتهای ASP.NET Identity
تصویر زیر اجزای تشکیل دهنده ASP.NET Identity را نمایش میدهد. بسته هایی که با رنگ سبز نشان داده شده اند سیستم کلی ASP.NET Identity را میسازند. مابقی بستهها وابستگی هایی هستند که برای استفاده از ASP.NET Identity در اپلیکیشنهای ASP.NET لازم اند.
دو پکیج دیگر نیز وجود دارند که به آنها اشاره نشد:
- Microsoft.Security.Owin.Cookies این بسته امکان استفاده از مدل احراز هویت مبتنی بر کوکی (Cookie-based Authentication) را فراهم میکند. مدلی مانند سیستم ASP.NET Forms Authentication.
- EntityFramework که نیازی به معرفی ندارد.
مهاجرت از Membership به ASP.NET Identity
قدمهای بعدی
Garbage Collection
فرض کنید متغیری را ایجاد کرده و به آن مقدار دادهاید:
string message = "Hello World!";
آیا تابحال به این موضوع فکر کردهاید که طول عمر متغیر message تا چه زمانی است و چه زمانی باید از بین برود؟
چه زمانی باید توسط کامپایلر ( یا بهتر بگوییم ، Runtime ) طول عمر این متغیر به پایان برسد و از حافظه حذف شود؟
- Unmanaged: در این دسته از زبان ها، وظیفهی ایجاد اشیاء، تشخیص زمان درست برای از بین بردن و از بین بردن آنها، برعهدهی شماست. زبانهای C و ++C جز این نوع زبانها میباشند.
- Managed: در این دسته از زبانها، ایجاد اشیاء مانند قبل بر عهدهی شماست، اما وظیفه تشخیص و از بین بردن آنها در زمان درست را Runtime برعهده میگیرد.
در این نوع زبانها ما دغدغهی حذف متغیری را که در چندین خط بالاتر، آن را ایجاد کرده و به آن مقدار داده، به چندین متد آن را پاس داده و انواع تغییرات را روی آن انجام دادهایم، نداریم. زبانهای #C و Java جزو این نوع زبانها میباشند.
ایده کلی ایجاد زبانهای Managed بر این است که شما درگیر مباحث مربوط به Memory Management نشوید و تمرکز اصلیتان روی Business باشد.
اکثر پروژهها به اندازه کافی پیچیدگیهای Business ای دارند و ترکیب کردن این نوع پیچیدگیها با پیچیدگیهای Low Level و Technical ای همچون مباحث Memory Management، در اکثر اوقات باعث میشود که نگهداری از پروژه کاری بسیار دشوار شده و به تدریج مانند بسیاری از پروژههای دیگر، نام Legacy را با خود به یدک بکشد.
این بدان معنا نیست که پروژههایی که با زبان هایی همچون C و ++C نوشته میشوند، از همان روز اول Legacy بوده و قابل نگهداری نیستند، بلکه بدین معناست که نگهداری کد چنین زبانهایی نسبت به زبانهای Managed، به مراتب مشکلتر است و همچنین قابل ذکر است که قابلیت نگهداری یک پروژه به مباحث بسیار زیاد دیگری نیز بستگی دارد.
کد این برنامه در زبان C :
#include <stdio.h> #include <stdlib.h> void printReport(int* data) { printf("Report: %d", *data); } int main(void) { int *myNumber; myNumber = (int*)malloc(sizeof(int)); if (myNumber == 0) { printf("ERROR: Out of memory"); return 1; } *myNumber = 25; printReport(myNumber); free(myNumber); myNumber = NULL; return 0; }
"هدف" و Business اصلی این برنامه، چاپ و یک گزارش ساده بود، اما مسائل بسیار بیشتری در این مثال دخیل شده اند:
چند مرحله از این مراحل ذکر شده، واقعا نیاز Business ای برنامه ما بود؟ این مثال، بسیار ساده و غیر واقعی بود؛ اما تصور کنید با این روش، یک برنامه بزرگ با Business Ruleها و پیچیدگیهای خودش، چه حجمی از کد و پیچیدگی را خواهد داشت.
کد این برنامه در زبان #C :
using System; public class Program { public static void Main() { int myNumber = 25; PrintReport(myNumber); } private static void PrintReport(int number) { Console.WriteLine($"Report: {number}"); } }
همانطور که میبینید در اینجا تمرکزمان روی هدف اصلی و Business است و درگیر پیچیدگی مباحث جانبی نظیر Manual Memory Management نشدهایم و Runtime زبان #C یعنی CoreCLR وظیفه Memory Management را در پشت صحنه برعهده گرفته است.
تفاوت بین زبانهای Managed و Unmanaged را میتوانیم به رانندگی با ماشین دندهای و اتومات تشبیه کنیم.
اکثر اوقات هدف اصلی رانندگی، رفتن از یک مبدا به یک مقصد است. با استفاده از یک ماشین دندهای، علاوه بر هدف اصلی یعنی رسیدن به یک مقصد، ذهن ما درگیر تعویض دنده در سرعت مناسب در طول مسیر میشود و اینکار را ممکن است بیش از صدها یا هزاران بار انجام دهیم. در این روش طبیعتا ما کنترل بیشتری داریم و در بعضی مواقع بسیار بهتر به نسبت یک سیستم خودکار عمل میکنیم، اما از هدف اصلی خود یعنی رفتن از نقطه A به B دور شدهایم.
در زبانهای Managed و Unmanaged هم دقیقا چنین تفاوت هایی وجود دارد.
در زبانهای Unmanaged، شما کنترل کاملی را روی طول عمر اشیا و مدیریت حافظه دارید و همه چیز برعهده شماست؛ اما علاوه بر هدف اصلیتان، درگیر مباحث جانبی دیگری نیز شدهاید. اکثر اوقات قدرت زبانهای Unmanaged را در Game Engineها و Real-Time Processing Systemها میتوانید ببینید که در آنها مدیریت حافظه بصورت دستی انجام میشود و برنامه نویسهای آن سیستم، تعیین کننده اصلی این هستند که طول عمر اشیاء تا چه زمانی باشد و چه زمانی از بین بروند که باعث اختلال یا کندی یک سیستم حتی برای چند میلی ثانیه نشوند.
در زبانهای Managed، اکثر اوقات حتی نیازی نیست که شما درگیر مباحث جانبی مدیریت حافظه شوید و تمام کار را Runtime شما بصورت خودکار انجام میدهد. اما گاهی اوقات لازم است که قسمتهای حساس برنامه ( اصطلاحا Hot-Pathها ) خود را پیدا کنید، از این قسمتها Benchmark گرفته و مطمئن شوید که با حجم تعداد بالای درخواست و بار، به خوبی عمل میکند و همچنین قسمتی از برنامه، نشستی حافظه ( اصطلاحا Memory Leak ) ندارد. همانطور که گفتیم، گاهی اوقات یک انسان ( سیستم دستی ) بهتر از یک سیستم خودکار میتواند تصمیم بگیرد که در یک لحظه چه اتفاقی داخل برنامه رخ دهد.
در این سری مقالات قصد داریم وارد مبحث Memory Management در #C شده، با Garbage Collector آشنا و دیدی کلی از نحوهی انجام کار آن داشته باشیم.
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> <edmx:DataServices> <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OwinAspNetCore.Models"> <EntityType Name="Product"> <Key> <PropertyRef Name="Id"/> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false"/> <Property Name="Name" Type="Edm.String"/> <Property Name="Price" Type="Edm.Decimal" Nullable="false"/> </EntityType> </Schema> <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default"> <Function Name="TestFunction" IsBound="true"> <Parameter Name="bindingParameter" Type="Collection(OwinAspNetCore.Models.Product)"/> <Parameter Name="Val" Type="Edm.Int32" Nullable="false"/> <Parameter Name="Name" Type="Edm.String"/> <ReturnType Type="Edm.Int32" Nullable="false"/> </Function> <EntityContainer Name="Container"> <EntitySet Name="Products" EntityType="OwinAspNetCore.Models.Product"/> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>
string postUrl = "http://localhost:port/...."; HttpClient client = new HttpClient(); var response = client.PostAsync(postUrl, new StringContent(JsonConvert.SerializeObject(new { Rating = 5 }), Encoding.UTF8, "application/json")).Result;
آن را نصب نموده و بعد از تکمیل شدن، visual studio را restart کنید.
پروژهی console خود را باز کرده و از طریق Add -> new item، آیتم OData client را جستجو کرده و با نام ProductClient.tt آن را تولید نمایید (نام آن اختیاری است):
فایل ProductClient.tt را که یک T4 code generator میباشد، باز کرده و مقدار ثابت MetadataDocumentUri را به آدرس سرویس odata خود تغییر دهید:
public const string MetadataDocumentUri = "http://localhost:port/odata/";
روی این آیتم کلیک راست و گزینهی Run Custom tool را انتخاب نمایید. این تمام کاری است که نیاز به انجام دادن دارید.
حال فایل Program.cs را باز کرده و آنرا اینگونه تغییر دهید:
using ConsoleApplication1.OwinAspNetCore.Models; using System; using System.Linq; namespace ConsoleApplication1 { public class Program { static void Main(string[] args) { Uri uri = new Uri("http://localhost:24977/odata"); //var context = new Default.Container(uri); var context = new TestNameSpace.TestNameSpace(uri); //get var products = context.Products.Where(pr => pr.Name.Contains("a")) .Take(1).Select(pr => new { Firstname = pr.Name, PriceValue = pr.Price }).ToList(); //add context.AddToProducts(new Product() { Name = "Name1", Price = 123 }); //update Product p = context.Products.First(); p.Name = "changed"; context.UpdateObject(p); //delete context.DeleteObject(context.Products.Last()); //commit context.SaveChanges(); } } }
مشاهده میفرمایید که همهی عملیاتهای لازم برای CRUD، به شرط اینکه در سمت سرور طراحی شده باشند، به راحتی از سمت کلاینت قابل فراخوانی خواهند بود.
از این ویژگی فوق العاده میتوان حتی در کلاینتها جاوااسکریپتی نیز استفاده کرد. فرض کنید نرم افزار تحت وبی را با استفاده از jquery یا angularjs طراحی کردهاید. قاعدتا فراخوانی درخواستهای شما به سمت سرور، چیزی شبیه به این خواهد بود:
//angularjs $http.get("/products/get", {Name: "Test", Company: "Test"}) .then(function(response) { console.log(response.data); }); //jquery $.get("/products/get", {Name: "Test", Company: "Test"}, function(data, status){ console.log("Data: " + data); });
با استفاده از odata و typescript و یک library مربوط به odata client در سمت کلاینت، نرم افزار شما بجای موارد، بالا چیزی شبیه به مثال زیر خواهد بود (با همراه داشتن strongly typed و intellisense کامل)
let product1 = await context.products.filter(c => c.Name.contains("Ali")).toArray(); let product2 = await context.products.getSomeFunction(1, 'Test'); context.product.add({Name: 'Test'} as Product); await context.saveChanges()
در مقالههای آتی به ویژگیهای بیشتری از Odata خواهیم پرداخت.