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; }
}
Related
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Hospital> Hospitals { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
}
}
Hospital.cs
public class Hospital : Common
{
[Key]
[Column(Order = 1)]
public int HospitalId { get; set; }
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Invalid e-mail address.")]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "Contact Person")]
public string ContactPerson { get; set; }
[Required]
[Display(Name = "Phone Number")]
[DataType(DataType.PhoneNumber)]
public string PhoneNum { get; set; }
[Required]
[MinLength(10)]
[Display(Name = "Mobile Number")]
public string MobileNum { get; set; }
[Required]
[Display(Name = "Address")]
public string Address { get; set; }
[Required]
[Display(Name = "Short Name")]
public string ShortName { get; set; } //Unique Slug name
}
Common.cs
public class Common
{
public DateTime? CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime? EditedOn { get; set; }
public string EditedBy { get; set; }
public DateTime? DeletedOn { get; set; }
public string DeletedBy { get; set; }
public bool? IsDeleted { get; set; } = false;
public bool? IsActive { get; set; } = true;
}
I want to add the primary key of the AspNetUsers table in the Hospital table as foreign key with these columns:
CreatedBy
EditedBy
DeletedBy
Main Goal: So using this kind of relationship I want to display the User Name with Hospital details, who is Create/Edit/Delete hospital details.
I don't recommend between two table more than one relation. I think, There's no need forgein relations for these colums. Bcs these colums're just information coll. But still you can edit common class like this.
public class Common
{
public DateTime? CreatedOn { get; set; }
[ForeignKey("AspNetUsers")] public string CreatedBy { get; set; }
public AspNetUsers CreatedByUser { get; set; }
public DateTime? EditedOn { get; set; }
[ForeignKey("AspNetUsers")] public string EditedBy { get; set; }
public AspNetUsers EditedByUser { get; set; }
public DateTime? DeletedOn { get; set; }
[ForeignKey("AspNetUsers")] public string DeletedBy { get; set; }
public AspNetUsers DeletedByUser { get; set; }
public bool? IsDeleted { get; set; } = false;
public bool? IsActive { get; set; } = true;
}
Configure the relasionship using the data annotation:
in Hospital class add the following attribute :
public int AspNetUsersId{ get; set; }
[ForeignKey("AspNetUsersId")]
public virtual Common aspNetUser { get; set; }
and in Common class add the Hospital Attribute:
public virtual Hospital hospital { get; set; }
Now, you should define this relation in ApplicationDvContext :
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Hospital>()
.HasOptional(s => s.aspNetUser)
.WithRequiredPrincipal(ad => ad.hospital);
}
You should configure your database from a management program with the key-relationships you want and then update the entity data model from your solution, otherwise Entity won't be able to find the relationship by itself. If you are using microsoft sql management studio check this article.
I want to Map two types of Objects but I didn't find the way to do it.
User class:
public partial class TUser
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("login")]
[StringLength(50)]
public string Login { get; set; }
[Column("password")]
[StringLength(50)]
public string Password { get; set; }
[Column("role")]
[StringLength(50)]
public string Role { get; set; }
[Column("isDeleted")]
public bool? IsDeleted { get; set; }
[Column("avatarUrl")]
[StringLength(50)]
public string AvatarUrl { get; set; }
[Column("iso")]
[StringLength(2)]
public string Iso { get; set; }
[Column("lastLogonDate", TypeName = "datetime")]
public DateTime? LastLogonDate { get; set; }
[Column("createdDate", TypeName = "datetime")]
public DateTime? CreatedDate { get; set; }
[Column("lastUpdatedDate", TypeName = "datetime")]
public DateTime? LastUpdatedDate { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
[InverseProperty("IdNavigation")]
public virtual TWorker TWorker { get; set; }
}
UserForLogin Class:
public class UserForLogin
{
public int Id { get; set; }
public string Login { get; set; }
public string Role { get; set; }
public string AvatarUrl { get; set; }
public string Iso { get; set; }
public TWorker TWorker { get; set; }
}
TWorker class:
public partial class TWorker
{
public TWorker()
{
TWorkerToWorkType = new HashSet<TWorkerToWorkType>();
TrEventToWorker = new HashSet<TrEventToWorker>();
TrWorkerToWorkerCategory = new HashSet<TrWorkerToWorkerCategory>();
}
[Key]
[Column("id")]
public int Id { get; set; }
[Column("lastname")]
[StringLength(50)]
public string Lastname { get; set; }
[Column("firstname")]
[StringLength(50)]
public string Firstname { get; set; }
[Column("email")]
[StringLength(50)]
public string Email { get; set; }
[Column("phone")]
[StringLength(50)]
public string Phone { get; set; }
[Column("address")]
[StringLength(50)]
public string Address { get; set; }
[Column("postcode")]
[StringLength(50)]
public string Postcode { get; set; }
[Column("locality")]
[StringLength(50)]
public string Locality { get; set; }
[Column("workerCategoryKey")]
public int? WorkerCategoryKey { get; set; }
[Column("sexe")]
[StringLength(50)]
public string Sexe { get; set; }
[ForeignKey(nameof(Id))]
[InverseProperty(nameof(TUser.TWorker))]
public virtual TUser IdNavigation { get; set; }
[InverseProperty("WorkerKeyNavigation")]
public virtual ICollection<TWorkerToWorkType> TWorkerToWorkType { get; set; }
[InverseProperty("WorkerKeyNavigation")]
public virtual ICollection<TrEventToWorker> TrEventToWorker { get; set; }
[InverseProperty("WorkerKeyNavigation")]
public virtual ICollection<TrWorkerToWorkerCategory> TrWorkerToWorkerCategory { get; set; }
}
AutoMapperProfiles class:
public AutoMapperProfiles()
{
CreateMap<TUser, UserForLogin>()
.ForMember(
dest => dest.TWorker,
opt => opt.MapFrom(src => src.TWorker)
);
}
But TWorker is always null and I can't find what am I doing wrong?
If I use TUser only to return my object without Automapper code, TWorker contains the values I want.
You just need to implement the map for the subObject and autoMapper will handle it for you.
To be precise, if you map a property to another property which has a different type, autoMapper will try to find a corresponding map.
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.
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 ?
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.