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

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

Related

Using Inherited Entity Class in asp.net EF Core

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

How to post some collection of <T> data to MVC action?

public class Graphic
{
...
...
[ScaffoldColumn(false)]
public int GraphicId { get; set; }
public virtual ICollection<GraphicArtwork> GraphicArtworks { get; set; }
}
public class GraphicArtwork
{
[Key]
public int GraphicArtworkId { get; set; }
public int GraphisId { get; set; }
[Required(ErrorMessage = "The {0} is required.")]
[StringLength(500)]
public string ArtOptionText { get; set; }
public decimal Price { get; set; }
[DisplayName("Active")]
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
[NotMapped]
public int TotalRecordCount { get; set; }
}
I have this two table and model. Here I have one Graphics with Many Art Work option. I have created my Artwork Option in view using Knockout.js. now my problem is that How to post(Action method) these data so that I can insert into GraphicArtwork table.
[HttpPost]
public ActionResult Add(Graphic data)
{
// "data" will contain all posted values
}

EF5: DB First. Generated Models and Custom Validation

If I use Code First Development Model, I have a full control of my code.
For Example, I have model: User.
public class User {
[Key]
public int id { get; set; }
[StringLength(50, ErrorMessage = "* Length might be less then 50 symbols")]
[Required(ErrorMessage = "* Login can't be empty")]
public string Login { get; set; }
[StringLength(50, ErrorMessage = "* Length might be less then 50 symbols")]
[Required(ErrorMessage = "* Name can't be empty")]
public string FirstName { get; set; }
[StringLength(50, ErrorMessage = "* Length might be less then 50 symbols")]
[Required(ErrorMessage = "* Last name can't be empty")]
public string LastName { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public string Phone { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
[Required]
public DateTime LastLogin { get; set; }
public List<Post> PostList { get; set; }
}
I have ability to create my own validation, like at model "User".
In case, if I use DB First Development Model, I need to use ADO.NET Entity Data Model to generate models. I have 3 tables:
I have generated files:
DBContext.edmx
- DBContext.Context.tt
- DBContext.Designer.cs
- DBContext.edmx.diagram
- DBContext.tt
- Comment.cs
- DBContext.cs
- Post.cs
- Comment.cs
and code below:
public partial class DBContext : DbContext
{
public DBContext()
: base("name=DBContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Comment> Comment { get; set; }
public DbSet<Post> Post { get; set; }
public DbSet<User> User { get; set; }
}
public partial class User
{
public User()
{
this.Comment = new HashSet<Comment>();
this.Comment1 = new HashSet<Comment>();
this.Post = new HashSet<Post>();
this.Post1 = new HashSet<Post>();
}
public int id { get; set; }
public string Login { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public System.DateTime CreatedAt { get; set; }
public System.DateTime LastLogin { get; set; }
public virtual ICollection<Comment> Comment { get; set; }
public virtual ICollection<Comment> Comment1 { get; set; }
public virtual ICollection<Post> Post { get; set; }
public virtual ICollection<Post> Post1 { get; set; }
public virtual User User1 { get; set; }
public virtual User User2 { get; set; }
}
public partial class Post
{
public Post()
{
this.Comment = new HashSet<Comment>();
}
public int id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public string TextFormatted { get; set; }
public System.DateTime CreatedAt { get; set; }
public System.DateTime UpdatedAt { get; set; }
public Nullable<int> CreatedById { get; set; }
public Nullable<int> UpdatedById { get; set; }
public Nullable<int> UserId { get; set; }
public virtual User User { get; set; }
public virtual User User1 { get; set; }
public virtual ICollection<Comment> Comment { get; set; }
}
public partial class Comment
{
public int id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public string TextFormatted { get; set; }
public System.DateTime CreatedAt { get; set; }
public System.DateTime UpdatedAt { get; set; }
public Nullable<int> CreatedById { get; set; }
public Nullable<int> UpdatedById { get; set; }
public Nullable<int> PostId { get; set; }
public virtual User User { get; set; }
public virtual User User1 { get; set; }
public virtual Post Post { get; set; }
public virtual Comment Comment1 { get; set; }
public virtual Comment Comment2 { get; set; }
}
Questions:
1. As I understand, if I use DB First Development Model, I can't use my own models for data access, just models/classes, generated by ADO.NET Entity Data Model?
I tried to use my own model "UserOwn" except generated "User", so I got an error "Unable to retrieve metadata for 'TestDBFirst02.Models.UserOwn'". Expected.
2. Can I use both Development Model: Code First and DB First inside one project?
3. If I need to use generated models, what I need to do, when I want to use my own validation? I tried to modificate generated model, and it works:
[StringLength(50, ErrorMessage = "* Length might be less then 50 symbols")]
[Required(ErrorMessage = "* Login can't be empty")]
public string Login { get; set; }
But, if I need to update model from DB, of course my validation code overwrites by ADO.NET Entity Data Model and attributes disappear. How can I overcome the situation with my own validation?
Answer the 3-rd question: need to use [MetadataTypeAttribute(typeof(UserValidation)] attribute and create validation class, so code is:
[MetadataType(typeof(UserValidation))]
public partial class User {}
public class UserValidation
{
[StringLength(50, ErrorMessage = "* Length might be less then 50 symbols")]
[Required(ErrorMessage = "* Login can't be empty")]
public string Login { get; set; }
}
with the same! namespace, as User model has.
Read more:
http://www.asp.net/mvc/tutorials/mvc-5/database-first-development/enhancing-data-validation
http://jnye.co/Posts/19/adding-validation-to-models-created-by-entity-framework-database-first-c
http://weblogs.asp.net/scottgu/asp-net-mvc-2-model-validation
Database First Validation
Adding validation to model with Database First model (EF 5)
http://www.elevenwinds.com/data-validation-in-asp-net-mvc-database-first

Foreign key is null despite being populated properly in database

I have the following entities in my context:
public class Asset
{
public int AssetId { get; set; }
public Registration Registration { get; set; }
[Required]
[Display(Name = "Asset Type")]
public AssetType AssetType { get; set; }
[Required]
[Range(Constants.SerialNumberStart, Constants.SerialNumberEnd)]
[DisplayFormat(DataFormatString = "{0:d8}")]
public int SerialNumber { get; set; }
[Required]
[Range(Constants.EquipNumberStart, Constants.EquipNumberEnd)]
[DisplayFormat(DataFormatString = "{0:d12}")]
public long EquipNumber { get; set; }
[Required]
[Display(Name = "Profile")]
[Range(Constants.ProfileStart, Constants.ProfileEnd)]
[DisplayFormat(DataFormatString = "{0:d2}")]
public int Profile { get; set; }
}
public class AssetType
{
public int AssetTypeId { get; set; }
public List<Asset> Asset { get; set; }
[Required]
[Display(Name = "Asset Type")]
[StringLength(40)]
public string AssetTypeFullName { get; set; }
[Required]
[Display(Name = "Asset Alias")]
[StringLength(8)]
public string AssetTypeShortName { get; set; }
}
I've seeded my database and everything looks fine, the AssetType foreign key properly shows the index into my AssetType table data.
But when I try to get the data ready for the View in the code below, the AssetType key/data is null.
public ActionResult Index()
{
Logger.Debug("Index");
RegistrationServerContext dbNotLazy = new RegistrationServerContext();
return View(dbNotLazy.Assets.ToList());
}
When I set a break point and examine my dbNotLazy.Asset data, the AssetType foreign key is null.
I thought this might have to do with Lazy Loading, but as you can see, I create a new context before my View call. What might I be doing wrong?
You may need to use an Include - change
return View(dbNotLazy.Assets.ToList());
to
return View(dbNotLazy.Assets.Include("AssetType").ToList());
Lazy loading doesn't always seem to work immediately.

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).