Using Inherited Entity Class in asp.net EF Core - entity-framework

I have following Entity Class (Which mapped directly to table of SQL Server DB)
public class PROCESSCARD : BaseClass
{
[Key]
[Display(Name = "Card No")]
public String ProcessCardID { get; set; }
[Display(Name = "Entry Date")]
public DateTime EntryDate { get; set; }
[Display(Name ="Job Type")]
public String JobType { get; set; }
[Display(Name = "Job / Non Job")]
public String JobNonJob { get; set; }
[Display (Name = "Cost Booking")]
public String CostBooking { get; set; }
[Display(Name = "Planned Hrs/Qty")]
public Decimal? PlannedHours { get; set; }
}
Above class inherits from BaseClass which is as follow
public class BaseClass
{
[NotMapped]
public String StatusMessage { get; set; }
}
Now upto this there is no issue everything is just fine,
but I am storing deleted data of entity PROCESSCARD to PROCESSCARD_HIST, and I want to show deleted history data to user.
Structures of both entity (PROCESSCARD and PROCESSCARD_HIST are ditto same) so I created another entity class PROCESSCARD_HIST, and to avoid duplicate members, I inherited PROCESSCARD_HIST from PROCESSCARD,
public class PROCESS_CARD_HIST : PROCESS_CARD
{
}
but now when I try to access data from PROCESSCARD_HIST class, it throws error like "Invalid column name 'Discriminator'",
Any Idea how I can achieve this?

If you don't want to repeat the properties in both classes, introduce another unmapped superclass
public class ProcessCardBase : BaseClass
{
[Key]
[Display(Name = "Card No")]
public String ProcessCardID { get; set; }
[Display(Name = "Entry Date")]
public DateTime EntryDate { get; set; }
[Display(Name ="Job Type")]
public String JobType { get; set; }
[Display(Name = "Job / Non Job")]
public String JobNonJob { get; set; }
[Display (Name = "Cost Booking")]
public String CostBooking { get; set; }
[Display(Name = "Planned Hrs/Qty")]
public Decimal? PlannedHours { get; set; }
}
Then
public class ProcessCard : ProcessCardBase
{
}
public class ProcessCardHistory : ProcessCardBase
{
}

Related

.Include() method in APIController with one-to-many relations?

One display should have many media(images, videos) in my project. I'm using Entity Framework Core to build my database, and CRUD with my API Controller.
I designed my Model classes as such:
[Table("Displays")]
public class Display : ConcurrencyCheck
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "{0} skal udfyldes!")]
[Display(Name = "Display navn")]
public string Name { get; set; }
[Display(Name = "Display tændt")]
public bool IsOn { get; set; }
[Display(Name = "Display beskrivelse")]
public string Description { get; set; }
public IEnumerable<Video> Videos { get; set; }
public IEnumerable<Image> Images { get; set; }
}
public abstract class Media : ConcurrencyCheck
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "{0} skal udfyldes!")]
[Display(Name = "Medie navn")]
public string Name { get; set; }
[Display(Name = "Medie beskrivelse")]
public string Description { get; set; }
[Display(Name = "Medie filtype")]
public string FileType { get; set; }
[Required(ErrorMessage = "{0} skal udfyldes!")]
[Display(Name = "Medie filsti")]
public string FilePath { get; set; }
public int? DisplayId { get; set; }
[ForeignKey("DisplayId")]
public Display Display { get; set; }
}
[Table("Videos")]
public class Video : Media
{
[Display(Name = "Frames")]
public int Frames { get; set; }
[Display(Name = "Filsti på undetekster")]
public string SubtitlesPath { get; set; }
[Display(Name = "Filsti på thumbnail")]
public string ThumbnailPath { get; set; }
}
[Table("Images")]
public class Image : Media
{
}
public abstract class ConcurrencyCheck
{
[Timestamp]
public byte[] RowVersion { get; set; }
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy HH:mm}", ApplyFormatInEditMode = true)]
public DateTime CreatedDate { get; set; }
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy HH:mm}", ApplyFormatInEditMode = true)]
public DateTime? UpdatedDate { get; set; }
}
And my API controllers are scaffolded with 'API Controller with actions, using Entity Framework'.
Currently in the api-controller 'DisplaysController', I don't see the .Include(x => x.Media), though I read somewhere it's used? One display should have many media(images, videos).
Should I include it somehow somewhere, or does it do that automatically by the models I have?
scaffolded with API Controller with actions, using Entity Framework just provide the most basic five database operation methods -- select , select by id, update, add and delete. Its purpose is to help users build basic crud faster and more easily. You can add your own code on the default code template.
The default code template
Add your own code

Overriding default Required ErrorMessage property in .NET Core

I have generated my DbContext using EF with DataAnnotations set and would like to override the Required ErrorMessage property using ModelMetadataType
eg
Generated class
public partial class ControlType
{
public ControlType()
{
Rule = new HashSet<Rule>();
}
[Key]
public int ControlTypeId { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[InverseProperty("ControlType")]
public virtual ICollection<Rule> Rule { get; set; }
}
My extension class
[ModelMetadataType(typeof(ControlTypeMeta))]
public partial class ControlType
{
}
public class ControlTypeMeta
{
[Display(Name = "Id")]
public int ControlTypeId { get; set; }
[Display(Name = "Control type")]
[Required(ErrorMessage = "'{0}' is required")]
public string Name { get; set; }
}
With the above I am expecting the Required ErrorMessage to be output as "'Control type' is required", but instead I get the default "The Control type field is required"
Does anyone know how I can override the message (without creating view models)

LINQ query throw exception on FirstOrDefault method

I'm using EF core, and I have a many-to-many relationship between two entity
IotaProject <--> User
Here's entities & dto related to the question
public class IotaProject
{
[Key]
public int Id { get; set; }
[Required]
public string ProjectName { get; set; }
[Required]
public DateTime Create { get; set; }
public ICollection<ProjectOwnerJoint> Owners { get; set; } = new List<ProjectOwnerJoint>();
}
public class ProjectOwnerJoint
{
public int IotaProjectId { get; set; }
public IotaProject IotaProject { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string ShortName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectOwnerJoint> OwnedProjects { get; set; } = new List<ProjectOwnerJoint>();
}
public class ApplicationDbContext : DbContext
{
public DbSet<IotaProject> IotaProjects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProjectOwnerJoint> ProjectOwnerJoint { get; set; }
}
public class IotaProjectDisplayDto
{
public int Id { get; set; }
public string ProjectName { get; set; }
public DateTime Create { get; set; }
public UserMinDto Owner { get; set; }
public int Count { get; set; }
public IEnumerable<UserMinDto> Reviewers { get; set; }
}
public class UserMinDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string ShortName { get; set; }
}
Following LINQ is the problem, the LINQ purpose is to convert IotaProject to IotaProjectDisplayDto, and key part is that Owners property of IotaProject is ICollection and Owner property in IotaProjectDisplayDto is just one single element UserMinDto, so I only need to get the first element of IotaProject's Owners and that's FirstOrDefault() comes.
IEnumerable<IotaProjectDisplayDto> results = _db.IotaProjects.Select(x => new IotaProjectDisplayDto
{
Id = x.Id,
ProjectName = x.ProjectName,
Create = x.Create,
Owner = x.Owners.Select(y => y.User).Select(z => new UserMinDto { Id = z.Id, FullName = z.FullName, ShortName = z.ShortName }).FirstOrDefault()
});
return results;
it throws run-time exception
Expression of type 'System.Collections.Generic.List`1[ToolHub.Shared.iota.UserMinDto]' cannot be used for parameter
of type 'System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto]'
of method 'ToolHub.Shared.iota.UserMinDto FirstOrDefault[UserMinDto](System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto])' (Parameter 'arg0')
I'm guessing it's probably related to deferred execution, but after read some posts, I still can't resolve it.
Any tips would be appreciated.
Right now, the only way I can get this work is I change type of Owner property in IotaProjectDisplayDto into IEnumrable, which will no longer need FirstOrDefault() to immediate execution. And later on, I manually get the first element in the client to display.
This issue happened in Microsoft.EntityFrameworkCore.SqlServer 3.0.0-preview7.19362.6
I end up downgrade to EF core stable 2.2.6 as Ivan suggested in comment, and everything works fine.

Sequence contains more than one matching element on schema update

I´m using ef-core inheritance like this:
public abstract class Person
{
public int Id { get; set; }
public string Name { get; set; }
public PersonType PersonType { get; set; }
public int PersonTypeId { get; set; }
public double Height { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
public class Daughter : Person
{
public double Weigth { get; set; }
public DateTime SomeDate { get; set; }
}
public abstract class Son : Person
{
public DateTime BirthDate { get; set; }
public DateTime GraduationDate { get; set; }
}
public class SingleSon : Son
{
}
public class SonWithDaughter : Son
{
public int Daughter { get; set; }
public Daughter Daughter { get; set; }
}
In DbContext:
public DbSet<PersonType> PersonTypes { get; set; }
public DbSet<Daughter> Daughters { get; set; }
public DbSet<SingleSon> SingleSons { get; set; }
public DbSet<SonWithDaughter> SonWithDaughters { get; set; }
When I update sql server database (dotnet ef database update) it throws a
System.InvalidOperationException, Sequence contains more than one
matching element
Any ideas about how to solve this?
UPDATE
ef migrations console output
This is issue #5894. It will be fixed in version 1.0.1. Until that's released, you can use the nightly feed.

How can I force my DB Initializer to create a table for an ENUM?

I have the following code for the Model, and also for the initializer.
However the status property is created as an INT and I would like it to be a foreign key to a STATUS Table.
Is this possible, or I need to remove the ENUM and create a class?
public class Applicant
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
public int ApplicantID { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(20, MinimumLength = 3, ErrorMessage="Name should not be longer than 20 characters.")]
[Display(Name = "First and LastName")]
public string name { get; set; }
[Required(ErrorMessage = "Telephone number is required")]
[StringLength(10, MinimumLength = 3, ErrorMessage = "Telephone should not be longer than 20 characters.")]
[Display(Name = "Telephone Number")]
public string telephone { get; set; }
[Required(ErrorMessage = "Skype username is required")]
[StringLength(10, MinimumLength = 3, ErrorMessage = "Skype user should not be longer than 20 characters.")]
[Display(Name = "Skype Username")]
public string skypeuser { get; set; }
public byte[] photo { get; set; }
public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
}
public class ApplicantPosition
{
[Key]
[Column("ApplicantID", Order = 0)]
public int ApplicantID { get; set; }
[Key]
[Column("PositionID", Order = 1)]
public int PositionID { get; set; }
public virtual Position Position { get; set; }
public virtual Applicant Applicant { get; set; }
[Required(ErrorMessage = "Applied date is required")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Date applied")]
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
}
public class ApplicationPositionHistory
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
public int ApplicationPositionHistoryID { get; set; }
public ApplicantPosition applicantPosition { get; set; }
public Status oldStatus { get; set; }
public Status newStatus { get; set; }
[StringLength(500, MinimumLength = 3, ErrorMessage = "Commebnts should not be longer than 500 characters.")]
[Display(Name = "Comments")]
public string comments { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Date")]
public DateTime dateModified { get; set; }
}
public enum Status
{
Applied,
AcceptedByHR,
AcceptedByTechnicalDepartment,
InterviewedByHR,
InterviewedByTechnicalDepartment,
InterviewedByGeneralManager,
AcceptedByGeneralManager,
NotAccepted
}
public class HRContext : DbContext
{
public DbSet<Position> Positions { get; set; }
public DbSet<Applicant> Applicants { get; set; }
public DbSet<ApplicantPosition> ApplicantsPositions { get; set; }
public DbSet<ApplicationPositionHistory> ApplicationsPositionHistory { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Position>().ToTable("Position");
modelBuilder.Entity<Applicant>().ToTable("Applicant");
modelBuilder.Entity<ApplicantPosition>().ToTable("ApplicantPosition");
modelBuilder.Entity<ApplicationPositionHistory>().ToTable("ApplicationsPositionHistory");
modelBuilder.Entity<Position>().Property(c => c.name).IsRequired();
modelBuilder.Entity<Applicant>().Property(c => c.name).IsRequired();
modelBuilder.Entity<ApplicantPosition>().Property(c => c.appliedDate).IsRequired();
base.OnModelCreating(modelBuilder);
}
}
If you want Status to be a table created automatically you must create it a class.
Other way is implementing custom database initializer and manually execute SQL to create table, fill it with data and create referential constraint from related tables.
Btw. Enum is not an entity and if you work with enum you should not model it as a table. Check constraint should be used in database to limit values for Status column (again you must create constraint manually in custom initializer).