C# Overriding inherited validation - c#

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.

Related

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 ?

Error with mapping entities in EF

I am trying to implement Asp.net Identity 2.0 with DB first.
I have imported my model.edmx into the project. It contains all the tables I need with the correct information and structure.
In the database there is a table called 'FSKUsers' I have edited this to contain the needed fields of the AspNetUsers which is the default table for Identity 2.0
So in my Identity DB Context I have mapped my FskUser class (which is a high level user for Identity sake)
public class IdentityDbContext : IdentityDbContext<FskUser, FskRole, int, FskUserLogin, FskUserRole, FskUserClaim>
{
public IdentityDbContext()
: base("FSK_FskNetworksEntities")
{
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var userEntity = modelBuilder.Entity<FskUser>();
userEntity.ToTable("FSKUsers", "dbo");
userEntity.Property(p => p.Id).HasColumnName("FSKUserId");
userEntity.Property(p => p.PasswordHash).HasColumnName("Password");
}
public static IdentityDbContext Create()
{
return new IdentityDbContext();
}
}
So basically I want to map the class FskUser to the Data Base table called FSKUser which is also contained in my .edmx model.
When I run the website I get the following error.
The entity type FskUser is not part of the model for the current context
My two POCO classes:
The one from my edmx model:
public partial class FSKUser
{
public FSKUser()
{
this.AspNetUserClaims = new HashSet<AspNetUserClaim>();
this.AspNetUserLogins = new HashSet<AspNetUserLogin>();
this.FSKDevices = new HashSet<FSKDevice>();
this.FSKEventLogs = new HashSet<FSKEventLog>();
this.FSKReports = new HashSet<FSKReport>();
this.FSKTransactions = new HashSet<FSKTransaction>();
this.FSKTriggers = new HashSet<FSKTrigger>();
this.UdlDownloads = new HashSet<UdlDownload>();
this.AspNetRoles = new HashSet<AspNetRole>();
this.FSKCompanies = new HashSet<FSKCompany>();
}
public int FSKUserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public string Password { get; set; }
public string SecurityStamp { get; set; }
public bool TwoFactorEnabled { get; set; }
public Nullable<System.DateTime> LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public byte FSKAccessLevelId { get; set; }
public string AddressStreet1 { get; set; }
public string AddressStreet2 { get; set; }
public string AddressStreet3 { get; set; }
public string AddressPostCode { get; set; }
public Nullable<int> CreatorId { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public Nullable<bool> IsConfirmed { get; set; }
public Nullable<System.DateTime> LastPasswordFailureDate { get; set; }
public Nullable<int> PasswordFailuresSinceLastSuccess { get; set; }
public Nullable<System.DateTime> PasswordChangedDate { get; set; }
public string PasswordVerificationToken { get; set; }
public string PasswordVerificationTokenExpirationDate { get; set; }
public bool IsDeleted { get; set; }
public Nullable<int> CostCentreId { get; set; }
public Nullable<int> AdminPasswordResetUserId { get; set; }
public Nullable<System.DateTime> PreviousLogInDate { get; set; }
public System.Guid msrepl_tran_version { get; set; }
public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual ICollection<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual ICollection<FSKDevice> FSKDevices { get; set; }
public virtual ICollection<FSKEventLog> FSKEventLogs { get; set; }
public virtual ICollection<FSKReport> FSKReports { get; set; }
public virtual ICollection<FSKTransaction> FSKTransactions { get; set; }
public virtual ICollection<FSKTrigger> FSKTriggers { get; set; }
public virtual ICollection<UdlDownload> UdlDownloads { get; set; }
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
public virtual ICollection<FSKCompany> FSKCompanies { get; set; }
}
The one I use in my Identity Config
public class FskUser : IdentityUser<int, FskUserLogin, FskUserRole, FskUserClaim>
{
[Display(Name = "First Name")]
[Required(ErrorMessage = "First Name is Required.")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required(ErrorMessage = "Last Name is Required.")]
public string LastName { get; set; }
[MaxLength(20)]
[Display(Name = "Cell Number")]
[RegularExpression(#"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$", ErrorMessage = "Entered phone format is not valid.")]
[StringLength(10, ErrorMessage = "The {0} must be 10 numbers long.", MinimumLength = 10)]
public override string PhoneNumber { get; set; }
[Display(Name = "Access Level")]
public byte? FSKAccessLevelId { get; set; }
[Display(Name = "Street Address 1")]
public string AddressStreet1 { get; set; }
[Display(Name = "Street Address 2")]
public string AddressStreet2 { get; set; }
[Display(Name = "Street Address 3")]
public string AddressStreet3 { get; set; }
[Display(Name = "Postal Code")]
public string AddressPostCode { get; set; }
[Display(Name = "Previous Login")]
public Nullable<DateTime> PreviousLogInDate { get; set; }
[Display(Name = "Account Confirmed")]
public Nullable<bool> IsConfirmed { get; set; }
[Display(Name = "Last Password Failier")]
public Nullable<DateTime> LastPasswordFailureDate { get; set; }
[Display(Name = "Password Last Changed")]
public Nullable<DateTime> PasswordChangedDate { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<FskUser, int> manager)
{
//TODO: add option for web and api (to create different auth types
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
When you use Database first approach with edmx file OnModelCreating method is never called. You may check that with debugger.

Is it possible to inherit data annotations in 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; }
}

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; }
}

Display name for nested complex type

i have two model class like below,
i want to use different labels for Categoy.Name property if its nested,but if i use complex type,it didnt bind to name property,is there any options something like BindTo("Category.Name") for complex types???
public class ProductModel
{
public int Id { get; set; }
[DisplayName("Product Name")]
public string Name { get; set; }
[DisplayName("Category Name")]
[BindTo("Category.Name")]
public CategoryModel Category { get; set; }
}
public class CategoryModel
{
public int Id { get; set; }
[DisplayName("Name")]
public string Name { get; set; }
}
One solution is to use the MetadataType attribute.
public class ProductModel
{
public int Id { get; set; }
[DisplayName("Product Name")]
public string Name { get; set; }
public CategoryPropertyModel Category { get; set; }
}
[MetadataType(typeof(ICategoryModelCategoryDisplay))
public class CategoryModel : CategoryBaseModel { }
[MetadataType(typeof(ICategoryModelDisplay))
public class CategoryPropertyModel : CategoryBaseModel { }
public class CategoryBaseModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface ICategoryModelSimpleDisplay
{
[DisplayName("Name")]
public string Name { get; set; }
}
public interface ICategoryModelCategoryDisplay
{
[DisplayName("Category Name")]
public string Name { get; set; }
}
When you use CategoryModel the display name will be Name.
When you use CategoryPropertyModel the display name will be Category Name.

Categories