How modelbuilder works - c#

i have a question about relations between entities created in Code-First:
I have Models:
public class ProjectGroup
{
[Key]
public int ProjectGroupID { get; set; }
public string ProjectGroupName { get; set; }
//FK
public virtual ICollection<File> Files { get; set; }
public virtual ICollection<List> Lists { get; set; }
}
public class File
{
[Key]
public int FileID { get; set; }
public int ProjectGroupID { get; set; }
[Required]
[Display(Name="Ścieżka pliku")]
public string FilePath { get; set; }
[Required]
[Display(Name="Data Zapisu")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd hh:mm:ss}", ApplyFormatInEditMode = true)]
public DateTime FileSaveDate { get; set; }//no NULL=>error
[Display(Name="Suma MD5")]
[StringLength(32)]
public string FileMD5Hash { get; set; }
public string IPHost { get; set; }
public int FileTemplateID { get; set; }
[ForeignKey("ProjectGroupID")]
public virtual ProjectGroup ProjectGroup { get; set; }
[ForeignKey("FileTemplateID")]
public virtual FileTemplate FileTemplate { get; set; }
public virtual ICollection<List> Lists { get; set; }//klucz obcy dla listy
}
public class List
{
[Key]
public int ListID { get; set; }
public int UserID { get; set; }
public int ProjectGroupID { get; set; }
public int FileID { get; set; }
public bool Modified { get; set; }
public bool Verified { get; set; }
public bool Alive { get; set; }
[ForeignKey("UserID")]
public virtual User User { get; set; } //referencja,przekazanie nazwy FK
[ForeignKey("ProjectGroupID")]
public virtual ProjectGroup ProjectGroup { get; set; }
[ForeignKey("FileID")]
public virtual File File { get; set; }
}
And Context:
public class AWZDContext : DbContext
{
public AWZDContext()
{
}
public DbSet<User> Users { get; set; }
public DbSet<File> Files { get; set; }
public DbSet<List> Lists { get; set; }
public DbSet<RemotePC> RemotePCs { get; set; }
public DbSet<UserType> UserTypes { get; set; }
public DbSet<ProjectGroup> ProjectGroups { get; set; }
public DbSet<FileTemplate> FileTemplates { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<File>()
.HasRequired(f=>f.ProjectGroup)
.WithMany(t=>t.Files)
.WillCascadeOnDelete(false);
modelBuilder.Entity<List>()
.HasRequired(c => c.ProjectGroup)
.WithMany(d=>d.Lists)
.WillCascadeOnDelete(false);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
when I created models, I got the following errors: problem with multi-cascade delete. I added modelbuilder in on model creating.
First I added no parameter in WithMany() and it doubled relations in database like this
This created many double relations between List and ProjectGroup (File, the same, read below).
when changed to WithMany(d=>d.Lists) relations looks ok, made only once like between File and ProjectGroup.
Does modelBuilder double the effect of [foreignKey] in model?
Can anyone explain how this works? Why did it double relation earlier, with no parameter in WithMany()

Related

EF Foreign Key constraint

I'm still studying Entity Framework and tried to create a model including the foreign keys.
But when I tried to migrate the code, I got this error
Introducing FOREIGN KEY constraint 'FK_dbo.QuestionResults_dbo.QuestionsTables_QuetionsTableId' on table 'QuestionResults' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
These are my model classes:
public class MainDetails
{
[Key]
public int Id { get; set; }
public string Language { get; set; }
[Required]
public string CustomerName { get; set; }
[Required]
public string ContactNumber { get; set; }
public string EmailAddress { get; set; }
[DisplayName("Service Type")]
[ForeignKey("QuestionsTable")]
public int ServiceTypeId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
[Required]
public string VehicleNumber { get; set; }
[Required]
public string ServiceLocation { get; set; }
public string Suggestion { get; set; }
public bool Status { get; set; } = true;
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Created Date")]
public DateTime CreatedDate { get; set; } = DateTime.Now;
public virtual QuestionResults QuestionResults { get; set; }
public virtual IList<QuestionResults> QuestionResultsMainlist { get; set; }
public virtual IList<QuestionsTable> QuestionsTables { get; set; }
}
public class QuestionsTable
{
[Key]
public int Id { get; set; }
public string ServiceType { get; set; }
public string Question { get; set; }
public virtual IList<MainDetails> MainDetailsServiceType { get; set; }
public QuestionsTable()
{
MainDetailsServiceType = new List<MainDetails>();
}
}
public class QuestionResults
{
[Key]
public int Id { get; set; }
[DisplayName("MainDetail ID")]
[ForeignKey("MainDetails")]
public int MainDetailsId { get; set; }
public virtual MainDetails MainDetails { get; set; }
[DisplayName("MainDetail ID")]
[ForeignKey("QuestionsTable")]
public int QuetionsTableId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
[Required]
public string CustoAnswer { get; set; }
}
This is the table structure I wanted to create:
To resolve this you can use the EF Model Builder
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<QuestionsTable>()
.HasRequired(a => a.MainDetails)
.WithOptionalDependent()
.WillCascadeOnDelete(false); //This is the important row
}
You'll have to play around with the type of relationship you would like along with whether you specify explicitly the foreign key. If you haven't seen this model builder before have a read here: https://learn.microsoft.com/en-us/ef/core/modeling/
Upon breaking your scenario down I noticed some "oddities". I reduced the noise in the domain model to from your example to this
public class MainDetails
{
[Key]
public int Id { get; set; }
[ForeignKey("QuestionsTable")]
public int ServiceTypeId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
public virtual QuestionResults QuestionResults { get; set; }
public virtual IList<QuestionResults> QuestionResultsMainlist { get; set; }
public virtual IList<QuestionsTable> QuestionsTables { get; set; }
}
public class QuestionsTable
{
[Key]
public int Id { get; set; }
public string ServiceType { get; set; }
public string Question { get; set; }
public virtual IList<MainDetails> MainDetailsServiceType { get; set; }
}
public class QuestionResults
{
[Key]
public int Id { get; set; }
[ForeignKey("MainDetails")]
public int MainDetailsId { get; set; }
public virtual MainDetails MainDetails { get; set; }
[ForeignKey("QuestionsTable")]
public int QuetionsTableId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
}
I few things I noted.
MainDetails contains both One-To-Many (QuestionTable) and Many-To-Many (IList) relationships? I'm unsure on your intention
QuestionsResults contains singular relationships to both entities which aren't replicated in the QuestionTable class? that's fine if it's intentional
ServiceType is a string in QuestionsTable but you are expecting an int as the foreign key in MainDetails?

EF6 Code First -relationship may cause cycles or multiple cascade paths

Introducing FOREIGN KEY constraint 'FK_dbo.Queries_dbo.Users_UserID' on table 'Queries' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
public partial class User
{
public User()
{
this.Alerts = new HashSet<Alert>();
this.DeviceTokens = new HashSet<DeviceToken>();
this.MobileNotifications = new HashSet<MobileNotification>();
this.Queries = new HashSet<Query>();
this.SendQueries = new HashSet<SendQuery>();
}
public int ID { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string SSOID { get; set; }
public Nullable<System.DateTime> LastLogin { get; set; }
public int LatestUpdatedRecord { get; set; }
public virtual ICollection<Alert> Alerts { get; set; }
public virtual ICollection<DeviceToken> DeviceTokens { get; set; }
public virtual ICollection<MobileNotification> MobileNotifications { get; set; }
public virtual ICollection<Query> Queries { get; set; }
public virtual ICollection<SendQuery> SendQueries { get; set; }
}
public partial class Query
{
public Query()
{
this.AlertEmails = new HashSet<AlertEmail>();
this.Alerts = new HashSet<Alert>();
this.QueryFacets = new HashSet<QueryFacet>();
}
public int ID { get; set; }
public int UserID { get; set; }
public string EntityType { get; set; }
public string Name { get; set; }
public string SearchTerm { get; set; }
public string OrderBy { get; set; }
public string QueryType { get; set; }
public string ReceiveUpdateTime { get; set; }
public Nullable<System.DateTime> NextSendTime { get; set; }
public bool IsActive { get; set; }
public string Token { get; set; }
public string AlertName { get; set; }
public bool Enabled { get; set; }
public bool GetNotifications { get; set; }
public string TimeFilterType { get; set; }
public string TimeFilterValue { get; set; }
public string RectangleFilter { get; set; }
public virtual ICollection<AlertEmail> AlertEmails { get; set; }
public virtual ICollection<Alert> Alerts { get; set; }
public virtual ICollection<QueryFacet> QueryFacets { get; set; }
public virtual User User { get; set; }
}
public partial class SearchAndAlertDbContext : DbContext
{
public virtual DbSet<AlertEmail> AlertEmails { get; set; }
public virtual DbSet<AlertingTime> AlertingTimes { get; set; }
public virtual DbSet<Alert> Alerts { get; set; }
public virtual DbSet<DeviceToken> DeviceTokens { get; set; }
public virtual DbSet<IgnoredSlide> IgnoredSlides { get; set; }
public virtual DbSet<Log> Logs { get; set; }
public virtual DbSet<MobileNotification> MobileNotifications { get; set; }
public virtual DbSet<Query> Queries { get; set; }
public virtual DbSet<QueryFacet> QueryFacets { get; set; }
public virtual DbSet<SendQuery> SendQueries { get; set; }
public virtual DbSet<StoredQuery> StoredQueries { get; set; }
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<BlockedUserForActivity> BlockedUserForActivities { get; set; }
public virtual DbSet<UserActivity> UserActivities { get; set; }
public virtual DbSet<UserActivityIgnoreList> UserActivityIgnoreLists { get; set; }
public virtual DbSet<UserActivityMonitor> UserActivityMonitors { get; set; }
public virtual DbSet<UserActivitySpecificSetting> UserActivitySpecificSettings { get; set; }
public virtual DbSet<WarnedUserForActivity> WarnedUserForActivities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
HasMany(p => p.Queries).
WithRequired(a => a.User).
HasForeignKey(a => a.UserID).WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
}
Tell EF not to cascade the query delete.
modelBuilder.Entity<Query>()
.HasRequired(q => q.User)
.WithMany(s => s.Queries)
.HasForeignKey(s => s.UserId)
.WillCascadeOnDelete(false);
Or turn off the convention:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

Entity Framework Unable to determine principal end of relationship. Multiple added entities may have same primary key

I'm having some issues with EF.
Migrations go fine, but when I try to run update-database I get the error :
Unable to determine the principal end of the 'LeagueInsight.Models.Image_Passive' relationship. Multiple added entities may have the same primary key.
Here are my models and config:
Passive.cs
public class Passive
{
public long Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string SanitizedDescription { get; set; }
// Navigation
public virtual Image Image { get; set; }
public virtual Champion Champion { get; set; }
}
Image.cs
public class Image
{
public long Id { get; set; }
public string Full { get; set; }
public string Group { get; set; }
public int H { get; set; }
public string Sprite { get; set; }
public int W { get; set; }
public int X { get; set; }
public int Y { get; set; }
// Navigation
public virtual Champion Champion { get; set; }
public virtual ChampionSpell ChampionSpell { get; set; }
public virtual Passive Passive { get; set; }
}
DBContext Configuration Partial:
modelBuilder.Entity<Passive>()
.HasRequired(p => p.Champion)
.WithRequiredPrincipal(c => c.Passive);
modelBuilder.Entity<Info>()
.HasRequired(i => i.Champion)
.WithRequiredPrincipal(c => c.Info);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Champion)
.WithRequired(c => c.Image);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Passive)
.WithRequired(p => p.Image);
base.OnModelCreating(modelBuilder);
It is just supposed to be a 1-1 relationship between the two, an I can't figure out why there would be multiple entities with the same Id here.
Edit: I was asked for the champion class:
public class Champion
{
public int Id { get; set; }
public string Blurb { get; set; }
public string Key { get; set; }
public string Lore { get; set; }
public string Name { get; set; }
public string Partype { get; set; }
public string Title { get; set; }
// Navigation
[InverseProperty("Ally")]
public virtual ICollection<Tip> Allytips { get; set; }
[InverseProperty("Enemy")]
public virtual ICollection<Tip> Enemytips { get; set; }
public virtual Image Image { get; set; }
public virtual Info Info { get; set; }
public virtual Passive Passive { get; set; }
public virtual ICollection<Recommended> Recommended { get; set; }
public virtual ICollection<Skin> Skins { get; set; }
public virtual Stats Stats { get; set; }
public virtual ICollection<ChampionSpell> Spells { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}

How to write a many-to-many relationship in Fluent in MVC4 and Entity Code First

I'm trying to write a many-to-many relationship in the override of the onModelCreating method of my context in ASP.NET MVC4. I think I have my classes wrong because I'm getting errors in Intellisense that I don't understand. Here is my override:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Software>().
HasMany(i => i.LocationId).
WithMany(c => c.SoftwareId).
Map(mc =>
{
mc.MapLeftKey("SoftwareId");
mc.MapRightKey("LocationId");
mc.ToTable("SoftwareLocations");
});
}
Here is my Software class:
public class Software
{
public int Id { get; set; }
public virtual List<SoftwareType> SoftwareTypes { get; set; }
public virtual List<Location> Locations { get; set; }
public virtual List<SoftwarePublisher> Publishers { get; set; }
[Required]
[StringLength(128)]
public string Title { get; set; }
[Required]
[StringLength(10)]
public string Version { get; set; }
[Required]
[StringLength(128)]
public string SerialNumber { get; set; }
[Required]
[StringLength(3)]
public string Platform { get; set; }
[StringLength(1000)]
public string Notes { get; set; }
[Required]
[StringLength(15)]
public string PurchaseDate { get; set; }
public bool Suite { get; set; }
public string SubscriptionEndDate { get; set; }
//[Required]
//[StringLength(3)]
public int SeatCount { get; set; }
public virtual Location LocationId { get; set; }
}
Here is my Location class:
public class Location
{
public int Id { get; set; }
[Required]
[StringLength(20)]
public string LocationName { get; set; }
public virtual Software SoftwareId { get; set; }
}
How do I write my Fluent override so I can map them correctly?
Many-to-Many means that in both entities is Collections. So you should set HasMany(software=>software.Locations) and set WithMany(location=>location.Softwares) as in example:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Software>().
HasMany(i => i.Locations).
WithMany(c => c.Softwares).
Map(mc =>
{
mc.MapLeftKey("SoftwareId");
mc.MapRightKey("LocationId");
mc.ToTable("SoftwareLocations");
});
}
Here is my Software class:
public class Software
{
public int Id { get; set; }
public virtual List<SoftwareType> SoftwareTypes { get; set; }
public virtual ICollection <Location> Locations { get; set; }
public virtual List<SoftwarePublisher> Publishers { get; set; }
[Required]
[StringLength(128)]
public string Title { get; set; }
[Required]
[StringLength(10)]
public string Version { get; set; }
[Required]
[StringLength(128)]
public string SerialNumber { get; set; }
[Required]
[StringLength(3)]
public string Platform { get; set; }
[StringLength(1000)]
public string Notes { get; set; }
[Required]
[StringLength(15)]
public string PurchaseDate { get; set; }
public bool Suite { get; set; }
public string SubscriptionEndDate { get; set; }
//[Required]
//[StringLength(3)]
public int SeatCount { get; set; }
}
Here is my Location class:
public class Location
{
public int Id { get; set; }
[Required]
[StringLength(20)]
public string LocationName { get; set; }
public virtual ICollection<Software> Softwares { get; set; }
}

MVC3 EF issues on multiple table

i am developing a database with 3 tables
Trip Model:
[Key]
public int TripId { get; set; }
[ForeignKey("Driver")]
public int DriverId { get; set; }
public virtual Driver Driver { get; set; }
public string StartingPoint { get; set; }
public string Destination { get; set; }
public DateTime TimeDepart { get; set; }
public int SeatAvailable { get; set; }
public virtual ICollection<Driver> Drivers { get; set; }
public virtual ICollection<Passenger> Passengers { get; set; }
Driver model:
[Key]
public int DriverId { get; set; }
public string DriverName { get; set; }
[ForeignKey("Trip"), Column(Order = 0)]
public int TripId { get; set; }
public virtual Trip Trip { get; set; }
And last passenger model:
[Key]
public int PassengerId { get; set; }
public string PassengerName { get; set; }
[ForeignKey("Trip"), Column(Order = 1)]
public int TripId { get; set; }
public virtual Trip Trip { get; set; }
With:
public class LiveGreenContext: DbContext
{
public DbSet<Trip> Trips { get; set; }
public DbSet<Driver> Drivers { get; set; }
public DbSet<Passenger> Passengers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
And i get the following error:
Model compatibility cannot be checked because the EdmMetadata type was
not included in the model. Ensure that IncludeMetadataConvention has
been added to the DbModelBuilder conventions.
Any solutions on this issue? Thanks!
Try adding a call to the Database.SetInitializer method in the Application_Start event handler of your Global.asax:
Database.SetInitializer<ContextName>(null);
where ContextName is the name of your custom DbContext class.

Categories