目录
ASP.NET Core — 支持服务器发送事件 (SSE)
Blazor — 使用 Blazor Router 添加“未找到”页面
EF Core — 支持 LeftJoin 和 RightJoin 运算符

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。
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文件。
无论您是想分享一个简单的示例、测试某些功能,还是创建一个小型应用程序或服务,现在都可以在一个文件中完成所有操作。

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

被折叠的 条评论
为什么被折叠?



