Code First with an existing database - entity-framework-4.1

I have a table in database which points to itself, i.e. parent_id >> category id. This is the ER diagram
I have modelled this table like following, but it gives *Error : 'Category': member names cannot be the same as their enclosing type :
public class Category
{
[Key]
public int category_id { get; set; }
public string category_name { get; set; }
public int category_parent { get; set; }
public string category_desc { get; set; }
public virtual Category Category { get; set; }
}
How should I model such tables ?

You have to make category_parent nullable and configure navigational property Category to the scalar property category_parent. Try to use proper naming convensions.
public class Category
{
[Key]
[Column("category_id")]
public int Id { get; set; }
[Column("category_name")]
public string Name { get; set; }
[Column("category_parent")]
public int? ParentId { get; set; }
[Column("category_desc")]
public string Description { get; set; }
[ForeignKey("ParentId")]
public virtual Category ParentCategory { get; set; }
}

I think you just need to change the Category property name to something else, so it is not the same as the class name...
public virtual Category SubCategory { get; set; }

public class Category
{
[Key]
public int category_id { get; set; }
public string category_name { get; set; }
public int category_parent { get; set; }
public string category_desc { get; set; }
public int parent_category_id { get; set; } <-- ADD & setup as foreign key
public virtual Category ParentCategory { get; set; } <-- Change name
public virtual ICollection<Category> Categories { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>.HasMany(cat => cat.Categories)
.WithRequired()
.HasForeignKey(cat => cat.parent_category_id);
}

Related

Circular references error when pulling JSON entities in EF Core

My Entities:
public partial class Student: IBrand
{
public Student()
{
Grades = new HashSet<Grade>();
}
[Key]
public int StudentId { get; set; }
[ForeignKey("Users")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
[ForeignKey("Parent")]
public int? ParentId { get; set; }
public Parent Parent { get; set; }
public int? SectionId { get; set; }
public string FatherName { get; set; }
public int classNumber { get; set; }
public Section Section { get; set; }
public string brevetResult { get; set; }
public DateTime? dateLeftAec { get; set; }
public string additionalInfo { get; set; }
public bool bacc { get; set; }
public string baccResult { get; set; }
public string baccSection { get; set; }
public int BrandId { get; set; }
[JsonIgnore]
public Brand Brand { get; set; }
public virtual ICollection<StudentRegistration> StudentReg { get; set; }
public virtual ICollection<Grade> Grades { get; set; }
public virtual ICollection<Absence> Absences { get; set; }
public virtual ICollection<StudentStudyYear> StudentStudyYears { get; set; }
}
public class Grade: IBrand
{
public int gradeId { get; set; }
public int grade { get; set; }
public int StudentId { get; set; }
public int SubjectId { get; set; }
public int TeacherId { get; set; }
public int TermId { get; set; }
public int SectionId { get; set; }
public bool IsApproved { get; set; }
public string ResultToEdit { get; set; }
public bool IsEditedByAdmin { get; set; }
public virtual Student Student { get; set; }
public virtual Subject Subject { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual Term Term { get; set; }
public virtual Section Section { get; set; }
public int BrandId { get; set; }
[JsonIgnore]
public Brand Brand { get; set; }
public virtual ICollection<GradeStudyYear> GradeStudyYears { get; set; }
}
I'm trying to get a student with his grades, but I encountered a problem with circular references. I tried to add this to the startup file:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
That seemed to solve the circular references problem, but then I started getting 5GB worth of data... so that code apparently just suppressed the error without solving the circular data problem.
I tried to put the attribute [JSONIgnore] in my grade.cs file, but I need to get a student from grade so it will not be useful.
How can I solve this circular references problem?
Serializing entities leads to a number of issues and exposes more information about your domain than is needed. When returning data to a view or an API consumer you can instead define a view model or DTO to contain just the details that consumer will need in whatever structure best serves that need. This avoids reference issues which EF navigation properties can cause, reduces the amount of domain knowledge and data you expose to clients, and minimizes the payload size to just what is needed. Data can be flattened, so if you are displaying a list of one entity, you don't need a ViewModel/DTO per related entity, your view model can merely contain relevant details of any related entity that applies to that consumer.
Once you have defined your view model / DTO you can use .Select() to populate it, or set up mapping with Automapper and populate it using .ProjectTo<TViewModel>().

Mapping table is empty

Below are two tables that has a many to many relation and and also another table that has a relation with the two first. A mapping table is created by Visual Studio with the name OrderEmployee, but when I run the application and enter some information to the Timeunit create form, the mapping table OrderEmployee is empty!
Should there not be some IDs added to that table since it has a relation with the Timeunit table or how is this mapping table thing working, when will there be data added to the mapping table? Or is something wrong with my Entity Classes?
public class Order
{
public int ID { get; set; }
public string Name { get; set; }
public int? ManufacturerID { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public int EmployeeNumber { get; set; }
public virtual ICollection<Timeunit> Timeunits { get; set; }
public virtual ICollection<Order> Order { get; set; }
}
public class Timeunit
{
public int ID { get; set; }
public int Week { get; set; }
public int HoursPerWeek { get; set; }
public int EmployeeID { get; set; }
public int? OrderID { get; set; }
public virtual Employee Employee { get; set; }
public virtual Order Order { get; set; }
}
EDIT:
Create Method for Timeunit:
public ActionResult Create([Bind(Include = "ID,Week,HoursPerWeek,EmployeeID,OrderID")] Timeunit timeunit)
{
if (ModelState.IsValid)
{
db.Timeunits.Add(timeunit);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ProjectID = new SelectList(db.Orders, "ID", "Name", timeunit.OrderID);
ViewBag.ResourceID = new SelectList(db.Employees, "ID", "Name", timeunit.EmployeeID);
return View(timeunit);
}

Code first generating strange column

I have an MVC 4 application that is using code first to generate tables and columns in my SQL Server DB. I am trying to figure out how I ended up with an additional TABLE that was not intended. I have looked through some questions but not found the exact same problem I am having. I will try to explain this simply.
I have added a model called Associate which keeps track of associates that my client does business with. Each Associate needs a foriegn key of AssociateTypedID and RegionID.
namespace XXX.Models
{
public class Associate
{
public int AssociateId { get; set; }
public string AssociateName { get; set; }
public int AddressNumber { get; set; }
public string AddressStreet { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string MainPhoneNumber { get; set; }
public string AssociateEmail { get; set; }
public string AssociateWebsite { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactPhoneNumber { get; set; }
public string ContactEmail { get; set; }
public int RegionId { get; set; }
public int AssociateTypeId { get; set; }
public virtual ICollection<AssociateType> AssociateTypes { get; set; }
public virtual ICollection<Region> Regions { get; set; }
}
}
AND
namespace XXX.Models
{
public class AssociateType
{
public int AssociateTypeId { get; set; }
public string AssociateTypeName { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
}
AND
namespace XXX.Models
{
public class Region
{
public int RegionId { get; set; }
public int RegionName { get; set; }
public int RegionDescription { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
}
AND
namespace XXX.Models
{
public class XXXDb : DbContext
{
public XXXDb(): base("name=DefaultConnection")
{
}
public DbSet<Associate> Associates { get; set; }
public DbSet<AssociateType> AssociateTypes { get; set; }
public DbSet<Region> Regions { get; set; }
}
}
So I have updated my code above and I'm getting very close to where I need to be in my database. I have the following tables generated.
Associates, AssociateTypes & Regions (each of them have the columns I would expect)
BUT I now have a new table called RegionAssociates which has the following columns:
Region_RegionId (int) & Associate_AssociateId (int)
This table was not expected or needed in my schema.
Your classes doesn't match your description of the model. You are saying
Each Associate can have a designation of AssociateType
I suppose that the same AssociateType can be assigned to more Associates, so there should be 1:N relationship between AssociateType and Associate.
But the Associate class defines the relationship the other way around - by convention public virtual ICollection<AssociateType> AssociateType { get; set; } creates 1:N relationship between Associate and AssociateType.
the correct definition of your classes would be
public class Associate
{
public int AssociateId { get; set; }
public string AssociateName { get; set; }
public int AddressNumber { get; set; }
public string AddressStreet { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zipcode { get; set; }
public string MainPhoneNumber { get; set; }
public string AssociateEmail { get; set; }
public string AssociateWebsite { get; set; }
public int RegionId { get; set; }
public int AssociateTypeId { get; set; }
public virtual AssociateType AssociateType { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactPhoneNumber { get; set; }
public string ContactEmail { get; set; }
public virtual ICollection<Region> Regions { get; set; }
}
public class AssociateType
{
public int AssociateTypeId { get; set; }
public string AssociateTypeName { get; set; }
public virtual ICollection<Associate> Associates { get; set; }
}
Can't say for sure what is missing from your configuration as you did't post it, but if you are using the fluent api something like this should fix the problem:
modelBuilder.Entity<AssociateType>()
.HasKey(t => t.AssociateTypeId);
modelBuilder.Entity<Associate>()
.HasRequired(t => t.AssociateType)
.WithRequiredPrincipal(t => t.Associate);
The above is adapted from this article http://msdn.microsoft.com/en-us/data/jj591620.aspx

Entity Framework Code 1st - Mapping many-to-many with extra info

I've looked through several of the questions here and am not quite connecting all the (mental) dots on this. I would appreciate some help.
My Models (code first):
public class cgArmorial
{
[Key]
[Display(Name = "Armorial ID")]
public Guid ArmorialID { get; set; }
[Display(Name = "User ID")]
public Guid UserId { get; set; }
public string Name { get; set; }
public string DeviceUrl { get; set; }
public string Blazon { get; set; }
public virtual ICollection<cgArmorialAward> ArmorialAwards { get; set; }
}
public class cgArmorialAward
{
public cgArmorial Armorial { get; set; }
public cgAward Award { get; set; }
public DateTime AwardedOn { get; set; }
}
public class cgAward
{
[Key]
[Display(Name = "Award ID")]
public Guid AwardID { get; set; }
public string Name { get; set; }
public string Group { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string Blazon { get; set; }
public virtual ICollection<cgArmorialAward> ArmorialAwards { get; set; }
}
Then in my Context class I have (last 2 entries):
public class Context : DbContext
{
public DbSet<cgUser> Users { get; set; }
public DbSet<cgEvent> Events { get; set; }
public DbSet<cgEventType> EventTypes { get; set; }
public DbSet<cgArmorial> Armorials { get; set; }
public DbSet<cgAward> Awards { get; set; }
public DbSet<cgArmorialAward> ArmorialAwards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<cgUser>()
.HasMany<cgEvent>(e => e.EventAutocrats)
.WithMany(u => u.EventAutocrats)
.Map(m =>
{
m.ToTable("EventAutocrats");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
modelBuilder.Entity<cgUser>()
.HasMany<cgEvent>(e => e.EventStaff)
.WithMany(u => u.EventStaff)
.Map(m =>
{
m.ToTable("EventStaff");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
modelBuilder.Entity<cgArmorialAward>()
.HasRequired(a => a.Armorial)
.WithMany(b => b.ArmorialAwards);
modelBuilder.Entity<cgArmorialAward>()
.HasRequired(a => a.Award)
.WithMany(); // b => b.ArmorialAwards
}
}
I am getting this error when I try to run:
System.Data.Edm.EdmEntityType: : EntityType 'cgArmorialAward' has no
key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ArmorialAwards�
is based on type �cgArmorialAward� that has no keys defined.
Well, as the exception says: You don't have a key defined on your entity cgArmorialAward. Every entity must have a key. Change it to the following:
public class cgArmorialAward
{
[Key, Column(Order = 0)]
[ForeignKey("Armorial")]
public Guid ArmorialID { get; set; }
[Key, Column(Order = 1)]
[ForeignKey("Award")]
public Guid AwardID { get; set; }
public cgArmorial Armorial { get; set; }
public cgAward Award { get; set; }
public DateTime AwardedOn { get; set; }
}
The fields in the composite key are foreign keys to the other two tables at the same time, hence the ForeignKey attribute. (I'm not sure if conventions would detect this automatically because you have non-standard names ("cgXXX" for the classes and "XXXId" for the foreign key properties). On the other hand the property names Armorial and Award match the foreign key property names. I'm not sure if EF conventions would consider this. So, perhaps the ForeignKey attribute is not necessary but at least it's not wrong.)

EF Code First Additional column in join table for ordering purposes

I have two entities that have a relationship for which I create a join table
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Image> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany(i => i.Images)
.WithMany(s => s.Students)
.Map(m => m.ToTable("StudentImages"));
}
I would like to add an additional column to allow chronological ordering of the StudentImages.
Where should I add insert the relevant code?
Do you want to use that new column in your application? In such case you cannot do that with your model. Many-to-many relation works only if junction table doesn't contain anything else than foreign keys to main tables. Once you add additional column exposed to your application, the junction table becomes entity as any other = you need third class. Your model should look like:
public class StudentImage
{
public int StudentId { get; set; }
public int ImageId { get; set; }
public int Order { get; set; }
public virtual Student Student { get; set; }
public virtual Image Image { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentImage> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public virtual ICollection<StudentImage> Students { get; set; }
}
And your mapping must change as well:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentImages>().HasKey(si => new { si.StudentId, si.ImageId });
// The rest should not be needed - it should be done by conventions
modelBuilder.Entity<Student>()
.HasMany(s => s.Images)
.WithRequired(si => si.Student)
.HasForeignKey(si => si.StudentId);
modelBuilder.Entity<Image>()
.HasMany(s => s.Students)
.WithRequired(si => si.Image)
.HasForeignKey(si => si.ImageId);
}