如何处理类型化域模型中的可选数据?



在开发应用程序/领域模型时,我想知道处理不完整或可选数据的最佳方法是什么。TypeScriptC#等类型化语言为我们提供了键入模型的能力。有时这很强大,因为它对我们的模型施加了约束,并且可以强制它是整数。 但是,通常,现实生活中的数据是不完整的,这似乎大大降低了键入约束的优势。

想象一下,在一个名为Project的实体的示例应用程序(TypeScript前端和一些后端(中,一个简单的数据模型具有ID名称和可选描述。数据是从后端检索的。

数据模型在前端使用接口定义:

export interface IProject {
id: number;
name: string;
description: string;
}

数据从后端检索,如下所示:

export class ProjectService {
public getProject(projectId: number): Observable<IProject> {
const url = 'http://server/api/project/' + projectId;
return this.httpClient.get<IProject>(url);
}
}

实际具有描述的项目的示例响应。

{
"id": 1
"name": "My first project",
"description": "My first project is just a demo"
}

在前端应用程序中,我们显示检索到的数据。例如,让我们显示项目描述的前 10 个字符。

alert(project.description.substr(0,10));

到目前为止,一切都很好。

但是想象一下,我们的用户在没有填写可选描述的情况下创建了"项目二"。现在,后端可以响应:

{
"id": 2
"name": "Project two"
}

{
"id": 2
"name": "Project two",
"description" : null
}

现在我们在前端得到一个空引用异常:无法读取 null 的属性 'substr'。 当然,可以添加一个 if 语句来检查描述是否为 null:

if(project.description) {
alert(project.description.substr(0,10));
}

这有效,但这意味着在整个应用程序中添加空检查。整个代码库将充满这些检查,以及掩盖错误而不是防止错误的危险。这对我来说是不对的。

一个可能的解决方案是始终返回描述,因此如果未填充任何描述,则返回空字符串。

{
"id": 2
"name": "Project two",
"description": ""
}

现在前端不再需要对描述进行空检查,但不再可能区分显式填充的空描述和尚未填写的描述。

处理这类问题的最佳方法是什么?上面的例子只是为了说明,范围非常通用。它似乎困扰着所有类型语言,其中定义了无法始终实现的类型化合约/接口。

在 C# 8 中,可以使用可为 null 的引用类型将description字段显式标记为可选1

class Project
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}

在这里,我们将Description声明为string?- 一个可能为空的字符串。然后,每当我们尝试在Description上调用一个方法时,首先检查它是否null,编译器会给我们一个警告。

C# 还为您提供了使用null的语法。例如,如果要获取描述的前 10 个字符,但如果描述null则返回空字符串,则可以编写:

project.Description?.Substring(0, 10) ?? "";

(请注意,如果您的描述不为 null,但长度少于 10 个字符,则会抛出此值,因此在实践中您需要一些额外的逻辑(。

看起来打字稿也有类似的概念。


1此类会给你一个警告,因为Name不可为空,但它也没有初始化。有几种方法可以解决这个问题:给它一个默认值"";给Project一个设置Name的构造函数;在通过分配null!对类型进行反序列化之前,在短时间内强制中断不可为空的协定;其他一些。

相关内容

  • 没有找到相关文章

最新更新