如何使用 ASP.Net 核心 ModelMetadata 属性

How to use ASP.Net core ModelMetadata attribute

[Table("LegalEntity")]
[ModelMetadataType(typeof(LegalEntityMeta))]
public class LegalEntity : Entity<long>
{
}

public class LegalEntityMeta
{
    [JsonProperty(PropertyName = "LegalEntityId")]
    public long Id { get; set; }

    [JsonProperty(PropertyName = "LegalEntityName")]
    public string Name { get; set; }
}

在Startup.cs....

        services
            .AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            })
            .AddAutoMapper(typeof(Startup))
            .AddMvcCore()
            .AddJsonFormatters()
            .AddApiExplorer();

我的期望是看到 json 具有属性 legalEntityId 和 legalEntityName 但生成的 json 具有 id 和 name 作为属性。 有人可以帮助我如何更改 json 属性吗? 谢谢 阿南德

Json.NET 目前不支持 Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute. In Issue #1349: Add support for ModelMetadataType for dotnetcore like supported MetadataTypeAttribute in previous versions 拒绝对其实施支持的请求。

Json.NET 确实支持 System.ComponentModel.DataAnnotations.MetadataTypeAttribute, albeit with some limitations described in ,但是即使此属性存在于 .Net 核心中(不确定是否存在)它也无济于事,因为您正在尝试使用元数据类型派生 class 重命名基本类型 中的属性 ,这不是元数据类型信息的预期用途。 IE。以下开箱即用(完整的 .Net):

[System.ComponentModel.DataAnnotations.MetadataType(typeof(EntityMeta))]
public class Entity<T>
{
    public T Id { get; set; }

    public string Name { get; set; }
}

public class EntityMeta
{
    [JsonProperty(PropertyName = "LegalEntityId")]
    public long Id { get; set; }

    [JsonProperty(PropertyName = "LegalEntityName")]
    public string Name { get; set; }
}

但以下不是:

[System.ComponentModel.DataAnnotations.MetadataType(typeof(LegalEntityMeta))]
public class LegalEntity : Entity<long>
{
}

public class LegalEntityMeta
{
    [JsonProperty(PropertyName = "LegalEntityId")]
    public long Id { get; set; }

    [JsonProperty(PropertyName = "LegalEntityName")]
    public string Name { get; set; }
}

为什么 Json.NET 不允许派生类型元数据信息修改基类型契约?您将不得不询问 Newtonsoft,但猜测包括:

  1. Json.NET 是一个基于契约的序列化器,其中每个类型通过属性指定其契约。并不是说一种类型可以重写第二种类型的契约。

  2. DataContractJsonSerializerDataContractSerializer 的工作方式相同。

  3. 这样做会违反Liskov substitution principle

那么,你有什么选择?

  1. 您可以序列化一个 DTO in place of your LegalEntity, and use something like 以在两者之间映射:

    public class LegalEntityDTO
    {
        [JsonProperty(PropertyName = "LegalEntityId")]
        public long Id { get; set; }
    
        [JsonProperty(PropertyName = "LegalEntityName")]
        public string Name { get; set; }
    }
    
  2. 您可以使用必要的逻辑为 LegalEntity 创建一个 custom JsonConverter

  3. 您可以创建一个 custom contract resolver with the necessary logic, similar to the one here,例如以下内容:

    using System.Reflection;
    
    public class ModelMetadataTypeAttributeContractResolver : DefaultContractResolver
    {
        public ModelMetadataTypeAttributeContractResolver()
        {
            // Default from https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs
            this.NamingStrategy = new CamelCaseNamingStrategy();
        }
    
        const string ModelMetadataTypeAttributeName = "Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute";
        const string ModelMetadataTypeAttributeProperty = "MetadataType";
    
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var properties = base.CreateProperties(type, memberSerialization);
    
            var propertyOverrides = GetModelMetadataTypes(type)
                .SelectMany(t => t.GetProperties())
                .ToLookup(p => p.Name, p => p);
    
            foreach (var property in properties)
            {
                var metaProperty = propertyOverrides[property.UnderlyingName].FirstOrDefault();
                if (metaProperty != null)
                {
                    var jsonPropertyAttribute = metaProperty.GetCustomAttributes<JsonPropertyAttribute>().FirstOrDefault();
                    if (jsonPropertyAttribute != null)
                    {
                        property.PropertyName = jsonPropertyAttribute.PropertyName;
                        // Copy other attributes over if desired.
                    }
                }
            }
    
            return properties;
        }
    
        static Type GetModelMetadataType(Attribute attribute)
        {
            var type = attribute.GetType();
            if (type.FullName == ModelMetadataTypeAttributeName)
            {
                var property = type.GetProperty(ModelMetadataTypeAttributeProperty);
                if (property != null && property.CanRead)
                {
                    return property.GetValue(attribute, null) as Type;
                }
            }
            return null;
        }
    
        static Type[] GetModelMetadataTypes(Type type)
        {
            var query = from t in type.BaseTypesAndSelf()
                        from a in t.GetCustomAttributes(false).Cast<System.Attribute>()
                        let metaType = GetModelMetadataType(a)
                        where metaType != null
                        select metaType;
            return query.ToArray();
        }
    }
    
    public static partial class TypeExtensions
    {
        public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
        {
            while (type != null)
            {
                yield return type;
                type = type.BaseType;
            }
        }
    }
    

    示例 .Net fiddle.

    要直接序列化,请执行:

    var settings = new JsonSerializerSettings
    {
        ContractResolver = new ModelMetadataTypeAttributeContractResolver(),
    };
    
    var json = JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
    

    要将合同解析器安装到 Asp.Net 核心中,请参阅 here

    请注意,我是使用完整的 .Net 4.5.1 编写的,因此它只是一个原型。 .Net Core 使用 different reflection API, however if you install System.Reflection.TypeExtensions as described 我相信它应该可以工作。

改用 Newtonsoft.Json 会有帮助:

  1. 添加nuget包Microsoft.AspNetCore.Mvc.Newtonsoft.Json

  2. 在 Startup.cs -> ConfigureServices(阅读 https://www.ryadel.com/en/use-json-net-instead-of-system-text-json-in-asp-net-core-3-mvc-projects/ 了解更多信息)

    services.AddControllers().AddNewtonsoftJson();

  3. 使用 System.Text.Json.Serialization 替换并使用 MetadataType 而不是 ModelMetadataType:

    using Newtonsoft.Json;
    namespace YourDbDataNamespace
    {
        [MetadataType(typeof(UserMetadata))]
        public partial class User {}
    
        public class UserMetadata
        {
            [JsonProperty(PropertyName = "LegalEntityId")]
            int Id { get; set; }
    
            [JsonIgnore]
            public string PasswordHash { get; set; }
        }
    }