简体   繁体   English

NSwag 中接口上鉴别器的 KnownType 等价物

[英]KnownType equivalent for discriminator on Interfaces in NSwag

When we need to tell NSwag and the generated OpenAPI 3.0 document that an abstract class should be converted to some concrete type on the client side, I use [KnownType] with a discriminator and that works really well:当我们需要告诉 NSwag 和生成的 OpenAPI 3.0 文档,抽象的 class 应该在客户端转换为某种具体类型时,我使用[KnownType]和鉴别器并且效果很好:

using Newtonsoft.Json;
using NJsonSchema.Converters;

namespace Xyz.common.objects
{
    [JsonConverter(typeof(JsonInheritanceConverter), "discriminator")]
    [KnownType(typeof(DeviceTemplate))]
    [KnownType(typeof(PersonelTemplate))]
    public abstract class AbstractResourceTemplate
    {
    }
}

The problem I've run into is that [KnownType] cannot be used on an interface.我遇到的问题是[KnownType]不能在接口上使用。 So this fails to compile:所以这无法编译:

using Newtonsoft.Json;
using NJsonSchema.Converters;

namespace Xyz.common.objects
{
    [JsonConverter(typeof(JsonInheritanceConverter), "discriminator")]
    [KnownType(typeof(DeviceTemplate))] // Can't put KnownType on interfaces
    [KnownType(typeof(PersonelTemplate))]
    public interface IResourceTemplate
    {
    }
}

Is there equivalent code that can be placed on the interface to let NSwag know what the discriminating objects should be?是否有等效的代码可以放在界面上让 NSwag 知道区分对象应该是什么?

For context, this is how the interface is used:对于上下文,这是接口的使用方式:

public class DeviceTemplateReponse {
    // In this class, Template is always going to be a DeviceTemplate, if that helps.
    [WhatGoesHere???(typeof(DeviceTemplate))]
    public IResourceTemplate Template { get; set; }
}

I imagine there is a way to inform NSwag that Template is always going to be the concrete type DeviceTemplate when in DeviceTemplateReponse, so don't use IResourceTemplate in the OpenAPI docuement.我想有一种方法可以通知 NSwag 在 DeviceTemplateReponse 中,Template 始终是具体类型 DeviceTemplate,因此不要在 OpenAPI 文档中使用 IResourceTemplate。 This is important because otherwise I get this error on the client side:这很重要,否则我会在客户端收到此错误:

The abstract class 'IResourceTemplate' cannot be instantiated.无法实例化抽象 class 'IResourceTemplate'。

Also, it seems like a TypeMapper would help here but I'm not able to find documentation or examples on how something like that would be implemented here.此外,似乎TypeMapper在这里会有所帮助,但我无法找到有关如何在此处实现类似内容的文档或示例。

Some workarounds I found is to convert the interfaces to abstract classes, or to write my own versions of objects that only use concrete classes.我发现的一些解决方法是将接口转换为抽象类,或者编写我自己的仅使用具体类的对象版本。 But I'd really rather not be messing with code like that if I don't have to.但如果不是必须的话,我真的宁愿不弄乱这样的代码。

The problem with interfaces is that a single type can implement multiple interfaces, but the type itself will still implement a single common base.接口的问题在于单个类型可以实现多个接口,但类型本身仍将实现一个公共基础。 What is more, we can explicitly overload interface properties and expose multiple values for the same property.更重要的是,我们可以显式重载接口属性并为同一属性公开多个值。

At the client side, if we allowed this, what are the default values for the fields that are not defined in the interface?在客户端,如果我们允许这样做,那么接口中未定义的字段的默认值是多少? Is the serializer supposed to allow all metadata through other end for the base type, or should it be masked and restricted to only the properties that are defined on the interface.序列化器是否应该允许所有元数据通过基类型的另一端,或者它应该被屏蔽并仅限于接口上定义的属性。 When a server side type has explicit properties for the interface, that have different values, which value do you want to send to the client, which one should be sent?当服务器端类型具有接口的显式属性时,具有不同的值,您希望将哪个值发送给客户端,应该发送哪个值?

public interface ITest1
{
    int Test { get;set; }
}
public interface ITest2
{
    int Test { get;set; }
}
public class TestCase : ITest1, ITest2
{
    public int Test { get;set; } = 0;
    int ITest1.Test { get;set; } = 1;
    int ITest2.Test { get;set; } = 2;
}

In NSwag, polymorphism is simplified and managed through direct inheritance, not through composition of interfaces.在 NSwag 中,多态性是通过直接 inheritance 来简化和管理的,而不是通过接口的组合。 See NJsonSchema - Inheritance and the associated PRs, internally only direct base types are inspected in the current implementation, so your attributes on the interface definition are ignored completely.请参阅NJsonSchema - Inheritance和相关的 PR,在当前实现中仅在内部检查直接基类型,因此您在接口定义上的属性将被完全忽略。

It hasn't come up often for me, out of habit I try to map all my API endpoints to specific DTOs for that resource and have different endpoints for different structures if they are needed, any aggregate endpoints deliberately use direct inheritance mainly because that is a requirement of other frameworks I use like EF and OData.它对我来说并不经常出现,出于习惯,我尝试 map 我所有的 API 端点到该资源的特定 DTO,并且如果需要的话,不同结构有不同的端点,任何聚合端点故意使用直接 inheritance 主要是因为那是我使用的其他框架(如 EF 和 OData)的要求。

When it does come up, I just create an abstract class that implements the interface, use it for this endpoint then in the logic we can still cast it back, it then becomes a very minimal intervention.当它确实出现时,我只是创建一个实现接口的抽象 class,将它用于此端点,然后在逻辑中我们仍然可以将其转换回去,然后它变成了一个非常小的干预。

using Newtonsoft.Json;
using NJsonSchema.Converters;

namespace Xyz.common.objects
{
    public interface IResourceTemplate
    {
    }

    [JsonConverter(typeof(JsonInheritanceConverter), "discriminator")]
    [KnownType(typeof(DeviceTemplate))]
    [KnownType(typeof(PersonelTemplate))]
    public abstract class ResourceTemplateBase : IResourceTemplate
    {
    }
}
public class DeviceTemplateReponse {
    [KnownType(typeof(DeviceTemplate))]
    public ResourceTemplateBase Template { get; set; }
}

Is this annoying, well yes, when it comes up... But you can use implicit type casting or an explicit helper method or tools like AutoMapper is it comes up enough to simplify setting your logical instances into the Template property of the response, should be very minimal overheads to your codebase and you only write this once.这很烦人吗,嗯,是的,当它出现时......但是你可以使用隐式类型转换或显式辅助方法或像 AutoMapper 这样的工具它是否足以简化将逻辑实例设置到响应的Template属性中,应该对您的代码库的开销非常小,您只需编写一次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM