فعالسازی قدم به قدم Roslyn refactoring افزونهی Roslynator
الف) فایل VSIX آنرا از اینجا دریافت کنید و سپس پسوند آنرا به zip تغییر دهید.
ب) این فایل zip را در پوشهای مشخص باز کنید.
ج) پس از باز کردن این فایل zip، دو فایل Roslynator.VisualStudio.Core.dll و Roslynator.VisualStudio.dll آنرا حذف کنید. این فایلها مخصوص نگارش کامل ویژوال استودیو هستند و در صورت وجود، با سیستم بارگذاری افزونههای OmniSharp تداخل میکنند.
د) در آخر مسیر زیر را گشوده:
%USERPROFILE%/.omnisharp
{ "RoslynExtensionsOptions": { "LocationPaths": [ "C:\\lib\\roslynator" ] } }
اکنون اگر VSCode را اجرا کنید، شاهد افزوده شدن امکانات Refactoring مخصوص افزونهی Roslynator به لیست Refactoring پیشفرض OmniSharp خواهید بود:
خودکار سازی دریافت، نصب و فعالسازی Roslyn refactoring افزونهی Roslynator
مراحل فوق را میتوان تبدیل به یک اسکریپت پاورشل کرد که با هر بار اجرای آن، به صورت خودکار کار دریافت و نصب این افزونه صورت گیرد:
Write-Host "Download, unzip and enable Roslynator for Visual Studio Code" $name = "josefpihrt.Roslynator2017" $url = "https://marketplace.visualstudio.com/items?itemName=$name" $currentDir = $PSScriptRoot $file = "$currentDir\Roslynator.zip" $pattern = "<script class=`"vss-extension`" defer=`"defer`" type=`"application\/json`">(.*?)<\/script>" $regex = [regex]"(?m)$pattern" Write-Host "Grab the home page of the $name." $dom = (New-Object Net.WebClient).DownloadString($url); if($dom -and $dom -match $pattern) { $matches = $regex.Match($dom) $jsonText = $matches[0].Groups[1] $json = ConvertFrom-Json $jsonText $version = $Json.versions[0].version # Parse the json in the page for the latest version number $parts = $name.Split(".") $publisher = $parts[0] $package = $parts[1] # Assemble the url for the vsix package $packageUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/$publisher/vsextensions/$package/$version/vspackage" Write-Host "Download the vsix package: $packageUrl" (New-Object Net.WebClient).DownloadFile($packageUrl, $file) Write-Host "Using $currentDir as the current dir." Write-Host "Unzip $file." $shellApp = new-object -com shell.application $zipFile = $shellApp.namespace($file) $destination = $shellApp.namespace($currentDir) $destination.Copyhere($zipFile.items(), 0x14)# overwrite and be silent Write-Host "Delete VS specific files. Otherwise they will interfere with the MEF services inside OmniSharp." Remove-Item "$currentDir\Roslynator.VisualStudio.Core.dll","$currentDir\Roslynator.VisualStudio.dll", "$currentDir\Roslynator.VisualStudio.pkgdef" $omnisharpJsonFilePath = "$env:USERPROFILE\.omnisharp\omnisharp.json"; Write-Host "Create $omnisharpJsonFilePath file." $omnisharpJson = @" {{ "RoslynExtensionsOptions": {{ "LocationPaths": [ "{0}" ] }} }} "@ -f $currentDir -Replace "\\","\\" $omnisharpJson | Out-File "$omnisharpJsonFilePath" -Confirm Write-Host "Done!" } else { Write-Host "Failed to find the packageUrl!" }
PS D:\Prog\1396\RoslynatorVSCode> .\update.ps1 Download, unzip and enable Roslynator for Visual Studio Code Grab the home page of the josefpihrt.Roslynator2017. Download the vsix package: https://marketplace.visualstudio.com/_apis/public/gallery/publishers/josefpihrt/vsextensions/Roslynator2017/1.7.0/vspackage Using D:\Prog\1396\RoslynatorVSCode as the current dir. Unzip D:\Prog\1396\RoslynatorVSCode\Roslynator.zip. Delete VS specific files. Otherwise they will interfere with the MEF services inside OmniSharp. Create C:\Users\Vahid\.omnisharp\omnisharp.json file. Confirm Are you sure you want to perform this action? Performing the operation "Output to File" on target "C:\Users\Vahid\.omnisharp\omnisharp.json". [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Done!
در ادامه این افزونه دریافت شده و در پوشهی جاری باز خواهد شد. سپس فایل omnisharp.json نیز به صورت خودکار تشکیل و مقدار دهی میشود.
اکنون اگر VSCode را اجرا کنید، همه چیز آماده بوده و امکانات این افزونه در دسترس خواهند بود.
We are thrilled to announce the highly anticipated .NET Conf 2024, a free, three-day virtual developer event celebrating the release of .NET 9. Co-organized by the .NET community and Microsoft, this annual tradition continues to grow, and we’re more excited than ever to bring you the latest innovations in .NET. Mark your calendars for November 12th to 14th, 2024, and prepare to be inspired by a wealth of knowledge, creativity, and community engagement.
The WebSocket inspector will be released in Firefox 71, but is ready for you to use in Firefox Developer Edition now.
مشکلی که در دراز مدت با SQLDom وجود خواهد داشت، مواردی مانند SelectStarExpression و CreateProcedureStatement و امثال آن هستند. اینها را از کجا باید تشخیص داد؟ همچنین مراحل بررسی این اجزاء، نسبتا طولانی هستند و نیاز به یک راه حل عمومیتر در این زمینه وجود دارد.
راه حلی برای این مشکل در مطلب «XML ‘Visualizer’ for the TransactSql.ScriptDom parse tree» ارائه شدهاست. در اینجا تمام اجزای TSqlFragment توسط Reflection مورد بررسی و استخراج قرار گرفته و نهایتا یک فایل XML از آن حاصل میشود.
اگر نکات ذکر شده در این مقاله را تبدیل به یک برنامه با استفاده مجدد کنیم، به چنین شکلی خواهیم رسید:
این برنامه را از اینجا میتوانید دریافت کنید:
DomToXml.zip
همانطور که در تصویر مشاهده میکنید، اینبار به سادگی، SelectStarExpression قابل تشخیص است و تنها کافی است در T-SQL پردازش شده، به دنبال SelectStarExpressionها بود. برای اینکار جهت ساده شدن آنالیز میتوان با ارث بری از کلاس پایه TSqlFragmentVisitor شروع کرد:
using System; using System.Linq; using Microsoft.SqlServer.TransactSql.ScriptDom; namespace DbCop { public class SelectStarExpressionVisitor : TSqlFragmentVisitor { public override void ExplicitVisit(SelectStarExpression node) { Console.WriteLine( "`Select *` detected @StartOffset:{0}, Line:{1}, T-SQL: {2}", node.StartOffset, node.StartLine, string.Join(string.Empty, node.ScriptTokenStream.Select(x => x.Text)).Trim()); base.ExplicitVisit(node); } } }
مرحلهی بعد، اجرای این کلاس Visitor است:
public static class GenericVisitor { public static void Start(string tSql, TSqlFragmentVisitor visitor) { IList<ParseError> errors; TSqlScript sqlFragment; using (var reader = new StringReader(tSql)) { var parser = new TSql120Parser(initialQuotedIdentifiers: true); sqlFragment = (TSqlScript)parser.Parse(reader, out errors); } if (errors != null && errors.Any()) { var sb = new StringBuilder(); foreach (var error in errors) sb.AppendLine(error.Message); throw new InvalidOperationException(sb.ToString()); } sqlFragment.Accept(visitor); } }
مثالی از نحوهی استفاده از کلاس GenericVisitor فوق را در اینجا ملاحظه میکنید:
var tsql = @"WITH ctex AS ( SELECT * FROM sys.objects ) SELECT * FROM ctex"; GenericVisitor.Start(tsql, new SelectStarExpressionVisitor());
if (ModelState.IsValid) { // find user by username var user = await _signInManager.UserManager.FindByNameAsync(Input.Username); // validate username/password using ASP.NET Identity if (user != null && (await _signInManager.CheckPasswordSignInAsync(user, Input.Password, true)) == SignInResult.Success) { await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (LoginOptions.AllowRememberLogin && Input.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(LoginOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.Id) { DisplayName = user.UserName }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage(Input.ReturnUrl); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return Redirect(Input.ReturnUrl); } // request for a local page if (Url.IsLocalUrl(Input.ReturnUrl)) { return Redirect(Input.ReturnUrl); } else if (string.IsNullOrEmpty(Input.ReturnUrl)) { return Redirect("~/"); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage); }
روشهای زیادی برای مدیریت این مساله وجود دارند؛ مانند استفاده از ماژولهای URL Rewrite برای بازنویسی آدرسهای نهایی صفحهی در حال رندر و یا ... به روز رسانی مستقیم بانک اطلاعاتی، یافتن تمام فیلدهای رشتهای ممکن در تمام جداول موجود و سپس اعمال تغییرات.
یافتن لیست تمام جداول قابل مدیریت توسط Entity framework
در ابتدا میخواهیم لیست پویای تمام جداول مدیریت شدهی توسط EF را پیدا کنیم. از این جهت که نمیخواهیم به ازای هر کدام یک کوئری جداگانه بنویسیم.
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using EFReplaceAll.Models; namespace EFReplaceAll.Config { public class DbSetInfo { public IQueryable<object> DbSet { set; get; } public Type DbSetType { set; get; } } public class MyContext : DbContext { public DbSet<Product> Products { set; get; } public DbSet<Category> Categories { set; get; } public DbSet<User> Users { set; get; } public MyContext() : base("Connection1") { this.Database.Log = sql => Console.Write(sql); } public IList<DbSetInfo> GetAllDbSets() { return this.GetType() .GetProperties() .Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)) .Select(p => new DbSetInfo { DbSet = (IQueryable<object>)p.GetValue(this, null), DbSetType = p.PropertyType.GetGenericArguments().First() }) .ToList(); } } }
یافتن فیلدهای رشتهای رکوردهای تمام جداول و سپس به روز رسانی آنها
میخواهیم متدی را طراحی کنیم که در آن لیستی از یافتنها و جایگزینیها قابل تعیین باشد. به همین جهت مدل زیر را تعریف میکنیم:
using System; namespace EFReplaceAll.Utils { public class ReplaceOp { public string ToFind { set; get; } public string ToReplace { set; get; } public StringComparison Comparison { set; get; } } }
سپس متدی که کار یافتن تمام فیلدهای رشتهای و سپس جایگزین کردن آنها را انجام میدهد به صورت زیر خواهد بود:
using System.Collections.Generic; using System.Linq; using EFReplaceAll.Config; namespace EFReplaceAll.Utils { public static class UpdateDbContextContents { public static void ReplaceAllStringsAcrossTables(IList<ReplaceOp> replaceOps) { int dbSetsCount; using (var uow = new MyContext()) { dbSetsCount = uow.GetAllDbSets().Count; } for (var i = 0; i < dbSetsCount; i++) { using (var uow = new MyContext()) // using a new context each time to free resources quickly. { var dbSetResult = uow.GetAllDbSets()[i]; var stringProperties = dbSetResult.DbSetType.GetProperties() .Where(p => p.PropertyType == typeof(string)) .ToList(); var dbSetEntities = dbSetResult.DbSet; var haveChanges = false; foreach (var entity in dbSetEntities) { foreach (var stringProperty in stringProperties) { var oldPropertyValue = stringProperty.GetValue(entity, null) as string; if (string.IsNullOrWhiteSpace(oldPropertyValue)) { continue; } var newPropertyValue = oldPropertyValue; foreach (var replaceOp in replaceOps) { newPropertyValue = newPropertyValue.ReplaceString(replaceOp.ToFind, replaceOp.ToReplace, replaceOp.Comparison); } if (oldPropertyValue != newPropertyValue) { stringProperty.SetValue(entity, newPropertyValue, null); haveChanges = true; } } } if (haveChanges) { uow.SaveChanges(); } } } } } }
- در اینجا using (var uow = new MyContext()) را زیاد مشاهده میکنید. علت اینجا است که اگر تنها با یک Context کار کنیم، EF تمام تغییرات و تمام رکوردهای وارد شدهی به آنرا کش میکند و مصرف حافظهی برنامه با توجه به خواندن تمام رکوردهای بانک اطلاعاتی توسط آن، ممکن است به چند گیگابایت برسد. به همین جهت از Contextهایی با طول عمر کوتاه استفاده شدهاست تا میزان مصرف RAM این متد سبب کرش برنامه نشود.
- در ابتدای کار توسط متد GetAllDbSets که به Context اضافه کردیم، تعداد DbSetهای موجود را پیدا میکنیم تا بتوان بر روی آنها حلقهای را تشکیل داد و به ازای هر کدام یک (()using (var uow = new MyContext را تشکیل داد.
- سپس با استفاده از نوع DbSet که توسط خاصیت dbSetResult.DbSetType در دسترس است، خواص رشتهای ممکن این DbSet یافت میشوند.
- در ادامه dbSetResult.DbSet یک Data Reader را به صورت پویا بر روی DbSet جاری باز کرده و تمام رکوردهای این DbSet را یک به یک بازگشت میدهد.
- در اینجا با استفاده از Reflection، از رکورد جاری، مقادیر خواص رشتهای آن دریافت شده و سپس کار جستجو و جایگزینی انجام میشود.
- در آخر هم فراخوانی uow.SaveChanges کار ثبت تغییرات صورت گرفته را انجام میدهد.
متدی برای جایگزینی غیرحساس به حروف بزرگ و کوچک
متد استاندارد Replace رشتهها، حساس به حروف بزرگ و کوچک است. یک نمونهی عمومیتر را که در آن بتوان StringComparison.OrdinalIgnoreCase را تعیین کرد، در ذیل مشاهده میکنید که از آن در متد ReplaceAllStringsAcrossTables فوق استفاده شدهاست:
using System; using System.Text; namespace EFReplaceAll.Utils { public static class StringExtensions { public static string ReplaceString(this string src, string oldValue, string newValue, StringComparison comparison) { if (string.IsNullOrWhiteSpace(src)) { return src; } if (string.Compare(oldValue, newValue, comparison) == 0) { return src; } var sb = new StringBuilder(); var previousIndex = 0; var index = src.IndexOf(oldValue, comparison); while (index != -1) { sb.Append(src.Substring(previousIndex, index - previousIndex)); sb.Append(newValue); index += oldValue.Length; previousIndex = index; index = src.IndexOf(oldValue, index, comparison); } sb.Append(src.Substring(previousIndex)); return sb.ToString(); } } }
UpdateDbContextContents.ReplaceAllStringsAcrossTables( new[] { new ReplaceOp { ToFind = "https://www.dntips.ir", ToReplace = "https://www.dntips.ir", Comparison = StringComparison.OrdinalIgnoreCase }, new ReplaceOp { ToFind = "https://www.dntips.ir", ToReplace = "https://www.dntips.ir", Comparison = StringComparison.OrdinalIgnoreCase } });
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: EFReplaceAll.zip
Any experienced .NET developer knows that even though .NET applications have a garbage collector, memory leaks occur all the time. It’s not that the garbage collector has bugs, it’s just that there are ways we can (easily) cause memory leaks in a managed language.
Memory leaks are sneakily bad creatures. It’s easy to ignore them for a very long time, while they slowly destroy the application. With memory leaks, your memory consumption grows, creating GC pressure and performance problems. Finally, the program will just crash on an out-of-memory exception.
In this article, we will go over the most common reasons for memory leaks in .NET programs. All examples are in C#, but they are relevant to other languages.
4.Visual Studio 2017 15.7 منتشر شد
These are the customer-reported issues addressed in 15.7.4:
- Green squiggles and light bulb with "Macro in skipped region" message.
- F7 does not switch to code.
- F7 does not build anymore.
- F7 build does not work.
- LINK : error : Telemetry event upload failed: 'Failed to open connection to VCTIP'.
- AXML file doesn't open after update.
- F7 no longer toggles between the designer and the code.
- XAML Editor Error: Window is not supported in WPF project.
- WinForms View.ToggleDesigner does not work as it used to.
- Toggle designer key binding lost.
- F7 does not start a new build.
- F7 no longer toggles between Designer and Code views.
- There is a problem with structure definition in C language.
- Provisioning a new SQL Server and new SQL DB in a different region than the App Service plan fails.
Git Security Vulnerability
We also fixed a security vulnerability in Git that was disclosed by the Git community. The vulnerability can lead to arbitrary code execution when a user clones a malicious repository. This blog post has more information.
اما با استفاده از ویژگیهای جدید اضافه شده(هر چند با تأخیر نسبت به Gridهای پیشرفته دیگر ) کار با این کنترل راحتتر و خواناتر شده است. یکی از این ویژگیها را با هم بررسی میکنیم:
با استفاده از ویژگی SelectMethod میتوان متدی را به GridView معرفی کرد که وظیفه منبع داده را انجام داده و هنگام Bind فراخوانی شده و گرید را پر کند:
مثال:
<asp:GridView ID="gvCities" runat="server" AutoGenerateColumns="False" ItemType="WebApplication3.City" SelectMethod="GetAllCities"> <Columns> <asp:TemplateField HeaderText="نام"> <ItemTemplate><%#: Item.Name %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
کد متد:
public IQueryable<City> GetAllCities() { var context = new EFContext(); var q = from c in context.City orderby c.Name select c; return q; }
gvCities.DataBind();
<asp:GridView ID="gvCities" runat="server" AutoGenerateColumns="False" AllowPaging="True" PageSize="10" ItemType="WebApplication3.City" SelectMethod="GetAllCities"> <Columns> <asp:TemplateField HeaderText="نام"> <ItemTemplate><%#: Item.Name %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
1- startRowIndex: نقطه شروع صفحه بندی را مشخص میکند.
2- maximumRows: تعداد سطرهایی که گرید باید نمایش دهد را مشخص میکند.
3- totalRowCount: این پارامتر باید در تابع مقدار دهی شود (مانند مثال) تا مشخص شود نتیجه Query چند رکورد است و در نهایت گرید تعداد صفحات را بر این اساس نمایش میدهد.
و برای اینکه صفحه بندی را در Query هم لحاظ کنیم از دو تا بع Skip و Take استفاده شده است.
public IQueryable<City> GetAllCities(int startRowIndex, int maximumRows, out int totalRowCount) { var context = new EFContext(); var q = from c in context.City select c; totalRowCount = q.Count(); return q.OrderBy(x=>x.Name).Skip(startRowIndex).Take(maximumRows); }
نکته مهم در این متد IQueryable بودن آن است که باعث واکشی دادهها بصورت صفحه به صفحه میشود.
دستورات SQL تولید شده در پروفایلر:
همانطور که مشاهده میکنید دو دستور SQL تولید شده ، یکی برای بازگرداندن تعداد رکوردها و یکی هم برای واکشی دادهها به اندازه تعداد رکوردهای مجاز در هر صفحه.