【问题标题】:Copy Attributes of one property to another instead of writing them again将一个属性的属性复制到另一个属性,而不是再次写入它们
【发布时间】:2020-04-13 07:26:28
【问题描述】:

我创建了一个包含所有基本验证属性和数据注释的模型 但我面临一个问题,因为我不能将此类用作表单模型,因为它具有额外的属性,并且我需要尽可能小的表单对象,因为我将其序列化的 json 发送到 API。

我可以通过仅将所需的属性及其属性复制到另一个类来接近“已经完成的”,我不应该担心保持值相同,因为它们将共享相同的常量,但如果我决定删除这可能会适得其反或添加属性。我必须转到每个表单模型并添加/删除属性

是否有一些内置属性可以克隆给定属性的所有属性?像下一个例子一样?

属性类

[AttributeUsage(AttributeTargets.Property)]
public class ClonePropertyAttributesAttribute : Attribute
{
    public ClonePropertyAttributesFromAttribute(Type TargetType,String PropertyName)
    {
         //clone attributes logic here
    }
}

表单模型

 public class SignUpFormModel
 {
    [ClonePropertyAttributesFrom(typeof(User),"Email")]
    public String Email { get; set; }
    [ClonePropertyAttributesFrom(typeof(User),"Username ")]
    public String Username { get; set; }
    [ClonePropertyAttributesFrom(typeof(User),"Password")]
    public String Password { get; set; }
    [ClonePropertyAttributesFrom(typeof(User),"First_Name ")]
    public String First_Name { get; set; }
    [ClonePropertyAttributesFrom(typeof(User),"Last_Name")]
    public String Last_Name { get; set; }
    [ClonePropertyAttributes(AttributeProvider(typeof(User).GetProperty("Profile_Picture")))]
    public String Profile_Picture { get; set; }
 }

型号

public class User
{
    //### Constansts Definitions ###//
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long? Id { get; set; }
    [Display(Name = EMAIL_DISPLAY_NAME)]
    [Required(ErrorMessage = EMAIL_REQUIRED_ERROR_MESSAGE)]
    [StringLength(EMAIL_MAX_LENGTH, ErrorMessage = EMAIL_LENGTH_ERROR_MESSAGE)]
    [RegularExpression(EMAIL_REGEX, ErrorMessage = EMAIL_REGEX_ERROR_MESSAGE)]
    [Remote(EMAIL_REMOTE_DOES_EXIST_ACTION_NAME, EMAIL_REMOTE_EXIST_CONTROLLER_NAME, ErrorMessage = EMAIL_REMOTE_DOES_EXIST_ERROR_MESSAGE)]
    [JsonProperty(EMAIL_JSON_PROPERTY_NAME)]
    public String Email { get; set; }
    [Display(Name = USERNAME_DISPLAY_NAME)]
    [Required(ErrorMessage = USERNAME_REQUIRED_ERROR_MESSAGE)]
    [StringLength(USERNAME_MAX_LENGTH, MinimumLength = USERNAME_MIN_LENGTH, ErrorMessage = USERNAME_LENGTH_ERROR_MESSAGE)]
    [RegularExpression(USERNAME_REGEX, ErrorMessage = USERNAME_REGEX_ERROR_MESSAGE)]
    [Remote(USERNAME_REMOTE_DOES_EXIST_ACTION_NAME, USERNAME_REMOTE_EXIST_CONTROLLER_NAME, ErrorMessage = USERNAME_REMOTE_DOES_EXIST_ERROR_MESSAGE)]
    [JsonProperty(USERNAME_JSON_PROPERTY_NAME)]
    public String Username { get; set; }
    [Display(Name = PASSWORD_DISPLAY_NAME)]
    [Required(ErrorMessage = PASSWORD_REQUIRED_ERROR_MESSAGE)]
    [StringLength(PASSWORD_MAX_LENGTH, MinimumLength = PASSWORD_MIN_LENGTH, ErrorMessage = PASSWORD_LENGTH_ERROR_MESSAGE)]
    [RegularExpression(PASSWORD_REGEX,ErrorMessage = PASSWORD_REGEX_ERROR_MESSAGE)]
    [JsonProperty(PASSWORD_JSON_PROPERTY_NAME)]
    public String Password { get; set; }
    [Display(Name = FIRST_NAME_DISPLAY_NAME)]
    [Required(ErrorMessage = FIRST_NAME_REQUIRED_ERROR_MESSAGE)]
    [StringLength(FIRST_NAME_MAX_LENGTH, MinimumLength = FIRST_NAME_MIN_LENGTH, ErrorMessage = FIRST_NAME_LENGTH_ERROR_MESSAGE)]
    [RegularExpression(FIRST_NAME_REGEX, ErrorMessage = FIRST_NAME_REGEX_ERROR_MESSAGE)]
    [JsonProperty(FIRST_NAME_JSON_PROPERTY_NAME)]
    public String First_Name { get; set; }
    [Display(Name = LAST_NAME_DISPLAY_NAME)]
    [Required(ErrorMessage = LAST_NAME_REQUIRED_ERROR_MESSAGE)]
    [StringLength(LAST_NAME_MAX_LENGTH, MinimumLength = LAST_NAME_MIN_LENGTH, ErrorMessage = LAST_NAME_LENGTH_ERROR_MESSAGE)]
    [RegularExpression(LAST_NAME_REGEX, ErrorMessage = LAST_NAME_REGEX_ERROR_MESSAGE)]
    [JsonProperty(LAST_NAME_JSON_PROPERTY_NAME)]
    public String Last_Name { get; set; }
    [JsonProperty(PROFILE_PICTURE_JSON_PROPERTY_NAME)]
    public String Profile_Picture { get; set; }
    [JsonProperty(REGISTERATION_TIME_JSON_PROPERTY_NAME)]
    public long? RegisterationTime { get; set; }
    [JsonProperty(VERIFIED_JSON_PROPERTY_NAME)]
    public int? Verified { get; set; }
    [JsonProperty(INACCESSIBLE_JSON_PROPERTY_NAME)]
    public int? Inaccessible { get; set; }
}

更新#1

这里有一些可能的重复:
stackoverflow.com/questions/49701038
stackoverflow.com/questions/14663763
这似乎很烦人,而且每次注册看起来都是一项昂贵的操作,不是吗?

【问题讨论】:

  • 嗯,我考虑过使用继承,但这显然行不通; stackoverflow.com/questions/2520035/…
  • 它应该可以正常工作,但正如我所说,我需要尽可能少的属性,因为我在 api 调用中发送序列化的 json。例如,我将 FormModel 中提到的六个属性放在一个基类中,如果我只需要两个呢?
  • 您是否假设额外的属性将成为问题,或者您是否存在特定表单不需要的 2-3 个属性的性能问题?我感觉你是通过创造一个实际的问题来解决想象中的问题。
  • @DStanley 不是性能问题,问题是我绑定到一些非对称加密系统,其中保存的密钥允许加密有限数量的数据.. 遗憾的是,序列化 json 中的开销会导致问题.而且这只是一个演示代码,用户类实际上比这个大很多

标签: c# asp.net-mvc


【解决方案1】:

您可以使用 AutoMapper 从 User 映射到 SignUpFormModel

当您需要从模型类(或数据库模型的实体)映射到 API 调用中使用的 DTO 对象以及相反方向时,AutoMapper 非常有用。

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<User, SignUpFormModel>();
});

IMapper mapper = config.CreateMapper();
var user = new User() { First_Name = "tester" };
var formModel = mapper.Map<User, SignUpFormModel>(user);

AutoMapper Getting Started Guide

更新:

如果源类和目标类中的属性名称不匹配,可以为其创建自定义映射,例如:

cfg.CreateMap<SignUpFormModel, User>()
    .ForMember(x => x.EmailInDestination, o => o.MapFrom(s => s.EmailInSource))

【讨论】:

  • 我不熟悉这个库。您是否在此代码中映射相似属性之间的值?我注意到您正在实例化一个填充其 First_Name 的用户对象。我不确定它是否克隆了相似的属性名称
  • AutoMapper 默认映射具有相同属性名称的属性,它也默认支持展平(docs.automapper.org/en/stable/Flattening.html)。它还支持配置自定义属性映射。对于您的模型,可以使用默认映射,因为表单模型中的所有属性都与用户类中的名称相同。
  • 这可能会起作用,但我认为我需要在运行时执行此操作,因为我在表单开头使用 FormModel 作为 asp.net mvc 表单 @Models.FormModels.SignUpFormModel 的包装器。正如我在代码的最后一行看到的结果映射对象。它提供了一个映射实例,但我想要的是复制属性并将其粘贴到实际的类定义中,以便在表单开头的这一行 @Models.FormModels.SignUpFormModel 中使用它。
  • 它看起来有多种方式可以完成 mpping :) 更新了答案,现在它显示了属性的自定义映射示例,现在我明白了这个问题
【解决方案2】:

我能想到的唯一方法是在运行时通过反射获取属性,然后将其应用于其他类。

见: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/accessing-attributes-by-using-reflection

和设置属性:

https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.typedescriptor.addattributes?redirectedfrom=MSDN&view=netframework-4.8#System_ComponentModel_TypeDescriptor_AddAttributes_System_Object_System_Attribute___

缺点: - 反射很慢 - 不是真正的类型安全

我不是这种方法的真正粉丝,但我个人认为没有其他方法。我认为你应该尝试修改你的架构,这样你就可以解决这个问题。

【讨论】:

  • 所以您的意思是我必须设计自己的属性,该属性在参数中采用给定属性并使用反射访问其属性,然后将它们分配给给定成员?对于像我这样的新手来说似乎很难......我实际上虽然关于使用基类,但正如我所说的,有时我不需要该基类中的所有属性,在序列化的 json 中留下一些大小开销
  • @MinaGerges 首先,也不喜欢这种方法。也许只是使用基类,我怀疑大小开销会是一个很大的问题。
  • 它会因为我绑定到一些非对称加密系统,其中保存的密钥会加密有限数量的数据......所以很遗憾,开销会导致问题
猜你喜欢
  • 2010-11-14
  • 2019-05-05
  • 2014-09-25
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多