Entity Framework Fluent API - asp.net-mvc

My Entities are as follows...
public class Project{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Survey> Surveys { get; set; }
}
public class Survey{
public int Id { get; set; }
public int ProjectId { get; set; }
public string Name { get; set; }
public virtual Project Project { get; set; }
}
public class Category{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Survey> Surveys { get; set; }
}
public class SurveyCategory{
public int Id { get; set; }
public int SurveyId{ get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual Survey Survey { get; set; }
public virtual Category Category { get; set; }
}
A project will have list of Surveys, A Survey will have only one Category, A Category can have multiple Survey, SurveyCategory is the table where I am storing Survey + Category link.
Can anyone direct me to what would be appropriate Fluent API code will be for this to map properly.... So far I have this....
protected override void OnModelCreating(DbModelBuilder modelBuilder){
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Project>().HasMany(project => project.Surveys);}

hi I hope I understood what you are looking for.
First of al you should do a few changes to your model
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Survey> Surveys { get; set; }
}
public class Survey
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual Project Project { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<SurveyCategory> SurveyCategory { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Survey> Surveys { get; set; }
public virtual ICollection<SurveyCategory> SurveyCategory { get; set; }
}
public class SurveyCategory
{
public int Id { get; set; }
public string Name { get; set; }
public int SurveyId { get; set; }
public virtual Survey Survey { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
And then on the model creating you should do this
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasMany(p => p.Surveys)
.WithRequired(u => u.Project);
modelBuilder.Entity<Category>()
.HasMany(p => p.Surveys)
.WithRequired(u => u.Category);
modelBuilder.Entity<Category>()
.HasMany(p => p.SurveyCategory)
.WithRequired(u => u.Category)
.HasForeignKey(k => k.CategoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Survey>()
.HasMany(p => p.SurveyCategory)
.WithRequired(u => u.Survey)
.HasForeignKey(k => k.SurveyId)
.WillCascadeOnDelete(false);
}
I hope this helps

Related

Fluent API One to Many mapping table

It's really helpful if anyone could explain me how to create a mapping (associated)table for one to many relationship using Fluent API.`
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public List<Image> Images { get; set; }
}
public class Image
{
public int ID { get; set; }
public string Name { get; set; }
public List<Category> Category{ get; set; }
The mapping table should contain CategoryID and ImageID.
The solution should be something similar to this. (This is for many to many)
modelBuilder.Entity<Category>()
.HasMany(c => c.Images).WithMany(i => i.Categories)
.Map(t => t.MapLeftKey("CategoryID")
.MapRightKey("ImageID")
.ToTable("CategoryImage"));
I want Fluent API to create new mapping table for the below relationship.
public class Category
{
public List<Image> Images{get; set;}
}
public class Image
{
public Category Category{ get; set; }
}
Adding NewTable:
modelBuilder
.Entity<NewTable>()
.HasRequired(_ => _.Category)
.WithMany()
.HasForeignKey(_ => _.CategoryId);
modelBuilder
.Entity<Image>()
.HasRequired(_ => _.NewTable)
.WithMany(_ => _.Images)
.HasForeignKey(_ => _.NewTableId)
public class NewTable
{
public int ID { get; set; }
public int CategoryId { get; set; }
public List<Image> Images { get; set; }
public virtual Category Category { get; set; }
}
public class Image
{
public int ID { get; set; }
public string Name { get; set; }
public List<Category> Categories { get; set; }
public int NewTableId { get; set; }
public virtual NewTable NewTable { get; set; }
}

Entity Framework Many to Many Cascade Delete Issue

Having a strange issue working on a code first EF project.
I have the following entities:
public class Booking
{
public Guid BookingId { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
public class AccountingDocumentItem
{
public Guid AccountingDocumentItemId { get; set; }
public virtual List<Employee> Employees { get; set; }
public virtual List<Booking> Bookings { get; set; }
}
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
As you can see, there is meant to be many-to-many relationship between AccountingDocumentItem and both Bookings and Employees. When configuring my AccountingDocumentItem I use the following:
public AccountingDocumentItemConfiguration()
{
HasMany(x => x.Employees);
HasMany(x => x.Bookings);
}
What is strange is that this works perfectly for Employees. I get a AccountingDocumentItemEmployees table created. But for Bookings I get the following error:
"Introducing FOREIGN KEY constraint 'FK_dbo.AccountingDocumentItemBookings_dbo.Bookings_Booking_BookingId' on table 'AccountingDocumentItemBookings' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints."
Now I've tried to do this along the lines of below code:
HasMany(x => x.Bookings).WithMany(b => b.AccountingDocumentItems)...
But I only get the option to do a Map using the above line, no option to do a WillCascadeOnDelete(false).
Can someone point out what I'm doing wrong, because comparing it to how I handle Employees I can't see any difference.
EDIT:
My original post abbreviated the entities, which is probably where the problem is arising. Here is the full entity:
public class AccountingDocument
{
public Guid AccountingDocumentId { get; set; }
public Guid SiteId { get; set; }
public virtual Site Site { get; set; }
public Guid? ClientId { get; set; }
public virtual Client Client { get; set; }
public Guid? SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
public string DocumentNumber { get; set; }
public string Reference { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public Guid LinkedAccountingDocumentId { get; set; }
public virtual AccountingDocument LinkedAccountingDocument { get; set; }
public byte AccountingDocumentTypeId { get; set; }
public DateTime CreationDate { get; set; }
public DateTime DocumentDate { get; set; }
public decimal Total { get; set; }
public Guid UserId { get; set; }
public virtual User User { get; set; }
public string Room { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
public class AccountingDocumentItem
{
public Guid AccountingDocumentItemId { get; set; }
public Guid AccountingDocumentId { get; set; }
public virtual AccountingDocument AccountingDocument { get; set; }
public string Description { get; set; }
public Guid TaxId { get; set; }
public virtual Tax Tax { get; set; }
public decimal Quantity { get; set; }
public string Unit { get; set; }
public decimal Cost { get; set; }
public decimal SellInclusive { get; set; }
public decimal SellExclusive { get; set; }
public decimal DiscountPercentage { get; set; }
public decimal TotalInclusive { get; set; }
public decimal TotalExclusive { get; set; }
public decimal CommissionInclusive { get; set; }
public decimal CommissionExclusive { get; set; }
public int LoyaltyPoints { get; set; }
public bool IsSeries { get; set; }
public byte ItemType { get; set; }
public Guid? ServiceId { get; set; }
public virtual Service Service { get; set; }
public Guid? ProductId { get; set; }
public virtual Product Product { get; set; }
public Guid? VoucherId { get; set; }
public virtual Voucher Voucher { get; set; }
public int SortOrder { get; set; }
public Guid? SourceId { get; set; }
public virtual Source Source { get; set; }
public Guid? CostCentreId { get; set; }
public virtual CostCentre CostCentre { get; set; }
public Guid? ClientId { get; set; }
public virtual Client Client { get; set; }
public Guid PackageGroupId { get; set; }
public Guid PackageServiceId { get; set; }
public virtual List<Employee> Employees { get; set; }
public virtual List<Booking> Bookings { get; set; }
public virtual List<MedicalDiagnosis> MedicalDiagnoses { get; set; }
}
public class Booking
{
public Guid BookingId { get; set; }
public Guid SiteId { get; set; }
public Site Site { get; set; }
public Guid? ClientId { get; set; }
public Client Client { get; set; }
public Guid BookingStateId { get; set; }
public BookingState BookingState { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
And my configuration:
public class AccountingDocumentConfiguration : EntityTypeConfiguration<AccountingDocument>
{
public AccountingDocumentConfiguration()
{
Property(x => x.Reference).HasMaxLength(200);
HasRequired(x => x.Site);
Property(x => x.DocumentNumber).IsRequired().HasMaxLength(100);
Property(x => x.Reference).HasMaxLength(200);
Property(x => x.Description).HasMaxLength(500);
Property(x => x.Notes).HasMaxLength(500);
HasOptional(x => x.LinkedAccountingDocument);
Property(x => x.AccountingDocumentTypeId).IsRequired();
Property(x => x.CreationDate).IsRequired();
Property(x => x.DocumentDate).IsRequired();
Property(x => x.Total).IsRequired();
Property(x => x.Room).HasMaxLength(50);
}
}
public class AccountingDocumentItemConfiguration : EntityTypeConfiguration<AccountingDocumentItem>
{
public AccountingDocumentItemConfiguration()
{
Property(x => x.Description).IsRequired().HasMaxLength(200);
HasMany(x => x.Employees);
HasMany(x => x.Bookings);
HasMany(x => x.MedicalDiagnoses);
Property(x => x.Unit).HasMaxLength(50);
}
}
Even with the added text above it's working for me, once I comment out the added nav properties that aren't fully defined above. The FK error means that there might be a race condition if you happen to delete (See this article), but with whats here I can't tell. Do you need to have cascading deletes on your database? If not, you could just turn it off - I realize it's a pretty broad stroke on a minor problem though.
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
If this is not an option - it's something else that you haven't included. Do you have mappings for the Bookings table? It looks like Bookings have a required Site - is it possible that deleting a site, could trigger a delete in a bunch of other things? It looks like Site could do something like this Site -> Account Document -> Accounting Document item.. Site -> Booking possibly?
Here's another SO question that could possibly be related.

multiple "1 to 0..1" relationship models

I am using this tutorial from microsoft to create a one-zero-to-one relationship with EF4.1 Between an Instructor and OfficeAssignment. This is working like a charm.
But now I want to add a Home for each Instructor (1 to zero-or-1) like in this:
I added the Home model exactly the same way as the OfficeAssignment (like in the tutorial above), but when I try to add controllers for these model, I get the error "An item with the same name has already been added".
So my model is set up incorrectly.
What is wrong with the below?
How do I create multiple one-to-zero-to-one relationships in EF4.1?
public class Instructor
{
public Int32 InstructorID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
public virtual Home Home { get; set; }
}
public class OfficeAssignment
{
[Key]
public int InstructorID { get; set; }
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
public class Home
{
[Key]
public int InstructorID { get; set; }
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
public class Context : DbContext
{
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Home> Homes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment)
.WithRequired(p => p.Instructor);
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.Home).WithRequired(p => p.Instructor);
}
Doesn't look like EF supports real 1 to 0..1 relationship. You need a foreign key. And add the optional (int?) into the main model.
So I did this as follow, and it works like a charm.
public class Instructor
{
public Int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public int? OfficeAssignmentID { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
public int? HomeID { get; set; }
public virtual Home Home { get; set; }
}
public class OfficeAssignment
{
public int OfficeAssignmentID { get; set; }
public string Location { get; set; }
}
public class Home
{
public int HomeID { get; set; }
public string Location { get; set; }
}

MVC3 many-to-many with additional column Examples

which is the best way or if you have seen an example of which is the best way or a way to display to the user a view with this model and if you have and link example will be better
public class Student
{
public int StudentID { get; set; }
public string FirstMidName { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}

EF Code First giving problems in foreign keys

public class ParikshaContext :DbContext
{
public ParikshaContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<ParikshaContext>());
}
public DbSet<UserDetail> UserDetails { get; set; }
public DbSet<Standard> Standards { get; set; }
public DbSet<Subject> Subjects { get; set; }
public DbSet<QuestionDescriptor> QuestionDescriptors { get; set; }
public DbSet<QuestionBrief> QuestionBriefs { get; set; }
public DbSet<QuestionCustom> QuestionCustoms { get; set; }
public DbSet<QuestionChoice> QuestionChoices { get; set; }
public DbSet<QuestionMatch> QuestionMatches { get; set; }
public DbSet<Test> Tests { get; set; }
public DbSet<Test_Question> Test_Questions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<QuestionCustom>().ToTable("Custom");
modelBuilder.Entity<QuestionBrief>().ToTable("Brief");
modelBuilder.Entity<QuestionMatch>().ToTable("Match");
modelBuilder.Entity<QuestionChoice>().ToTable("Choice");
}
}
public class QuestionDescriptor
{
public int QuestionDescriptorId { get; set; }
public int StandardId { get; set; }
[ForeignKey("StandardId")]
public virtual Standard Standard { get; set; }
public int SubjectId { get; set; }
[ForeignKey("SubjectId")]
public virtual Subject Subject { get; set; }
public int Rating { get; set; }
public int Difficulty { get; set; }
public DateTime DateOfCreation{get;set;}
public int UserDetailId { get; set; }
[ForeignKeyAttribute("UserDetailId")]
public virtual UserDetail Creator { get; set; }
}
public class QuestionBrief : QuestionDescriptor
{
public String QuestionText { get; set; }
public String Answer { get; set; }
//true for fill in the blanks and false for a loing answers
public bool Short { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
public String StandardName { get; set; }
}
public class Subject
{
public int SubjectId { get; set; }
public String SubjectName { get; set; }
public String SubjectCategory { get; set; }
// public int StandardId { get; set; }
// [ForeignKey("StandardId")]
// public virtual Standard Standard { get; set; }
}
public class Test
{
public int TestID { get; set; }
public DateTime DateOfCreation { get; set; }
public String StandardName { get; set; }
public String SubjectName { get; set; }
public String SubjectCategory { get; set; }
// public int UserDetailId { get; set; }
// [ForeignKey("UserDetailId")]
// public virtual UserDetail Creator { get; set; }
}
public class Test_Question
{
public int Test_QuestionID { get; set; }
public int TestId { get; set; }
[ForeignKey("TestId")]
public virtual Test Test { get; set; }
public int QuestionDescriptorId { get; set; }
[ForeignKey("QuestionDescriptorId")]
public virtual QuestionDescriptor Question { get; set; }
}
public class UserDetail
{
public int UserDetailId { get; set; }
[Required]
[MaxLength(10, ErrorMessage = "UserName must be 10 characters or less"), MinLength(5)]
public String Name { get; set; }
[Required]
public String Password { get; set; }
public String UserRole { get; set; }
public DateTime DateOfCreation{ get; set;}
}
//Match,Custom,Choice classes have been omitted for lack of space (which sounds stupid when i look at the amount of code i have pasted )
I have two problems:-
I cant get a foreign key relation between standard and subjects,it says the relation will cause several cascade delete paths...
if I make a foreign key rlation between test and usedetail it gives me the above problem for mapping the tst_question table .
Also since I am new to EF code first ,please point out my mistakes.all help and disccussion is welcome.
By default EF will create foreign keys will cascade delete. In your model if you delete a Standard there are multiple paths to delete the QuestionDescriptor.
Standard -> QuestionDescriptor
and
Standard -> Subject -> QuestionDescriptor
That is why SQL server does not allow you to do this. See this answer for more details
What you can do is explicitly tell EF to create foreign keys without cascade delete. But this may create data integrity problems. So make sure you understand the consequences.
What you can do is configure the relationships using fluent API with WillCascadeOnDelete(false).
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//other mappings
modelBuilder.Entity<Subject>()
.HasRequired(subject => subject.Standard).WithMany()
.HasForeignKey(subject => subject.StandardId)
.WillCascadeOnDelete(false);
}

Resources