نگاهی به ویژگی های جدید 6 #C
The C# language itself has changed little in version 6, the main importance of the release being the introduction of the Roslyn .NET Compiler Platform. However the New features and improvements that have been made to C# are welcome because they are aimed at aiding productivity. Paulo Morgado explains what they are, and how to use them.
معرفی Relay و GraphQL
Data fetching for React applications
Relay is a new framework from Facebook that provides data-fetching functionality for React applications. It was announced at React.js Conf (January 2015).
What is GraphQL?
GraphQL is a data querying language designed to describe the complex, nested data dependencies of modern applications. It's been in production use in Facebook's native apps for several years.
_identityVerifier.Initialize(); var isValidIdentity = _identityVerifier.Validate( application.Applicant.Name, application.Applicant.Age, application.Applicant.Address);
_creditScorer.CalculateScore(application.Applicant.Name, application.Applicant.Address); if (_creditScorer.Score < MinimumCreditScore) { return application.IsAccepted; }
تنظیم مقدار خاصیت Score شیء Mock شده
اینترفیس ICreditScorer به صورت زیر تعریف شدهاست و دارای خاصیت Score میباشد که مقدار عددی آن با مقدار حداقل اعتبار تنظیم شدهی در کلاس LoanApplicationProcessor مقایسه خواهد شد (MinimumCreditScore = 100_000):
namespace Loans.Services.Contracts { public interface ICreditScorer { int Score { get; } void CalculateScore(string applicantName, string applicantAddress); } }
var mockCreditScorer = new Mock<ICreditScorer>(); mockCreditScorer.Setup(x => x.Score).Returns(110_000);
اکنون اگر متد آزمایش واحد Accept را بررسی کنیم، چون شخص درخواست دهنده، دارای اعتبار بیشتری از حداقل اعتبار مورد نیاز است، این آزمایش با موفقیت به پایان خواهد رسید. اگر این تنظیم صورت نمیگرفت، شیء mockCreditScorer، مقدار پیشفرض int یا همان صفر را به عنوان مقدار Score بازگشت میداد.
تنظیم مقادیر خواص تو در تو و سلسله مراتبی اشیاء Mock شده
برای کار با خواص تو در تو، ابتدا دو مدل زیر را ایجاد میکنیم:
namespace Loans.Models { public class ScoreResult { public ScoreValue ScoreValue { get; } } public class ScoreValue { public int Score { get; } } }
using Loans.Models; namespace Loans.Services.Contracts { public interface ICreditScorer { int Score { get; } void CalculateScore(string applicantName, string applicantAddress); ScoreResult ScoreResult { get; } } }
//if (_creditScorer.Score < MinimumCreditScore) if (_creditScorer.ScoreResult.ScoreValue.Score < MinimumCreditScore)
برای رفع این مشکل در متد آزمون واحد Accept، باید به صورت زیر عمل کرد:
var mockCreditScorer = new Mock<ICreditScorer>(); mockCreditScorer.Setup(x => x.Score).Returns(110_000); var mockScoreValue = new Mock<ScoreValue>(); mockScoreValue.Setup(x => x.Score).Returns(110_000); var mockScoreResult = new Mock<ScoreResult>(); mockScoreResult.Setup(x => x.ScoreValue).Returns(mockScoreValue.Object); mockCreditScorer.Setup(x => x.ScoreResult).Returns(mockScoreResult.Object);
سپس یک سطح بالاتر را یعنی ScoreResult را تنظیم خواهیم کرد. در اینجا نیاز است خاصیت ScoreValue آن به mock object قبلی تنظیم شود. به همین جهت Returns آن به خاصیت Object شیء mockScoreValue، تنظیم شدهاست.
در آخر برای تنظیم خاصیت ScoreResult شیء mockCreditScorer اصلی، از شیء mockScoreResult استفاده خواهیم کرد.
در این حالت اگر متد آزمون واحد Accept را اجرا کنیم، اینبار به خطای زیر برخواهیم خورد:
Test method Loans.Tests.LoanApplicationProcessorShould.Accept threw exception: System.NotSupportedException: Unsupported expression: x => x.Score Non-overridable members (here: ScoreValue.get_Score) may not be used in setup / verification expressions.
namespace Loans.Models { public class ScoreResult { public virtual ScoreValue ScoreValue { get; } } public class ScoreValue { public virtual int Score { get; } } }
ساده سازی روش تنظیم مقادیر خواص تو در تو و سلسله مراتبی اشیاء Mock شده
روش دیگری نیز برای تنظیم مقادیر خواص تو در تو در کتابخانهی Moq وجود دارد:
mockCreditScorer.Setup(x => x.ScoreResult.ScoreValue.Score).Returns(110_000);
بدیهی است در این حالت نیز باید شرط virtual بودن این خواص، برقرار باشد؛ در غیراینصورت همان استثنای NotSupportedException را دریافت خواهیم کرد.
یک نکته: اگر در زمان تشکیل یک Mock object، مقدار خاصیت DefaultValue آنرا به صورت زیر تنظیم کنیم:
var mockCreditScorer = new Mock<ICreditScorer> { DefaultValue = DefaultValue.Mock };
بررسی تغییرات مقادیر خواص اشیاء Mock شده
کتابخانهی Moq، امکان ردیابی تغییرات مقادیر خواص اشیاء Mock شده را نیز داراست. برای نمایش آن، فرض کنید خاصیت جدید Count را به اینترفیس ICreditScorer اضافه کردهایم:
using Loans.Models; namespace Loans.Services.Contracts { public interface ICreditScorer { int Score { get; } void CalculateScore(string applicantName, string applicantAddress); ScoreResult ScoreResult { get; } int Count { get; set; } } }
_creditScorer.CalculateScore(application.Applicant.Name, application.Applicant.Address); _creditScorer.Count++;
Assert.AreEqual(1, mockCreditScorer.Object.Count);
برای فعال سازی ردیابی تغییرات مقادیر خاصیت Count، تنها کافی است آنرا توسط متد SetupProperty، معرفی کنیم:
mockCreditScorer.SetupProperty(x => x.Count);
در اینجا میتوان یک مقدار اولیه را هم درنظر گرفت:
mockCreditScorer.SetupProperty(x => x.Count, 10);
فعالسازی بررسی تغییرات تمام مقادیر خواص اشیاء Mock شده
اگر تعداد خواصی که قرار است مورد ردیابی قرارگیرند زیاد است، بجای فراخوانی متد SetupProperty بر روی تک تک آنها، میتوان تمام آنها را به صورت زیر تحت کنترل قرار داد:
mockCreditScorer.SetupAllProperties();
نکتهی مهم: محل قرارگیری SetupAllProperties مهم است. برای مثال اگر این سطر را پس از سطر تنظیم مقدار پیشفرض x.ScoreResult.ScoreValue.Score قرار دهید، آزمایش با شکست مواجه میشود؛ چون تنظیمات بازگشت مقادیر پیشفرض خواص را به طور کامل بازنویسی میکند. بنابراین این سطر باید پیش از سطر تنظیم مقادیر پیشفرض خواص Mock شده، فراخوانی شود تا بر روی این مقادیر تنظیمی، تاثیری نداشته باشد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MoqSeries-03.zip
public IExpectPrimaryContactPersonBuilder WithSurname(string surname) { return new PersonBuilder { Name = this.Name; Surname = surname; } }
class ExpectSurname : IExpectSurnamePersonBuilder { private string Name { get; } public ExpectSurname(string name) { this.Name = name; } public IExpectPrimaryContactPersonBuilder WithSurname(string surname) { if (string.IsNullOrEmpty(surname)) throw new ArgumentException(nameof(surname)); return new ExpectPrimaryContact(this.Name, surname); } }
اگر یک لیست رشته ایی داشته باشیم به راحتی میتونیم روی پایگاه داده جستجو کنیم:
var eyeColors = new List<string> { "Black", "Green", "Brown" }; var specificUsers = _context.Users.Where(x => eyeColors.Contains(x.EyeColor)).ToList();
حالا اگه لیست ما ترکیبی باشه این سرچ باید به چه صورت انجام بشه:
public class User { public string FullName { get; set; } public string EyeColor { get; set; } } var customUsers = new List<User> { new User{FullName = "Ali Ahmadi", EyeColor = "Brown"}, new User{FullName = "Milad Rezaei", EyeColor = "Green"} }; var specificUsers = _context.Users.Where(x => customUsers.Contains(new User(){EyeColor = x.EyeColor, FullName = x.FullName})).ToList();
روش فوق و روش های زیادی رو سرچ کردم ولی به جواب نرسیدم. آیا میشه از EF استفاده کرد یا راه حل دیگری دارد ؟
محدودیتهای کار با اشیاء COM در NET Core 2x.
پیاده سازی پشتیبانی از اشیاء COM در NET Core 2x. به همراه اینترفیس IDispatch نیست. به این معنا که از مفهوم «late binding» پشتیبانی نمیکند. حدود 10 سال قبل در زمان ارائهی C# 4.0، واژهی کلیدی dynamic نیز ارائه شد که یکی از مهمترین اهداف آن، ساده سازی کار با اشیاء COM و پشتیبانی از Late binding بود:
dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true)); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
System.__ComObject does not contain a definition for 'Visible'
یک نکته: NET Core 3x. از Late binding پشتیبانی میکند.
روش کار با اشیاء COM در NET Core 2x.
چون NET Core 2x. از late binding اشیاء COM پشتیبانی نمیکند، میتوان در اینجا از روش قدیمیتر کار با اشیاء COM که استفادهی از «Interop assemblies» نام دارد، استفاده کرد. Interop assemblies در حقیقت محصور کنندههای اشیاء COM هستند که امکان کار مستقیم با آنها را از طریق early binding میسر میکنند. در یک چنین حالتی، کدهای فوق برای دسترسی به اشیاء COM کار با اکسل، به صورت زیر که early binding نام دارد، تغییر میکند:
using Excel = Microsoft.Office.Interop.Excel; // ... var excel = new Excel.Application(); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
روش تولید Interop assemblies
هنوز خود NET Core. روشی را برای تولید Interop assemblies ارائه ندادهاست و تولید آنها یکی از معدود مواردی است که نیاز به نصب Visual Studio را دارد. برای این منظور یک پروژهی خالی (از هر نوعی) را که بر اساس NET Framework 4x. تهیه میشود، در VS آغاز کنید و سپس در solution explorer بر روی پروژهی ایجاد شده کلیک راست کرده و گزینهی Add > Reference را انتخاب کنید. در صفحهی باز شده، گزینهی COM آنرا باید انتخاب کنید. در اینجا است که میتوانید با انتخاب یکی از موارد، ارجاعی را به آن شیء COM اضافه کنید.
پس از اینکار:
- ابتدا این ارجاع اضافه شده را در solution explorer انتخاب کرده و در پایین صفحه، در قسمت برگهی خواص آن، گزینهی «Embed Interop Types» آنرا به false تنظیم کنید.
- سپس یکبار پروژه را نیز کامپایل کنید.
این مراحل سبب تولید یک فایل dll خواهند شد که Interop assembly نام دارد و هم در برنامههای NET. و هم NET Core.، قابل استفادهاست.
روش استفاده از Interop assemblies در برنامههای NET Core.
اکنون که یک فایل dll را از شیء COM انتخابی، در یک پروژهی مجزای مبتنی بر NET 4x. تولید کردیم، روش استفادهی از آن در یک برنامهی دیگر مبتنی بر NET Core. به صورت زیر است:
<ItemGroup> <Reference Include="Interop.WIA"> <HintPath>..\DNTScanner.Core.TypeLibrary\bin\Debug\Interop.WIA.dll</HintPath> <EmbedInteropTypes>True</EmbedInteropTypes> </Reference> </ItemGroup>
یک نکته: اگر EmbedInteropTypes را به true تنظیم کردید، نیاز به بستهی Microsoft.CSharp را نیز خواهید داشت:
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' "> <Reference Include="Microsoft.CSharp" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> </ItemGroup>
روش دیگر استفاده از Interop assemblies در برنامههای NET Core.
روش فوق، جهت کار با فایلهای dll ای است که خودمان تولید کردهایم. برای سایر حالاتی که این موارد در سیستم نصب شدهاند (مانند Office Primary Interop Assemblies (PIA))، پس از افزودن ارجاعی به COM reference مدنظر، فایل csproj همان پروژهی NET 4x. را باز کرده و قسمت COMReference آنرا در اینجا (در فایل csproj پروژهی NET Core.) کپی کنید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <!-- The following 'COMReference' items were copied from a .NET Framework project. They were added by using the Visual Studio COM References window. See https://docs.microsoft.com/en-us/visualstudio/ide/managing-references-in-a-project?view=vs-2017. Observe the 'EmbedInteropTypes' tag value. See https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2017#comreference --> <ItemGroup> <COMReference Include="Microsoft.Office.Core"> <Guid>{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}</Guid> <VersionMajor>2</VersionMajor> <VersionMinor>8</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="Microsoft.Office.Interop.Excel"> <Guid>{00020813-0000-0000-C000-000000000046}</Guid> <VersionMajor>1</VersionMajor> <VersionMinor>9</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="VBIDE"> <Guid>{0002E157-0000-0000-C000-000000000046}</Guid> <VersionMajor>5</VersionMajor> <VersionMinor>3</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> </ItemGroup> </Project>
سپس یک نمونه از MS Office automation را توسط اشیاء COM آن به صورت زیر میتوان پیاده سازی کرد:
using System; using System.Reflection; using Excel = Microsoft.Office.Interop.Excel; namespace ExcelDemo { class Program { public static void Main(string[] args) { Excel.Application excel; Excel.Workbook workbook; Excel.Worksheet sheet; Excel.Range range; try { // Start Excel and get Application object. excel = new Excel.Application(); excel.Visible = true; // Get a new workbook. workbook = excel.Workbooks.Add(Missing.Value); sheet = (Excel.Worksheet)workbook.ActiveSheet; // Add table headers going cell by cell. sheet.Cells[1, 1] = "First Name"; sheet.Cells[1, 2] = "Last Name"; sheet.Cells[1, 3] = "Full Name"; sheet.Cells[1, 4] = "Salary"; // Format A1:D1 as bold, vertical alignment = center. sheet.get_Range("A1", "D1").Font.Bold = true; sheet.get_Range("A1", "D1").VerticalAlignment = Excel.XlVAlign.xlVAlignCenter; // Create an array to multiple values at once. string[,] saNames = new string[5, 2]; saNames[0, 0] = "John"; saNames[0, 1] = "Smith"; saNames[1, 0] = "Tom"; saNames[1, 1] = "Brown"; saNames[2, 0] = "Sue"; saNames[2, 1] = "Thomas"; saNames[3, 0] = "Jane"; saNames[3, 1] = "Jones"; saNames[4, 0] = "Adam"; saNames[4, 1] = "Johnson"; // Fill A2:B6 with an array of values (First and Last Names). sheet.get_Range("A2", "B6").Value2 = saNames; // Fill C2:C6 with a relative formula (=A2 & " " & B2). range = sheet.get_Range("C2", "C6"); range.Formula = "=A2 & \" \" & B2"; // Fill D2:D6 with a formula(=RAND()*100000) and apply format. range = sheet.get_Range("D2", "D6"); range.Formula = "=RAND()*100000"; range.NumberFormat = "$0.00"; // AutoFit columns A:D. range = sheet.get_Range("A1", "D1"); range.EntireColumn.AutoFit(); // Make sure Excel is visible and give the user control // of Microsoft Excel's lifetime. excel.Visible = true; excel.UserControl = true; } catch (Exception e) { Console.WriteLine($"Error: {e.Message} Line: {e.Source}"); } } } }
How to automate Microsoft Excel from Microsoft Visual C#.NET
{"jti":"26bdfd20-104f-45d4-a4e1-111044808041", "iss":"http://localhost:5000/", "iat":1531729854, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"1", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"Vahid", "DisplayName":"وحید", "http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber":"046fb152a7474043952475cfa952cdc9", "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata":"1", "DynamicPermission":[":MyProtectedApi2:Get", ":MyProtectedEditorsApi:Get", ":MyProtectedApi3:Get", ":MyProtectedApi4:Get"], "http://schemas.microsoft.com/ws/2008/06/identity/claims/role":["Admin", "Editor", "User"], "nbf":1531729855, "exp":1531729975, "aud":"Any"}
public bool CanUserAccess(ClaimsPrincipal user, string area, string controller, string action) { var currentClaimValue = $"{area}:{controller}:{action}"; var securedControllerActions = _mvcActionsDiscoveryService.GetAllSecuredControllerActionsWithPolicy(ConstantPolicies.DynamicPermission); if (!securedControllerActions.SelectMany(x => x.MvcActions).Any(x => x.ActionId == currentClaimValue)) { throw new KeyNotFoundException($@"The `secured` area={area}/controller={controller}/action={action} with `ConstantPolicies.DynamicPermission` policy not found. Please check you have entered the area/controller/action names correctly and also it's decorated with the correct security policy."); } if (!user.Identity.IsAuthenticated) { return false; } if (user.IsInRole("Admin")) { // Admin users have access to all of the pages. return true; } // Check for dynamic permissions // A user gets its permissions claims from the `ApplicationClaimsPrincipalFactory` class automatically and it includes the role claims too. //for check user has claim for access to action return user.HasClaim(claim => claim.Type == ConstantPolicies.DynamicPermissionClaimType && claim.Value == currentClaimValue); }
امکان ساخت قالب برای پروژههای NET Core.
{ "author": "Firstname and lastname", "classifications": [ ".Net core", "AspNetCore" ], "name": "Base project", "identity": "BaseProject", "shortName": "baseproject", "tags": { "language": "C#" }, "sourceName": "DotnetCoreSample", "preferNameDirectory": true // Here }