.NET 10 的新特性_探索 C# 14、ASP.NET Core 和 .NET 10 中最有趣的新特性

目录

C# — 扩展成员

C# — 空条件赋值

C# — 表达式树中的命名参数和可选参数

C# — 复合赋值运算符

ASP.NET Core — 以最小 API 实现验证支持

ASP.NET Core — 支持服务器发送事件 (SSE)

Blazor — 使用 Blazor Router 添加“未找到”页面

EF Core — 支持 LeftJoin 和 RightJoin 运算符

EF Core — JSON 类型

.NET — 基于文件的应用程序


如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

C# — 扩展成员

C# 14 中最大的更新可能就是扩展成员了。

在之前的版本中,C# 已经支持扩展,但仅限于方法。现在,我们可以轻松地为类型添加属性、方法、事件,甚至引入运算符(除了 ` implicit explicit ` 运算符)。

假设你正在使用以下类:

public sealed class Result<TValue, TError>
{
private readonly TValue? _value;
private readonly TError? _error;

private Result(TError error)
{
_error = error;
}

private Result(TValue value)
{
_value = value;
}

public bool IsSuccess => _error == null;

public TValue? Value => _value ?? throw new InvalidOperationException();

public static Result<TValue, TError> Success(TValue value) => new(value);

public static Result<TValue, TError> Error(TError error) => new(error);
}

这个类几乎包含了你所需的一切,但你可能会注意到,你经常需要编写以下几行代码:

if(!result.IsSuccess)
{
// 执行某些操作
}

var resultOfNull = result.IsSuccess ? result.Value : null;

现在您可以轻松地将其提取到扩展成员中。

创建一个名为 `class` 的类ResultExtensions,并在其中添加以下内容:

public static class ResultExtensions
{
extension<TValue, TError>(Result<TValue, TError> result)
{
public bool IsError => !result.IsSuccess;//<-- 这是一个属性!

public TValue? GetValueOrDefault() => result.IsSuccess
? result.Value
: default;
}
}

更棒的是——现在你还可以定义静态成员:

public sealed class NotFound;

// 带有静态成员的扩展类
public static class ResultExtensions
{
extension<TValue, TError>(Result<TValue, TError> _)
{
public static Result<TValue, NotFound> NotFound() =>
Result<TValue, NotFound>.Error(new NotFound());
}
}

C# — 空条件赋值

此功能引入了一种更简洁的方式来为空引用赋值。

假设你有类似这样的代码:

public sealed class Account
{
public decimal Balance { get; set; }
}

public sealed class PaymentProcessor
{
public void Payout(Account? account, decimal amount)
{
if(account != null)
{
account.Balance -= amount;
}
}
}

你可以Payout按如下方式简化该方法:

public sealed class Account
{
public decimal Balance { get; set; }
}

public sealed class PaymentProcessor
{
public void Payout(Account? account, decimal amount)
{
account?.Balance -= amount;
}
}

如上所示,除了赋值之外,你还可以将空条件成员访问运算符与复合赋值运算符(+=-=)一起使用。

C# — 表达式树中的命名参数和可选参数

从现在起,表达式树将不再有任何限制,您可以放心使用以下代码而不会出现任何错误:

public static TValue? GetValueOrDefault<TValue, TError>(this Result<TValue, TError> result, TValue? defaultValue = default)
{
if (result.IsSuccess)
{
return result.Value;
}

return defaultValue is not null
? defaultValue
: default;
}

Expression<Func<Result<TValue, TError>, TValue, TValue>> getOrSetValue;

getOrSetValue = (result, defaultValue) => result.GetValueOrDefault();//<-- 可选参数
getOrSetValue = (result, defaultValue) => result.GetValueOrDefault(defaultValue: defaultValue);//<-- 命名参数

C# — 复合赋值运算符

现在你也可以创建自己的复合赋值运算符,例如+=and -=

假设你有类似这样的内容:

public sealed class Counter(int value)
{
public int Value { get; private set; } = value;
}

要添加对两个计数器求和的功能,需要显式地将该运算符添加到类中。

类似这样的:

public static Counter operator +(Counter counter, Counter counter1)
{
var currentValue = counter.Value;

currentValue += counter1.Value;

return new (currentValue);
}

但是,在这种情况下,必须创建一个新对象。

使用复合赋值运算符可以避免这种情况,你可以这样编写代码:

public void operator +=(int value)
{
Value += value;
}

ASP.NET Core — 以最小 API 实现验证支持

DataAnnotation提供了一种简单直观的方法来验证客户输入的信息。

现在,它们也以极简 API 的形式提供。

要启用验证,只需将以下代码添加到依赖注入容器中:

builder.Services.AddValidation();

InterceptorsNamespaces 然后在项目中按如下方式设置该属性:

<PropertyGroup> 
  <InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.Http.Validation.Generated</InterceptorsNamespaces> 
</PropertyGroup>
就这样!

现在你可以向类中添加数据注解,验证功能将开箱即用。

// 在需要
公开密封记录的地方添加验证属性 CreateNoteRequest([Required] [MaxLength(256)] string Value);
此外,从现在开始,您可以使用顶级验证。这意味着您可以轻松地在记录上添加属性并验证整个类:

[SumLimit(42)]
记录点(int X, int Y);
您还可以通过向特定类添加SkipValidation属性来轻松跳过该类的验证:

public class Order 

    public Address PaymentAddress { get; set; } 

    [SkipValidation] 
    public Address ContactAddress { get; set; } 
}
要跳过端点的验证,可以使用以下扩展方法:

app.MapPost("/v4/notes", (CreateNoteRequest request) => 

    return Results.Ok(); 
}) 
.DisableValidation();//<- 此方法禁用端点的验证

ASP.NET Core — 支持服务器发送事件 (SSE)

ASP.NET Core 现在支持使用 API 返回服务器发送事件 (SSE) TypedResults.ServerSentEvents

一般来说,SSE 允许服务器在没有被明确请求的情况下向客户端发送响应——可以把它看作是反向轮询。

在服务器端,您可能会有类似这样的端点:

app.MapGet("/notifications", ( 
    [FromServices] IRandomizerString randomizer, 
    CancellationToken token) => 

    return TypedResults.ServerSentEvents(GetNotificationsAsync(randomizer, token), "notification"); 

    async IAsyncEnumerable<NotificationEvent> GetNotificationsAsync( 
        IRandomizerString randomizerString, 
        [EnumeratorCancellation] CancellationToken cancellationToken) 
    { 
        while (!cancellationToken.IsCancellationRequested) 
        { 
            await Task.Delay(4000, cancellationToken); 

            yield return new NotificationEvent( 
                Guid.CreateVersion7(), 
                randomizerString.Generate()!); 
        } 
    } 
});
客户端只需连接到该端点即可:

// 你应该使用你自己的源,而不是我的本地源
const eventSource = new EventSource('https://localhost:7197/notifications'); 

eventSource.onmessage = (event) => { 
    console.log('收到事件:', event); 
    // 处理事件
};
就这样——不需要再编写其他代码了。

Blazor — 使用 Blazor Router 添加“未找到”页面

在此功能推出之前,您必须直接在组件内部指定NotFoundRoutes页面,这使得您的路由组件更加杂乱:

<Router AppAssembly="@typeof(Program).Assembly"> 
    <Found Context="routeData"> 
        <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" /> 
        <FocusOnNavigate RouteData="@routeData" Selector="h1" /> 
    </Found> 
    <NotFound>页面未找到。</NotFound> /* <-- 未找到页面的内容 */ 
</Router>
但从现在开始,您可以使用单独的页面来做这件事:

<Router AppAssembly="typeof(Program).Assembly" 
        NotFoundPage="typeof(Pages.NotFound)">/*<-- 新增 NotFoundPage 标签 */ 
    <Found Context="routeData"> 
        <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" /> 
        <FocusOnNavigate RouteData="routeData" Selector="h1" /> 
    </Found> 
</Router>
通过指定NotFoundPage组件的属性Router。

EF Core — 支持 LeftJoin 和 RightJoin 运算符

EF 和 EF Core 早就支持左连接和右连接了,但它们实现的方式相当笨拙:

var query = students.GroupJoin 
    ( 
        departments, 
        student => student.DepartmentID, 
        department => department.ID, 
        (student, departmentList) => new { student, subgroup = departmentList }). 
    SelectMany( 
        joinedSet => joinedSet.subgroup.DefaultIfEmpty(), 
        (student, department) => new 
        { 
            student.student.FirstName, 
            student.student.LastName, 
            Department = department.Name ?? "" "[NONE]" 
        });
这就是为什么我更喜欢使用查询语法而不是lambda 表达式的原因:

var query = 
    from s in students 
    join d in departments on s.DepartmentID equals d.ID into deptGroup 
    from d in deptGroup.DefaultIfEmpty() 
    select new 
    { 
        s.FirstName, 
        s.LastName, 
        Department = d != null ? d.Name : "" NONE" 
    };
然而,从现在开始,EF Core 支持LeftJoin扩展RightJoin,这使得代码更加清晰易懂:

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

EF Core — JSON 类型

你曾多少次将 JSON 数据存储到 SQL 数据库中?

你曾多少次费尽心思从中寻找东西——这相当困难,对吧?

现在,EF Core 完全支持新的JSON 数据类型。

现在可以使用新ToJson()方法将对象存储为 JSON:

public class Blog 

    public int Id { get; set; } 

    public string[] Tags { get; set; } 
    public required BlogDetails Details { get; set; } 

public class BlogDetails 

    public string? Description { get; set; } 
    public int Viewers { get; set; } 

protected override void OnModelCreating(ModelBuilder modelBuilder) 

    modelBuilder.Entity<Blog>() 
        .ComplexProperty(b => b.Details, b => b.ToJson());//<-- 一个新方法
}
这能给你带来什么?

现在可以使用 LINQ 查询存储的 JSON 数据。例如,以下查询会筛选出浏览量超过三个的博客:

var highlyViewedBlogs = await context.Blogs.Where(b => b.Details.Viewers > 3).ToListAsync();

.NET — 基于文件的应用程序

现在您可以将整个应用程序存储在单个*.cs文件中,并且无需相应的项目(*.csproj)文件即可构建或运行它。

例如,假设你有这样一些东西:

Console.WriteLine("Hello, world");
您可以使用以下命令运行此应用程序:

dotnet run app.cs
就是这样——不再.csproj需要多个.sln文件。
无论您是想分享一个简单的示例、测试某些功能,还是创建一个小型应用程序或服务,现在都可以在一个文件中完成所有操作。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hefeng_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值