ویرگول
ورودثبت نام
احسان پژومان
احسان پژوماندانشجوی مهندسی کامپیوتر و علاقه مند به مهندسی نرم افزار
احسان پژومان
احسان پژومان
خواندن ۷ دقیقه·۱ ماه پیش

کنفرانس دات نت ۲۰۲۵: از دات نت ۱۰ تا قابلیت‌های جدید C# ۱۴ و EF Core ۱۰

کنفرانس دات نت 2025 یکی از مهم‌ترین رویدادهای سال برای توسعه‌دهندگان و علاقه‌مندان دنیای مایکروسافت بود؛ رویدادی که مثل همیشه به‌صورت رایگان و کاملاً آنلاین برگزار شد و فرصتی فراهم کرد تا جامعه جهانی توسعه‌دهندگان با آخرین دستاوردها، قابلیت‌ها و مسیر آینده اکوسیستم دات نت آشنا شوند.
در این رویداد، نسخه جدید دات نت و Visual Studio،قابلیت‌های تازه C# و ابزارهای مرتبط با هوش مصنوعی، همگی با رویکردی عملی و آینده‌محور معرفی شدند. هدف این مقاله، مرور دقیق و خلاصه‌وار مهم‌ترین نکات، ویژگی‌ها و اتفاقاتی است که در کنفرانس دات نت 2025 مطرح شد؛ نکاتی که می‌توانند مسیر توسعه نرم‌افزار را در سال‌های پیش‌رو تحت تأثیر قرار دهند.


تغییرات مهم ASP.NET Core 10

اعتبارسنجی در minimal api

اپلیکیشن های minimal api به دلیل سبک و سریع بودن همیشه محبوب برنامه نویسان بوده است اما بزرگترین ایراد آن پشتیبانی نکردن از کتابخانه های دات نت برای اعتبارسنجی بود که کدنویسی را بسیار طولانی و طاقت فرسا میکرد.

app.MapPost("/create", async (Employee employee) => { // ❌ تمام اعتبارسنجی داده ها باید به صورت دستی انجام میشد var errors = new List<string>(); if (string.IsNullOrWhiteSpace(employee.FirstName)) errors.Add("FirstName is required."); if (string.IsNullOrWhiteSpace(employee.LastName)) errors.Add("LastName is required."); if (errors.Any()) return Results.BadRequest(errors); return Results.Ok(employee); });

اما اکنون از کتابخانه های اعتبارسنجی دات نت مثل DataAnnotations،AbstractValidator و ... میتوانید در اپلیکیشن های minimal api استفاده کنید.

public class Employee { [Required] [StringLength(50)] public string FirstName; [Required] [StringLength(50)] public string LastName; }
builder.Services.AddValidation();
app.MapPost("/create", (Employee employee) => { // ✔ No manual checks needed // If model is invalid, .NET automatically returns 400 BAD REQUEST return Results.Ok(employee); });

پشتیبانی بهبود یافته از رویداد های ارسالی سمت سرور (SSE)

رویدادهای ارسالی از سرور (server-side events) یا اختصارا SSE یک روش ارتباطی یک طرفه و بی‌درنگ است که در آن سرور داده ها را از طریق یک اتصال HTTP طولانی‌مدت به سمت کلاینت ارسال میکند.

سوال :چرا به SSE نیاز داریم؟

به طور نرمال، مرورگر معمولا اینگونه عمل میکند:

  • کاربر : "سرور؛ داده جدیدی دریافت کردی؟"

  • سرور :"نه"

  • کاربر : "سرور؛ داده جدیدی دریافت کردی؟"

  • سرور :"نه"

به این فرایند polling گفته میشود؛ polling باعث اتلاف پهنای باند شبکه، CPU و منابع سرور میشود.

سرویس SSE با اجازه دادن به سرور برای ارسال خودکار به‌روزرسانی‌ها این مشکل را حل کرده است.

دات نت 10 یک api جدید داخلی برای SSE معرفی کرده است:

TypedResults.ServerSentEvents(...)

که ارسال جریان داده های لحظه‌ای را بسیار آسان تر کرده است.

در اینجا به طور مثال یک سرویس فرضی نوشته ایم که قیمت سهام را هر 2 ثانیه بروزرسانی میکند:

public record StockPriceEvent(string Id, string Symbol, decimal Price, DateTime Timestamp); public class StockService { public async IAsyncEnumerable<StockPriceEvent> GenerateStockPrices( [EnumeratorCancellation] CancellationToken cancellationToken) { var symbols = new[] { "MSFT", "AAPL", "GOOG", "AMZN" }; while (!cancellationToken.IsCancellationRequested) { var symbol = symbols[Random.Shared.Next(symbols.Length)]; var price = Math.Round((decimal)(100 + Random.Shared.NextDouble() * 50), 2); var id = DateTime.UtcNow.ToString("o"); yield return new StockPriceEvent(id, symbol, price, DateTime.UtcNow); await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); } } }

برای ایجاد یک endpoint که جریان داده‌های آپدیت شده را برگرداند از TypedResults.ServerSentEvents استفاده میکنیم :

builder.Services.AddSingleton<StockService>(); app.MapGet("/stocks", (StockService stockService, CancellationToken ct) => { return TypedResults.ServerSentEvents( stockService.GenerateStockPrices(ct), eventType: "stockUpdate" ); });

اکنون کاربران با اتصال به این endpoint میتوانند به صورت بلادرنگ بروزرسانی قیمت های سهام را دریافت کنند.

و نمونه خروجی به صورت زیر میشود:

event: stockUpdate data: {"Id":"2025-11-25T12:00:01.1234567Z","Symbol":"AAPL","Price":137.45,"Timestamp":"2025-11-25T12:00:01.1234567Z"} event: stockUpdate data: {"Id":"2025-11-25T12:00:03.1234567Z","Symbol":"GOOG","Price":145.12,"Timestamp":"2025-11-25T12:00:03.1234567Z"} event: stockUpdate data: {"Id":"2025-11-25T12:00:05.1234567Z","Symbol":"AMZN","Price":121.78,"Timestamp":"2025-11-25T12:00:05.1234567Z"} event: stockUpdate data: {"Id":"2025-11-25T12:00:07.1234567Z","Symbol":"MSFT","Price":152.33,"Timestamp":"2025-11-25T12:00:07.1234567Z"}

ویژگی‌های جدید C# 14

قابلیت Extension members

Extension member ها نسخه پیشرفته تر و بهبود یافته تر متدهای extension هستند که پیشتر در نسخه سوم سی شارپ معرفی شدند. در سی شارپ 14 شما نه تنها میتوانید توابع را گسترش دهید؛ بلکه میتوانید یک نوع(type) را با متدها، ویژگی‌ها(properties) و اعضای استاتیک گسترش دهید؛ بدون اینکه کد اصلی را تغییر دهید.

قبل از سی شارپ 14، فقط میتوانستید متدها را با استفاده از کلاس های static و کلیدواژه this اضافه کنید:

public static class StringExtensions { public static bool IsNullOrEmpty(this string value) { return string.IsNullOrEmpty(value); } public static string Truncate(this string value, int maxLength) { if (string.IsNullOrEmpty(value) || value.Length <= maxLength) return value; return value.Substring(0, maxLength); } }
string name = "Hello World"; if (!name.IsNullOrEmpty()) { Console.WriteLine(name.Truncate(5)); // Hello }

در سینتکس جدید سی شارپ 14، میتوانید از بلوک های extension که خواناتر و سازمان‌یافته تر است استفاده کنید.در این روش یک بار نوع دریافت کننده(receiver) را مشخص کنید سپس چندین عضو را داخل همان بلوک تعریف کنید:

public static class StringExtensions { extension(string value) { public bool IsNullOrEmpty() { return string.IsNullOrEmpty(value); } public string Truncate(int maxLength) { if (string.IsNullOrEmpty(value) || value.Length <= maxLength) return value; return value.Substring(0, maxLength); } } }
  • داخل بلوک، value به نمونه ای که متد روی آن اعمال میشود اشاره دارد.

class Program { static void Main() { string name = "Ehsan"; bool isEmpty = name.IsNullOrEmpty(); // extension member string shortName = name.Truncate(3); // extension member Console.WriteLine(isEmpty); // False Console.WriteLine(shortName); // "Ehs" } }

در مورد بعد، میتوانید ویژگی‌ها(properties) و اعضای استاتیک نیز داخل بلوک اضافه کنید.properties باعث میشوند کد شما خواناتر و گویاتر باشد.

مثلا: برای بررسی اینکه یک مجموعه خالی است یا نه:

public static class CollectionExtensions { extension<T>(IEnumerable<T> source) { public bool IsEmpty => !source.Any(); public bool HasItems => source.Any(); public int Count => source.Count(); } }
IEnumerable<int> numbers = new List<int>(); if (numbers.IsEmpty) { Console.WriteLine("هیچ موردی یافت نشد."); }

در اینجا یک کلاس توسعه دهنده برای Product نوشتیم و اعضای استاتیک را داخل بلوک تعریف کردیم:

public static class ProductExtensions { extension(Product) { public static Product CreateDefault() => new Product { Name = "Unnamed Product", Price = 0, StockQuantity = 0, Category = "Uncategorized", CreatedDate = DateTime.UtcNow }; public static bool IsValidPrice(decimal price) => price >= 0 && price <= 1000000; public static string DefaultCategory => "General"; } }
var product = Product.CreateDefault(); if (Product.IsValidPrice(999.99m)) { product.Price = 999.99m; } Console.WriteLine(Product.DefaultCategory); //"General"
  • این اکستنشن ها بدون تغییر کلاس اصلی، قابلیت های جدیدی به ما میدهند.

و در مورد آخر، داخل بلوک میتوانید با تعریف فیلد private از آن برای کش کردن محاسبات سنگین استفاده کنید:

public static class CollectionExtensions { extension<T>(IEnumerable<T> source) { private List<T>? _materializedList; public List<T> MaterializedList => _materializedList ??= source.ToList(); public bool IsEmpty => MaterializedList.Count == 0; public T FirstItem => MaterializedList[0]; } }

تابع source.ToList صرفا یک بار اجرا میشود و مقدار آن در materializedList_ کش میشود. این کار سبب میشود زمان اجرای توابعی مثل IsEmpty و FirstItem به طرز چشمگیری کاهش پیدا کند.

بهبود عملگرهای کنترلی Null

در نسخه جدید عملگرهای کنترلی Null (?. یا [ ]?) اکنون میتوانند در سمت چپ یک assignment استفاده شوند.این یعنی می‌توانیم به یک property، field یا indexer (اگر شیء null نباشد) مقدار بدهیم ، بدون اینکه نیاز باشد شرط جداگانه برای چک کردن null بنویسیم.

در نسخه های پیشین، باید همیشه null بودن شیء را چک می‌کردید:

if (user is not null) { user.Profile = LoadProfile(); }

در سینتکس نسخه جدید :

user?.Profile = LoadProfile();
  • مقدار LoadProfile فقط در صورتی assign میکند که user نال نباشد.

  • اگر user نال باشد هیچ کاری انجام نمیشود.

  • این کار سبب خوانایی بیشتر کد و عدم تکرار کدهای تکراری میشود


تغییرات EF Core 10

پشتیبانی مستقیم از RightJoin و LeftJoin

با پشتیبانی مستقیم LeftJoin و RightJoin در LINQ، کوئری ها ساده‌تر، تمیزتر و شبیه تر به زبان SQL شده اند.

روش قدیمی(برای مثال LeftJoin):

var query = students .GroupJoin( departments, s => s.DepartmentID, d => d.ID, (s, dList) => new { s, dList }) .SelectMany( x => x.dList.DefaultIfEmpty(), (x, d) => new { x.s.FirstName, Department = d != null ? d.Name : "[NONE]" });

در نسخه جدید، میتوانید مستقیما از LeftJoin استفاده کنید:

var query = students.LeftJoin( departments, student => student.DepartmentID, department => department.ID, (student, department) => new { student.FirstName, student.LastName, Department = department?.Name ?? "[NONE]" });

پشتیبانی کامل از تایپ JSON

شاید مهم ترین تغییری که در EF Core 10 ایجاد شد، همین باشد. این ویژگی به توسعه‌دهندگان اجازه می‌دهد تا انواع مدل های پیچیده را مستقیماً به ستون‌های JSON نگاشت کنند، از طریق LINQ به پراپرتی های داخل ستون کوئری بزنند و فیلدهای JSON را به طور کارآمد به‌روزرسانی کنند.

اکنون با استفاده از EF Core 10 و SQL Server 2025 میتوانید ستون های تایپ JSON را تعریف کنید. در نسخه های پیشین برای ذخیره دیتاهای JSON باید آن را در ستون هایی با تایپ nvarchar(max) یا بقیه ستون های متنی ذخیره میکردید.

public class Blog { public int Id { get; set; } public string Title { get; set; } public BlogDetails Details { get; set; } // Complex type stored as JSON } [Owned] public class BlogDetails { public int Views { get; set; } public string[] Tags { get; set; } }
modelBuilder.Entity<Blog>(b => { b.OwnsOne(blog => blog.Details, a => a.ToJson()); });

در نسخه جدید این امکان نیز فراهم شده که به ویژگی های داخل ستون JSON مستقیما کوئری بزنید:

var popularBlogs = context.Blogs .Where(b => b.Details.Views > 100) .ToList();

در مجموع، تغییرات دات نت 10 و سی شارپ 14، کدنویسی را هم ساده‌تر و هم خواناتر کرده است و امکانات جدید EF Core 10 توسعه‌دهندگان را قادر می‌سازد با داده‌های پیچیده راحت‌تر کار کنند. این ویژگی‌ها نه تنها روند توسعه نرم‌افزار را سریع‌تر می‌کنند، بلکه چشم‌انداز روشنی برای آینده برنامه‌نویسی در دنیای دات نت ایجاد می‌کنند.

دات نتسی شارپمایکروسافتبرنامه نویسی
۰
۰
احسان پژومان
احسان پژومان
دانشجوی مهندسی کامپیوتر و علاقه مند به مهندسی نرم افزار
شاید از این پست‌ها خوشتان بیاید