Is it possible to inherit data annotations in C#? - c#

Can I inherit the "password" data annotation in another class?
public class AccountCredentials : AccountEmail
{
[Required(ErrorMessage = "xxx.")]
[StringLength(30, MinimumLength = 6, ErrorMessage = "xxx")]
public string password { get; set; }
}
The other class:
public class PasswordReset : AccountCredentials
{
[Required]
public string resetToken { get; set; }
**["use the same password annotations here"]**
public string newPassword { get; set; }
}
I have to use different models due to API call's, but like to avoid having to maintain two definitions for the same field.
Thanks!
Addition: something like
[UseAnnotation[AccountCredentials.password]]
public string newPassword { get; set; }

Consider favoring composition over inheritance and using the Money Pattern.
public class AccountEmail { }
public class AccountCredentials : AccountEmail
{
public Password Password { get; set; }
}
public class PasswordReset : AccountCredentials
{
[Required]
public string ResetToken { get; set; }
public Password NewPassword { get; set; }
}
public class Password
{
[Required(ErrorMessage = "xxx.")]
[StringLength(30, MinimumLength = 6, ErrorMessage = "xxx")]
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
Perhaps it has become a golden hammer for me, but recently I have had a lot of success with this, especially when given the choice between creating a base class or instead taking that shared behavior and encapsulating it in an object. Inheritance can get out of control rather quickly.

In the base class, you can make it virtual property, and change it override in the derived class. However, it would not inherit attribute, we do a tricky here :
public class AccountCredentials : AccountEmail
{
[Required(ErrorMessage = "xxx.")]
[StringLength(30, MinimumLength = 6, ErrorMessage = "xxx")]
public virtual string password { get; set; }
}
public class PasswordReset : AccountCredentials
{
[Required]
public string resetToken { get; set; }
public override string password { get; set; }
}

Related

Implementing interface/abstract with null/notnull properties

So heres the situation. I've been thinking through how to do this. But I'm not super sure the best approach.
We have two data model classes that are used to create or update another data model class. Lets call them CreateUser and UpdateUser. and those are used to create/update the User class.
public class CreateUser
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser
{
public int userId { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? email { get; set; }
public string? fullName { get; set; }
}
I want the model from UpdateUser to share every attribute from the CreateUser model. But I want those attributes to be nullable. This is so you can do differential updates. This may seem trivial with the classes I'm providing. But in my use case we have very large classes and differential updates will perform better.
Currently we are maintaining these two classes separately. So when you make a change to CreateUser, You have to also go tweak UpdateUser as well. I would like to know if theres a better approach to this.
I've considered interfaces as a good option for this. But the problem is that I cannot define an interface with properties that can be nullable. They either are nullable or they arent. And I dont want my properties in CreateUser to be nullable. Those are required properties
public interface ICreateOrUpdateUser
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
in my example an interface like this would work. Because strings are nullable by default. But if we used an int instead, It breaks.
What I really need is the ability to create a blueprint for two classes that can be either nullable or not nullable.
In C#, strings are nullable (as you stated), so string? is redundant, but for the int case, there are a few options.
https://learn.microsoft.com/en-us/dotnet/api/system.string?view=net-6.0#Nulls
If you're trying to identify a state based model change (for instance, a create user model is different than all other states), you can document that in code by separating the interfaces for the states. This may be a better way of creating self-documenting code.
public interface IUser : IUserInitialization
{
int? userId {get;set;}
}
public interface IUserInitialization
{
string username { get; set; }
string password { get; set; }
string email { get; set; }
string fullName { get; set; }
}
public class CreateUser : IUserInitialization
{
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser : IUser
{
public int userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
I would recommend removing the nullable if possible, if you want to simplify, the following would match your case. In your CreateUser model, you can change the behaviour for the userId to always be null.
public interface IUser
{
int? userId {get;set;}
string username { get; set; }
string password { get; set; }
string email { get; set; }
string fullName { get; set; }
}
public class CreateUser : IUser
{
public int? userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}
public class UpdateUser : IUser
{
public int? userId { get; set; }
public string username { get; set; }
public string password { get; set; }
public string email { get; set; }
public string fullName { get; set; }
}

C# Overriding inherited validation

I have the following class that all of my entities inherit from:
using System.ComponentModel.DataAnnotations;
#nullable enable
namespace AutomationNavigator.Model.Core
{
public abstract class NamedEntity : Entity, INamedEntity
{
public NamedEntity() : base()
{
}
[MaxLength(100,ErrorMessage ="Name must be 100 characters or less.")]
[Required]
[MinLength(3, ErrorMessage = "Name must be at least 3 characters.")]
[RegularExpression("^[A-Za-z0-9_. ]{3,100}$")] // Alphanumeric with Underscore and Dot only
[Display(Name= "Name")]
public string? Name { get; set; }
}
}
The validation on the Name field is ok to universally apply except in a specific scenario that I just created where I need to allow special characters is name field in my app. Can I somewhow override/remove the Regex validation? The class I am trying to accomplish this for is:
namespace AutomationNavigator.Model.ProcessAssessment
{
public class ProcessFile: NamedEntity
{
[Display(Name = "OrganizationId")]
public Guid? OrganizationId { get; set; }
[ForeignKey("OrganizationId")]
[Display(Name = "Organization")]
public Organization? Organization { get; set; }
[Display(Name = "BusinessProcessId")]
public Guid? BusinessProcessId { get; set; }
[ForeignKey("BusinessProcessId")]
[Display(Name = "BusinessProcess")]
public BusinessProcess? BusinessProcess { get; set; }
[Display(Name = "ProcessDocumentId")]
public Guid? ProcessDocumentId { get; set; }
[ForeignKey("ProcessDocumentId")]
[Display(Name = "ProcessDocument")]
public ProcessDocument? ProcessDocument { get; set; }
[Display(Name = "ProcessFileStatusLookupId")]
public Guid? ProcessFileStatusLookupId { get; set; }
[ForeignKey("ProcessFileStatusLookupId")]
[Display(Name = "LookupValue")]
public LookupValue? LookupValue { get; set; }
[Display(Name = "BlobId")]
public Guid? BlobId { get; set; }
[ForeignKey("BlobId")]
[Display(Name = "Blob")]
public Blob? Blob { get; set; }
[Display(Name = "SizeInBytes")]
public double? SizeInBytes { get; set; }
[Display(Name = "Version")]
public double? Version { get; set; }
[MaxLength(500, ErrorMessage = "Description must be 500 characters or less.")]
[Display(Name = "Description")]
public string? Description { get; set; }
}
}
Virtual properties are a good way of allowing descendents to customise behaviour.
I would make the Name property virtual in the abstract class and override it in the derived specific class where you need to change the validation.
In the abstract class:
[MaxLength(100,ErrorMessage ="Name must be 100 characters or less.")]
[Required]
[MinLength(3, ErrorMessage = "Name must be at least 3 characters.")]
[RegularExpression("^[A-Za-z0-9_. ]{3,100}$")]
[Display(Name= "Name")]
public virtual string? Name { get; set; }
In the derived class:
public class ProcessFile: NamedEntity
{
public override string? Name { get; set;}
}
You can also make the base class' name directly available for the derived class and validate it. Something like:
public abstract class NamedEntity
{
protected string name;
public virtual string Name
{
get { return name; }
set
{
//validation, if any
}
}
public NamedEntity()
{
name = "";
}
}
public class ProcessFile: NamedEntity
{
public override string Name
{
get { return base.Name; }
set { base.Name = // new validation; }
}
public ProcessFile()
: base()
{
base.Name = "";
}
}
I hope it helps.
Cheers.

Why referenced class validation attribute is not active?

I have the following two object Classes :
[Serializable]
[DataContract]
public class W_ListItemPrice
{
[DataMember]
[Required]
public string nSize { get; set; }
[DataMember]
[Required, Range(0.5, Constants.FOOD_ITEM_MAX_SELLING_PRICE, ErrorMessage = "Price range invalid")]
public decimal ItemPrice { get; set; }
}
[Serializable]
[DataContract]
public class W_Listing
{
[DataMember]
[Required, RegularExpression(GlobalFromat.IDformat, ErrorMessage = "Invalid FoodID format")]
public Int64 MemberID { get; set; }
[DataMember]
[Required, RegularExpression(GlobalFromat.IDformat, ErrorMessage = "Invalid ScheduleID format")]
public Int64 ScheduleID { get; set; }
[DataMember]
[Required]
public List<Days?> FoodDays { get; set; }
[Required]
public List<W_ListItemPrice> ListingPortionPrice { get; set; }
public W_Listing()
{
oDays = new List<Days?>();
ListingPrice = new List<W_ListItemPrice>();
}
}
When I call the function with Class W_Listing, it validates only the member items listed in W_Listing class. It does not validate members in W_ListItemPrice class ? It does not care what I pass or not.
Why ?

Override MetaData UIHint in Derived Class

EF creates the Item class, I then create a partial Item class with a metadataType, ItemMD.
In certain cases, I want to use the UIHint defined in ItemMD, but in others, I want to override the UIHint to use another editor. So I try creating a derived class that inherits ItemMD....but I don't think this is how you are supposed to do it. It compiles, but I get inconsistent behaviour....when I have 5 fields in my Razor form, 4 are using the base ItemMD UIHint and 1 is using the derived class UIHint. Not sure why the inconsistency.
[MetadataType(typeof(ItemMD))]
public partial class Item : AuditStamps, IEntity, IAuditStamps
{
}
public partial class ItemMD
{
public int Id { get; set; }
[Display(Name = "Company Id")]
public int CompanyId { get; set; }
public string Description { get; set; }
[Display(Name = "Short Description")]
public string ShortDescription { get; set; }
[Display(Name = "Type")]
[UIHint("ItemAtrributesComboBox")]
public virtual string Attribute1 { get; set; }
[Display(Name = "Color")]
[UIHint("ItemAtrributesComboBox")]
public virtual string Attribute2 { get; set; }
[Display(Name = "Finish")]
[UIHint("ItemAtrributesComboBox")]
public virtual string Attribute3 { get; set; }
[Display(Name = "Texture")]
[UIHint("ItemAtrributesComboBox")]
public virtual string Attribute4 { get; set; }
[Display(Name = "Gauge")]
[UIHint("ItemAtrributesComboBox")]
public virtual string Attribute5 { get; set; }
public class ItemSearchFiltersViewModel : OTIS.domain.InventoryMgmt.Item.ItemMD
{
[Display(Name = "Type:")]
[UIHint("ItemAttributesDDL")]
public override string Attribute1 { get; set; }
[Display(Name = "Color:")]
[UIHint("ItemAttributesDDL")]
public override string Attribute2 { get; set; }
[Display(Name = "Finish:")]
[UIHint("ItemAttributesDDL")]
public override string Attribute3 { get; set; }
[Display(Name = "Texture:")]
[UIHint("ItemAttributesDDL")]
public override string Attribute4 { get; set; }
[Display(Name = "Gauge:")]
[UIHint("ItemAttributesDDL")]
public override string Attribute5 { get; set; }
}

Is it possible to use DataAnnotations with Interfaces inheritance?

not performing the validation in PersonaFisica
..
Blockquote
[MetadataType(typeof(IValidationPersona))]
public class Persona : Entidad, IValidationPersona
{
public string Mail { get; set; }
public string Telefono { get; set; }
}
[MetadataType(typeof(IValidationPersonaFisica))]
public class PersonaFisica : Persona, IValidationPersonaFisica
{
public string Nombre { get; set; }
public string Apellido { get; set; }
}
public interface IValidationPersona
{
[DisplayName("Persona -- Email")]
string Mail { get; set; }
[RegularExpression(#"^\d+$", ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = "SoloNumeros")]
string Telefono { get; set; }
}
public interface IValidationPersona
{
[DisplayName("Persona -- Email")]
string Mail { get; set; }
[RegularExpression(#"^\d+$", ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = "SoloNumeros")]
string Telefono { get; set; }
}
validation does not work with inheritance in interfaces, thanks!
This feature is not implemented in the framework for couple of good reasons. Please refer to this MSDN forum DataAnnotations in Interfaces

Categories