使用构造函数大摇大摆地设置属性

Use constructor to set properties in swagger

我正在构建一个 API 并使用 swagger 来测试端点。我有一个 ProductDTO:

public string ProductName { get; set; }
.
.
.
public Price Price { get; set; }

在此 DTO 中,我想使用我的代码中使用的价格 class。价格 class 看起来像这样:

public class Price
{
    public Price(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public decimal Amount { get; private set; }
    public string Currency { get; private set; }
}

但是由于在 Price class 中使用了私有设置器,我无​​法使用 swagger 设置这些值(它在这些值上具有 readonly 属性)。我真的很喜欢这种拥有私有 setter 并使用构造函数设置值的方法,顺便说一下,它是 public。有什么方法可以使用 swagger 设置 Price class 的值,并且仍然在属性上使用私有设置器?

更新: 我最初的回答(见下文)是不可能的,但是,这实际上取决于项目中使用的序列化库。

例如Newtonsoft的Json.NET allows you to set some of your class' properties via constructor (note: in case your class comes with more than one constructor, apply the JsonConstructorAttribute):

public class Price
{
    [JsonConstructor]
    public Price(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public decimal Amount { get; private set; }
    public string Currency { get; private set; }
}

原回答

不,这是不可能的。因为如果您将您的属性保密并仅通过构造函数初始化它们,您的反序列化器将不知道这些属性应该如何映射到您的后端 (DTO) 模型。

因此,在使用 DTO 时,您通常不会看到有人通过构造函数进行初始化。

此外,由于您在整个代码中都使用价格 class,因此您将 "domain model" 与 "view model"(= 您用来与客户沟通的模型)混合在一起– 这是一种务实的方法,但不提倡像 DDD 这样的风格。在这种情况下,如果您想要在您的属性上使用不同的属性或不同的访问修饰符,您应该创建一个专用的 PriceDTO 映射到您的 Price 实体,但具有 public setter 和 getter。

另一种选择是将发送数据到客户端(通过 GET)的端点使用的模型与接收数据作为有效负载以创建或更新事物的端点使用的模型分开(通过 POST/PATCH/PUT).然而,这通常是以冗余为代价的,因为两种情况下的模型通常高度相似。

添加这个架构过滤器对我有用。

public class AddReadOnlyPropertiesFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (schema?.Properties?.Count > 0)
            schema.Properties.Values.SingleOrDefault(v => v.ReadOnly = false);
    }
}