Я работаю с документацией по API Swagger и хочу, чтобы мое описание перечислений было в документации. Я использую собственный фильтр для извлечения информации из классов и перечислений с помощью аннотаций. например.:
using System.ComponentModel;
...
[DisplayName("Upload class")] // Works fine
public class Upload
{
[DisplayName("Primary Key")] // Works fine
public int UploadMetaId { get; set; }
[Display(Name = "document name")] // Works fine too
public string Title { get; set; }
}
Но у меня проблемы с моими Enums:
[DisplayName("Document types")] // Illegal, gives error CS0592
// or
[Display(Name = "Document types")] // Illegal too, gives also CS0592
public enum UploadType
{
[Display(Name = "Årsopgørelse")] // Works fine
PartnerAarsopgoerelse = 1,
[Display(Name = "Ægtefælles årsopgørelse")]
AegtefaelleAarsopgoerelse = 2
}
Ошибка CS0592 говорит о том, что она недействительна для этого типа объявления.
Итак, что я могу использовать вместо этого?
ОБНОВЛЯТЬ
Я использую следующие пакеты NuGet:
Ответ может зависеть от пакета, генерирующего описание Open API. Swashbuckle, NSwag и т. д.
DisplayAttribute
и DisplayNameAttribute
не работают с типами перечислений, им не хватает AttributeTargets.Enum
в определении AttributeUsage. Learn.microsoft.com/en-us/dotnet/api/… Learn.microsoft.com/en-us/dotnet/api/…Благодаря @ralf и статье в его комментарии я создал решение с помощью собственного IDocumentFilter.
Я надеюсь, что смогу спасти кого-то от большого количества поисков, используя приведенный ниже код.
Я программирую.cs. Я беру XML-документацию из проекта и передаю соответствующую «сводку» в IDocumentFilter:
private static void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(c =>
{
...
Dictionary<string, string> Dict = new Dictionary<string, string>();
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
foreach (var filePath in System.IO.Directory.GetFiles(dir, "*.xml"))
{
c.IncludeXmlComments(filePath);
GetXMLDoc(filePath, Dict);
}
c.DocumentFilter<SwaggerEnumDocumentFilter>(Dict);
});
}
private static void GetXMLDoc(string path, Dictionary<string,string> Dict)
{
var _xmlComments = new XmlDocument();
var reader = XmlReader.Create(path);
_xmlComments.Load(reader);
var xpath = $"//member[@name[starts-with(.,'F')] or @name[starts-with(.,'T')]]";
var nodes = _xmlComments.DocumentElement.SelectNodes(xpath);
foreach(XmlNode node in nodes)
{
var attribute = node.Attributes["name"];
if (attribute != null)
{
string summary = node.FirstChild.InnerText.Trim(' ','\n');
Dict.Add(attribute.Value, summary);
}
}
}
В фильтре подбираю XML-документацию из переданного словаря:
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
Dictionary<string, string> EnumDescription = new Dictionary<string, string>();
public SwaggerEnumDocumentFilter(Dictionary<string, string> enumDescription)
{
EnumDescription = enumDescription;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
// add enum descriptions Schemas section in Swagger
foreach (var property in swaggerDoc.Components.Schemas)
{
var propertyEnums = property.Value.Enum;
if (propertyEnums is { Count: > 0 })
{
property.Value.Description += DescribeEnum(property.Key, false);
}
}
if (swaggerDoc.Paths.Count <= 0)
{
return;
}
//add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values)
{
foreach (var operation in pathItem.Operations)
{
if (operation.Value.Parameters != null)
{
foreach (var param in operation.Value.Parameters)
{
if (param.Schema.Reference != null)
{
param.Description += DescribeEnum(param.Schema.Reference.Id, true);
}
}
}
}
}
}
private Type GetEnumTypeByName(string enumTypeName)
{
if (string.IsNullOrEmpty(enumTypeName))
{
return null;
}
try
{
return AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Single(x => x.FullName != null && x.Name == enumTypeName);
}
catch (InvalidOperationException e)
{
throw new Exception($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
}
}
private string DescribeEnum(string propertyTypeName, bool inApiList)
{
var enumType = GetEnumTypeByName(propertyTypeName);
if (enumType == null)
{
return null;
}
var values = Enum.GetValues(enumType);
if (values == null)
{
return null;
}
string result = "<ul>";
if (inApiList)
{
var key = $"T:{enumType.FullName}";
var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
result = $"<p>{summary}</p><ul>";
}
foreach (var value in values)
{
var key = $"F:{enumType.FullName}.{value}";
var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
string n = EnumUtils.GetDisplayName((System.Enum)value);
result += $"<li>{(int)value} - {n} : {summary} </li>";
}
result += "</ul>";
return result;
}
}
Перечисления в C# не являются классами, они, по сути, помечены целыми числами.