This question already has answers here:
EF7 RC1 : Disable Cascade Delete
(2 answers)
Closed 7 years ago.
In Entity Framework 7 when I am trying to apply a migration I get the error
Introducing FOREIGN KEY constraint 'FK_ChangeOrder_User_CreatedByID' on table 'ChangeOrder' 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.
I know in older versions of Entity Framework you would deal with this by adding
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
to the DbContext but in EF7 modelBuilder does not seem to have a .Conventions to it and google is only returning older EF 4 though EF 6 results.
How do I specific the ON DELETE NO ACTION constraint in Entity Framework 7?
Edit:
The answer provided by Oleg will apparently do it per Foreign Key but I would like to do it globally as it will much easier to use one line of code to declare this globally then have to specify code it out for every single one of the hundreds of relationships I will end up having.
Edit 2: Code for Oleg
public class ChangeOrder
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public Int16? ApprovedByID { get; set; }
public Byte ApprovalStatusID { get; set; }
public Int16 AssignedToID { get; set; }
public Int16 CreatedByID { get; set; }
public Byte CurrentStatusID { get; set; }
public DateTime? DateApproved { get; set; }
public DateTime? EndDate { get; set; }
public Byte ImpactID { get; set; }
public Byte PriorityID { get; set; }
public DateTime? StartDate { get; set; }
public Byte TypeID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string ReasonForChange { get; set; }
[ForeignKey("ApprovedByID")]
public User ApprovedBy { get; set; }
[ForeignKey("ApprovalStatusID")]
public ChangeApprovalStatus ApprovalStatus { get; set; }
[ForeignKey("AssignedToID")]
public User AssignedTo { get; set; }
[ForeignKey("CreatedByID")]
public User CreatedBy { get; set; }
[ForeignKey("ImpactID")]
public ChangeImpact Impact { get; set; }
[ForeignKey("PriorityID")]
public ChangePriority Priority { get; set; }
[ForeignKey("TypeID")]
public ChangeType ChangeType { get; set; }
[ForeignKey("CurrentStatusID")]
public ChangeStatus CurrentStatus { get; set; }
}
public class JobSightDBContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
base.OnModelCreating(modelbuilder);
}
DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }
DbSet<ChangeImpact> ChangeImapct { get; set; }
DbSet<ChangeOrder> ChangeOrders { get; set; }
DbSet<ChangePriority> ChangePriorities { get; set; }
DbSet<ChangeStatus> ChangeStatus { get; set; }
DbSet<ChangeType> ChangeTypes { get; set; }
DbSet<User> Users { get; set; }
}
After digging around on GitHub, and working with a very patient guy from MS there, the current solution is to add this to the DbContext
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelbuilder);
}
The construction
modelBuilder.Entity("myNamespace.Models.ChangeOrder", b =>
{
b.HasOne("myNamespace.Models.User")
.WithMany()
.HasForeignKey("CreatedByID")
.OnDelete(DeleteBehavior.Cascade);
});
will means creating FK_ChangeOrder_User_CreatedByID with REFERENCES [dbo].[User] ([CreatedByID]) ON DELETE CASCADE. It should exist in protected override void BuildModel(ModelBuilder modelBuilder) of YourContextModelSnapshot.cs created during migration. I'm not sure that I full understand your question, but I think that you should either add such construct to XXXModelSnapshot.cs or to remove unneeded construct, which already exist here.
UPDATED: I see that you have the problem in the Model. You have the following properties in
public Int16? ApprovedByID { get; set; }
public Int16 AssignedToID { get; set; }
public Int16 CreatedByID { get; set; }
// navigation properties
[ForeignKey("ApprovedByID")]
public User ApprovedBy { get; set; }
[ForeignKey("AssignedToID")]
public User AssignedTo { get; set; }
[ForeignKey("CreatedByID")]
public User CreatedBy { get; set; }
By default migration try to set DeleteBehavior.Cascade on all the properties.
You can overwrite the behavior by changing OnModelCreating, which sets either DeleteBehavior.Restrict behavior for all the keys or to set on one only key the DeleteBehavior.Cascade or DeleteBehavior.SetNull behavior. For example, the below code uses DeleteBehavior.Cascade on CreatedByID (which creates ON DELETE CASCADE on the foreign keys) and DeleteBehavior.Restrict on other foreign keys (no ON DELETE on the foreign keys):
public class JobSightDBContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
base.OnModelCreating(modelbuilder);
modelbuilder.Entity(typeof (ChangeOrder))
.HasOne(typeof (User), "ApprovedBy")
.WithMany()
.HasForeignKey("ApprovedByID")
.OnDelete(DeleteBehavior.Restrict); // no ON DELETE
modelbuilder.Entity(typeof (ChangeOrder))
.HasOne(typeof (User), "AssignedTo")
.WithMany()
.HasForeignKey("AssignedToID")
.OnDelete(DeleteBehavior.Restrict); // no ON DELETE
modelbuilder.Entity(typeof (ChangeOrder))
.HasOne(typeof (User), "CreatedBy")
.WithMany()
.HasForeignKey("CreatedByID")
.OnDelete(DeleteBehavior.Cascade); // set ON DELETE CASCADE
}
DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }
DbSet<ChangeImpact> ChangeImapct { get; set; }
DbSet<ChangeOrder> ChangeOrders { get; set; }
DbSet<ChangePriority> ChangePriorities { get; set; }
DbSet<ChangeStatus> ChangeStatus { get; set; }
DbSet<ChangeType> ChangeTypes { get; set; }
DbSet<User> Users { get; set; }
}
Related
I'm trying to make an insert in a SQL database using Entity Framework 6 and I'm stuck on this issue that I cannot solve.
The error that I keep getting is :
UpdateException: Entities in 'Connect.CompanyFinancialDetails' participate in the 'Company_CompanyFinancialDetails' relationship. 0 related 'Company_CompanyFinancialDetails_Source' were found. 1 'Company_CompanyFinancialDetails_Source' is expected
I have these 2 entities:
public class Company
{
public long CUI { get; set; }
public string UserName { get; set; }
public string CompanyName { get; set; }
public string Symbol { get; set; }
public int? SharesCount { get; set; }
public decimal? SharePrice { get; set; }
public virtual Account Account { get; set; }
public virtual CompanyFinancialDetails CompanyFinancialDetails { get; set; }
}
public class CompanyFinancialDetails
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// other properties
public decimal? NumberOfEmployees { get; set; }
public virtual Company Company { get; set; }
}
This is the Fluent API configuration:
public DbSet<Account> SignUpModels { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyFinancialDetails> CompanyFinancialDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasKey(k => k.Id)
.HasOptional(s => s.Company)
.WithRequired(d => d.Account);
modelBuilder.Entity<Company>()
.HasKey(k => k.CUI)
.HasOptional(s => s.CompanyFinancialDetails)
.WithRequired(d => d.Company);
}
The relationship that I want to have is 1-many (one Company has many CompanyFinancialDetails).
This is the code where I add the objects to the database:
Company co = Context.Find(username);
foreach (CompanyFinancialDetails s in c)
{
s.Company = co;
}
a.CompanyFinancialDetails.AddRange(c);
a.SaveChanges();
I get a list of CompanyFinancialDetails and I add them using the AddRange method. I had this issue before and what I did was to add the virtual property object to the object that I wanted to insert in the database and it worked. This is what I tried to do here: the Find() method gets the company object that is related to the CompanyFinancialDetails and for each CompanyFinancialDetails object an Company virtual property is adding the related company object.
Well, it didn't work, when the SaveChanges() method is called, I get that error. Any help would be appreciated.
Hi I have problem with EF Core insert entity. The problem is that I need to insert new entity with relation to another one which is already existing. I have created the relations with fluent API. I have done this for two times. First I am creating car and adding the last edited by field with Identity user and all works but when I am trying to do the same with another entity it crashes down with
My fluent APi code which works good:
builder.Entity<Car>()
.HasOne(x => x.Owner)
.WithMany(x => x.OwnerCars)
.HasForeignKey(x => x.OwnerId);
Here is car entity:
public class Car : CarBase
{
[Key]
public int CarId { get; set; }
public bool IsTrailer { get; set; }
public virtual TrailerType TrailerType { get; set; }
public virtual int? TrailerTypeId { get; set; }
public virtual ApplicationUser Owner { get; set; }
public virtual string OwnerId { get; set; }
}
and here is Application user entity
public class ApplicationUser : IdentityUser
{
[MaxLength(100)]
public string Address { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime LastEditationDateTime { get; set; }
public virtual ApplicationUser LastEditedBy { get; set; }
public bool IsDeleted { get; set; }
public virtual DateTime DeletedDateTime { get; set; }
public ICollection<DriverLicenseApplicationUser> DriverLicenses { get; set; }
public ICollection<RideApplicationUser> Rides { get; set; }
public ICollection<Car> OwnerCars { get; set; }
public ICollection<Car> EditedCars { get; set; }
public ICollection<Trailer> EditedTrailers { get; set; }
public ICollection<Customer> EditedCustomers { get; set; }
}
To add this entity I only call this function and all works.
public Car CreateCar(Car car)
{
_context.Cars.Add(car);
return car;
}
But when I want to save this way this another entity type it shows an error. All steps are same so I do not understand this. Here I am adding the code I use to do that.
builder.Entity<Trailer>()
.HasOne(x => x.TrailerType)
.WithMany(x => x.Trailers)
.HasForeignKey(x => x.TrailerTypeId);
Here is Trailer:
public class Trailer : CarBase
{
[Key]
public int TrailerId { get; set; }
//[Required]
public virtual TrailerType TrailerType { get; set; }
public virtual int TrailerTypeId { get; set; }
}
and here is traylerTyper:
public class TrailerType:Trackable
{
//[Key]
public int TrailerTypeId { get; set; }
[MaxLength(100)]
[Required]
public string Type { get; set; }
public string Note { get; set; }
public ICollection<Car> TrailerTypeCars { get; set; }
public ICollection<Trailer> Trailers{ get; set; }
}
and the method is the same as the one already mentioned
public Trailer CreateTrailer(Trailer trailer)
{
trailer.TrailerTypeId = trailer.TrailerType.TrailerTypeId;
//_context.Attach(trailer.TrailerType);
var result = _context.Trailers.Add(trailer);
return result.Entity;
}
When I uncomment the attach it works but I think that I dont have to attach this because I have got the relation based on IDs and the example mentioned first works great. It gives me no sense. So if anyone could give me advice it would be awsome.
Here is the error I am getting:
Cannot insert explicit value for identity column in table 'TrailerTypes' when IDENTITY_INSERT is set to OFF.
It looks like the EF doesnt know that the traylertype entity already exists and is trying to insert the same entity again and the app crashes because it already exists and I am not allowing to insert IDs directly. As I said I have absolutely no idea why is this happening.
The problem is Lazy loading. Propetry from ViewModel is not completly same as property in Database and EF tracks whole graph of property in object and doesn´t recognize that it is the same object. The solution is to work only with IDs instead with whole objects.
I have two classes that relate to one another (one-to-many) and I thought I had the properties setup correctly, but when I run the Update-Database command for my migration, I get the following error:
Introducing FOREIGN KEY constraint
'FK_dbo.ParentEnrollment_dbo.CellGroup_CellGroupID' on table
'ParentEnrollment' 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 or index. See previous
errors.
My two classes:
[Table("CellGroup")]
public class CellGroup : BaseEntity
{
public Guid AcademicYearID { get; set; }
[ForeignKey("AcademicYearID")]
public virtual AcademicYear AcademicYear { get; set; }
public Guid LeaderID { get; set; }
[ForeignKey("LeaderID")]
public virtual Parent Leader { get; set; }
public Guid PreviousGroupID { get; set; }
[ForeignKey("PreviousGroupID")]
public virtual CellGroup PreviousGroup { get; set; }
public string Name { get; set; }
public int MaximumSize { get; set; }
public virtual ICollection<ParentEnrollment> Parents { get; set; }
}
and
[Table("ParentEnrollment")]
public class ParentEnrollment : BaseEntity
{
public Guid ParentID { get; set; }
[ForeignKey("ParentID")]
public virtual Parent Parent { get; set; }
public Guid AcademicYearID { get; set; }
[ForeignKey("AcademicYearID")]
public virtual AcademicYear AcademicYear { get; set; }
public bool FirstTimeEnrolling { get; set; }
public string HSLDAAccountNumber { get; set; }
public DateTime HSLDARenewalDate { get; set; }
public string CurrentChurch { get; set; }
public string CurrentChurchContact { get; set; }
public string CurrentChurchPhone { get; set; }
public Guid CellGroupID { get; set; }
[Required]
[ForeignKey("CellGroupID")]
public virtual CellGroup CellGroup { get; set; }
public bool VolunteerBuyOut { get; set; }
public Guid VolunteerPositionID { get; set; }
[ForeignKey("VolunteerPositionID")]
public virtual VolunteerPosition VolunteerPosition { get; set; }
public string VolunteerPositionNotes { get; set; }
public virtual ICollection<StudentEnrollment> StudentEnrollments { get; set; }
}
I only have the Parents property on the CellGroup class so I can easily access the list of enrollments in that cell group. I tried to remove the property to see if it cleared up the warning/error, but it did not. Can someone spot where I have gone wrong with my model(s)?
This error says that you cannot introduce a foreign key from table ParentEnrollment to table CellGroup that has cascading delete enabled, because this will create multiple cascade paths, which is not allowed on SQL Server.
According to the code you posted both tables have relations to a table Parent as well as AcademicYear, which are on non nullable FK columns, so EF will enable cascading on delete by default. With another FK from ParentEnrollment to CellGroup there would be multiple cascade paths, e.g. Parent to CellGroup to ParentEnrollment and Parent to ParentEnrollment, and this is causing your error. Removing the Parent property won't solve this because there still is the same cascading path problem starting from table AcademicYear.
So you have to disable cascading delete for your foreign key, which has to be done using Fluent API in your DbContext like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ParentEnrollment>()
.HasRequired(m => m.CellGroup)
.WithMany(m => m.Parents)
.HasForeignKey(m => m.CellGroupID)
.WillCascadeOnDelete(false);
}
I'm using ASP.NET MVC 4 Entity Framework 5 to generate a code first migration for a database structure of the below classes that have relationships between them. However I've encountered a problem where it's causing this error whenever I try to update my database from the migration:
Paste Bin of the migration file can be found here: http://pastebin.com/ngXacrKV
Error returned:
Introducing FOREIGN KEY constraint 'FK_dbo.Bookings_dbo.Rooms_RoomId' on table 'Bookings' 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.
Bunk.cs
public class Bunk
{
[Key]
public int BunkId { get; set; }
public BunkStatus BunkStatus { get; set; }
[ForeignKey("Room")]
public int RoomId { get; set; }
public virtual Room Room { get; set; }
// Added for convenience
public virtual ICollection<Booking> Bookings { get; set; }
}
Room.cs
public class Room
{
[Key]
public int RoomId { get; set; }
public string RoomName { get; set; }
public Gender RoomGender { get; set; }
public RoomStatus RoomStatus { get; set; }
public virtual ICollection<Bunk> Bunks { get; set; }
// Added for convenience
public virtual ICollection<Booking> Bookings { get; set; }
}
Bookings.cs
public class Booking
{
[Key]
public int BookingId { get; set; }
//[ForeignKey("UserProfile")]
//public int UserProfileId { get; set; }
//public UserProfile UserProfile { get; set; }
[ForeignKey("Bunk")]
public int BunkId { get; set; }
public Bunk Bunk { get; set; }
public int Duration { get; set; }
[ForeignKey("Preferred_Room")]
public int RoomId { get; set; }
public Room Preferred_Room { get; set; }
public Decimal Price { get; set; }
public BookingStatus BookingStatus { get; set; }
}
What would the best work around be to remove this issue without causing too much disturbance to the original class structure. I'm not too worried about adding new joining tables as long as I can still access the code in a Lazy loading way in my controllers/view models.
you can try to specify Fluent API to set no cascade delete
public class YOURContext: DbContext{
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
// here is where fluent API goes.
// I suspected the error is EF wanting a NO cascade delete. Hence the suggestion to try
entity<Booking>.HasOptional(t => t.Bunk)
.WithOptionalPrincipal()
.WillCascadeOnDelete(false); // <<<<<< this is the option to try.
// you may also need to try the same with Preferred_Room as well.
If no cascade delete is required then in Database context class we can set cascade delete to false for all the relationships by overriding OnModelCreating method as given below. Default EF makes it cascade delete that is why you are getting exception of cycles or multiple delete path.
public class MyContext: DbContext
{
//db sets defined
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PrimaryKeyTable>().HasMany(x => x.ChildTableCollection).WithRequired(x =>
Key).WillCascadeOnDelete(false);
//In your case
modelBuilder.Entity<Bunk>().HasMany(x => x.Bookings).WithRequired(x =>
x.BunkId).WillCascadeOnDelete(false);
// same for room if required.
}
}
How do I specify ON DELETE NO ACTION Foreign Key Constraint in my model designs?
At present, I have:
public class Status
{
[Required]
public int StatusId { get; set; }
[Required]
[DisplayName("Status")]
public string Name { get; set; }
}
public class Restuarant
{
public int RestaurantId { get; set; }
[Required]
public string Name { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
public string Telephone { get; set; }
[Required]
public int StatusId { get; set; }
public List<Menu> Menus { get; set; }
// NAVIGATION PROPERTIES
public virtual Status Status { get; set; }
}
public class Menu
{
public int MenuId { get; set; }
[Required]
public int RestaurantId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int StatusId { get; set; }
// NAVIGATION PROPERTIES
public virtual Status Status { get; set; }
public virtual Restaurant Restaurant { get; set; }
}
And my DbContext:
public class MenuEntities : DbContext
{
public DbSet<Status> Statuses { get; set; }
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<Menu> Menus { get; set; }
}
As you can see:
a Restaurant has many menus
a Restaurant has one status
a Menu belongs to 1 restaurant
Both Restaurants and Menus have 1 status. (Live, Invisible, Draft)
Naturally, if a status is deleted, I certainly don't want to cascade as this will muck everything up.
UPDATE:
Mark Oreta mentions using the following in his example below:
modelBuilder.Entity<FirstEntity>()
.HasMany(f => f.SecondEntities)
.WithOptional()
.WillCascadeOnDelete(false);
Where do I put this code? Within my MenuEntities / DbContext class?
Can anybody provide an example of this being used?
UPDATE:
Got this bit working now, however this has created a multiplicity constraint error when trying to seed the DB...
Multiplicity constraint violated. The role 'Menu_Status_Source' of the relationship 'LaCascadaWebApi.Models.Menu_Status' has multiplicity 1 or 0..1.
My Database Initialiser:
http://pastebin.com/T2XWsAqk
You can either disable it for your entire context by removing the cascade delete convention in the OnModelCreating method:
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
or, you can do it per relationship using a fluent mapping (also in the OnModelCreating):
EDIT: you would put it in your menu entities
public class MenuEntities : DbContext
{
public DbSet<Status> Statuses { get; set; }
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<Menu> Menus { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Menu>()
.HasRequired( f => f.Status )
.WithRequiredDependent()
.WillCascadeOnDelete( false );
modelBuilder.Entity<Restaurant>()
.HasRequired( f => f.Status )
.WithRequiredDependent()
.WillCascadeOnDelete( false );
}
}
Just make the FK property nullable, then the cascade delete will be gone.
public int? StatusId { get; set; }
After making the changes to the model, make sure you regenerate the migration file by adding the -Force parameter.
Add-Migration MigrationName -Force
add this line to end of the field in the context;
.OnDelete(DeleteBehavior.Restrict);
Put this into your MenuEntities class (class that descend from DbContext):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}