【问题标题】:Is there an annotation for data transformations in DTOs?DTO 中是否有用于数据转换的注释?
【发布时间】:2020-10-15 09:24:26
【问题描述】:

我想创建一个 .NET Core REST API 作为两个系统之间的代理。接收系统接收特定值,但发送系统发送不同变化的值。

鉴于以下示例,接收系统需要以下类型为string 的性别键

  • 男性
  • 女性
  • 多样化
  • 未定义

发送系统可能会发送“男性”的变体,例如“米”。如果 DTO 中的值为“m”,我想将其转换为“male”。如果键不存在,它应该简单地返回一个 400。我知道我可以创建验证属性,但我也可以创建转换属性吗?

也许我可以直接转换属性中的DTO值?

这是我当前的示例,展示了我想要实现的目标

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class MyValidationAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value != null)
        {
            string key = value.ToString();

            switch (key)
            {
                case "male":
                case "m":
                    key = "male"; // Transform the value from the DTO here
                    break;
                case "female":
                case "f":
                    key = "female"; // ...
                    break;
                
                // ...

                default:
                    return false; // Throw 400 because the value didn't match
            }
        }

        return false;
    }

    public override string FormatErrorMessage(string name) => "... Invalid ...";
}

【问题讨论】:

    标签: c# .net-core asp.net-core-webapi


    【解决方案1】:

    您可以在 DTO 中为您的字段创建自定义转换器。假设您的代码中有某种性别枚举,您可以引入自定义属性来为您的枚举值争取可能的值。如DescriptionsAttribute

    using System;
    
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
    public class DescriptionsAttribute : Attribute
    {
        public DescriptionsAttribute(params string[] values)
        {
            Values = values ?? throw new ArgumentNullException(nameof(values));
        }
    
        public string[] Values { get; }
    }
    

    此属性实现将允许您在枚举中为每个值添加一对多的描述。然后,您可以使用此自定义属性来描述 Gender 枚举中的字段。我故意将Undefined 值保留为没有任何描述,因为我打算稍后将其用作备用值,但您可以根据需要进行更改。

    public enum Gender
    {
        [Descriptions("m", "male")]
        Male,
    
        [Descriptions("f", "female")]
        Female,
    
        [Descriptions("d", "diverse")]
        Diverse,
    
        Undefined
    }
    

    您还需要某种从枚举字段中提取DescriptionsAttribute 值的方法。我已经将该逻辑封装在一个扩展方法中。

    using System;
    using System.Linq;
    using System.Reflection;
    
    public static class EnumExtensions
    {
        public static T GetAttribute<T>(this Enum value) where T : Attribute
        {
            string stringValue = value.ToString();
    
            MemberInfo memberInfo = value
                .GetType()
                .GetMember(stringValue)
                .FirstOrDefault();
    
            return memberInfo?
                .GetCustomAttributes(typeof(T), false)
                .Cast<T>()
                .FirstOrDefault();
        }
    }
    

    完成后,您可以提供自定义的JsonConverter&lt;Gender&gt; 实现。在下面的示例中,我允许不区分大小写的 Gender 匹配,并将 Undefined 作为没有适当匹配的所有情况的后备值。

    using System;
    using System.Linq;
    using System.Text.Json;
    using System.Text.Json.Serialization;
    
    public class GenderConverter : JsonConverter<Gender>
    {
        public override Gender Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            string value = reader.GetString();
    
            foreach (Gender gender in Enum.GetValues(typeof(Gender)))
            {
                string[] descriptions = gender.GetAttribute<DescriptionsAttribute>()?.Values;
    
                if (descriptions != null &&
                    descriptions.Any(description => description.Equals(value, StringComparison.OrdinalIgnoreCase)))
                {
                    return gender;
                }
            }
    
            return Gender.Undefined;
        }
    
        public override void Write(Utf8JsonWriter writer, Gender value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(value.ToString());
        }
    }
    

    最后,您只需在 DTO 中使用适当的 JsonConverter 实现用法来装饰您的 Gender 属性。

    using System.Text.Json.Serialization;
    
    public class SomeDto
    {
        [JsonConverter(typeof(GenderConverter))]
        public Gender Gender { get; set; }
    }
    

    【讨论】:

    • 好的,这确实是一个巨大的矫枉过正,但是是的,谢谢:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 2018-09-06
    • 1970-01-01
    相关资源
    最近更新 更多