【问题标题】:How to use ASP.Net core ModelMetadata attribute如何使用 ASP.Net 核心 ModelMetadata 属性
【发布时间】:2018-04-20 05:30:36
【问题描述】:
[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();

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

【问题讨论】:

  • LegalEntity 没有 IdName 属性。那些属于基类吗?
  • 是的,那些是从基类继承而来的
  • 在黑暗中拍摄,因为我个人不使用它,但如果没记错的话,我认为您申请ModelMetadataType 的课程必须是partial
  • 还值得一提的是,该属性的存在主要是为了支持验证。序列化时,Json.Net 完全有可能根本不注意它。
  • 如果它不是派生类并且 id 和 name 属性将被重命名为 JsonProperty 属性。如果不使用 ModelMetadataType,我希望 Json.Net 能够以其他方式支持派生属性的 JsonProperty - 否则这将是一个不可能的大洞

标签: c# json.net asp.net-core-2.0 data-annotations


【解决方案1】:

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

Json.NET 确实支持System.ComponentModel.DataAnnotations.MetadataTypeAttribute,尽管在this answer 中描述了一些限制,但是即使此属性存在于 .Net 核心中(不确定是否存在)它也无济于事,因为您正在尝试使用派生类的元数据类型来重命名基类型中的属性,这不是元数据类型信息的预期用途。 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 来代替您的LegalEntity,然后使用 之类的东西在它们之间进行映射:

    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,类似于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 Core 中,请参阅here

    请注意,我使用完整的 .Net 4.5.1 编写了此代码,因此它只是一个原型。 .Net Core 使用different reflection API,但是如果您按照here 的描述安装System.Reflection.TypeExtensions,我相信它应该可以工作。

【讨论】:

  • 非常感谢!我从你的回复中学到了很多,我使用了你的合同解析器,它就像一个魅力 .AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new ModelMetadataTypeAttributeContractResolver();})
  • @Anand - 很高兴为您提供帮助。你可能想cache the serializer for best performance
【解决方案2】:

切换到 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; }
        }
    }
    

【讨论】:

    猜你喜欢
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2018-08-02
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    相关资源
    最近更新 更多