Entity Framework Many to Many Cascade Delete Issue - entity-framework-4

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.

Related

How to set the one-to-one relationship with fluent api in this case? (EF6)

I have this two entities:
public partial class Ficheros
{
public Guid Idfichero { get; set; }
public long Iddocumento { get; set; }
public byte[] Fichero { get; set; }
public virtual Documentos IddocumentoNavigation { get; set; }
}
public partial class Documentos
{
public Documentos()
{
ElementosDocumentos = new HashSet<ElementosDocumentos>();
}
public long Iddocumento { get; set; }
public string Nombre { get; set; }
public long? IdtipoDocumento { get; set; }
public string Codigo { get; set; }
public decimal? Espacio { get; set; }
public string Unidades { get; set; }
public long? Bytes { get; set; }
public virtual ICollection<ElementosDocumentos> ElementosDocumentos { get; set; }
public virtual Ficheros Ficheros { get; set; }
public virtual DocumentosTipos IdtipoDocumentoNavigation { get; set; }
}
In the database, IDFichero is an uniqueidentifier and in Documentos the IDDocumento is a big int autoincrement. The main table is Documentos, that has one and only one fichero, and it is requiered.
The examples that I have seen, it would make me that IDFichero was IDDocumento, but to store a file in the database I need that the ID is a uniqueidentifier.
Thanks.
The relationship you are describing in EF terms is one-to-one FK association with both ends required, Documentos being the principal and Ficheros the dependent.
EF does not support explicit FK for this type of association, so start by removing the Ficheros.Iddocumento property:
public partial class Ficheros
{
public Guid Idfichero { get; set; }
public byte[] Fichero { get; set; }
public virtual Documentos IddocumentoNavigation { get; set; }
}
then use the following fluent configuration:
modelBuilder.Entity<Documentos>()
.HasRequired(e => e.Ficheros)
.WithRequiredPrincipal(e => e.IddocumentoNavigation)
.Map(m => m.MapKey("Iddocumento"));

error deleting record from database in Entity Framework

I'm trying to delete a record from database table but I get this error
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TicketDetail_dbo.Person_PersonID". The conflict occurred in database "AbcDB", table "dbo.TicketDetail", column 'PersonID'
I'm sharing my code please guide me.
TicketDetail:
public class TicketDetail
{
[Key]
public int TicketDetailId { get; set; }
public int? GenericOrderId { get; set; }
public int? PartId { get; set; }
public int? JunkPartId { get; set; }
public int PersonID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual Part Part { get; set; }
public virtual Ticket Ticket { get; set; }
public virtual Customer Customer { get; set; }
public virtual JunkPart JunkPart { get; set; }
public virtual ICollection<MailSystem> MailSystems { get; set; }
public virtual ICollection<MailLog> MailLogs { get; set; }
}
Person:
public abstract class Person
{
[Key]
public int PersonID { get; set; }
public string City { get; set; }
public DateTimeOffset DateAdded { get; set; }
}
Customer:
public class Customer : Person
{
public int? CountryId { get; set; }
public string Company { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public virtual ICollection<TicketDetail> TicketDetails { get; set; }
public virtual ICollection<MailSystem> MailSystems { get; set; }
public virtual ICollection<MailLog> MailLogs { get; set; }
public virtual Country Country { get; set; }
}
Migration code:
modelBuilder.Entity<Customer>()
.HasMany<TicketDetail>(c => c.TicketDetails)
.WithRequired(x => x.Customer)
.WillCascadeOnDelete(true);
The error happens when I try to delete any customerId.
I have searched internet but couldn't find any appropriate solution, please help me with this error.

Renaming default table names in asp.net mvc project without breaking foreign keys

I Created a new MVC 5 project whenever i try to rename the table names as described in this question
Change table names using the new Identity system
It always breaks the foreign key relationships in the Logins, Claims, Roles tables.
I have tried to override the OnModelCreating method but in vein here is my code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//base.OnModelCreating(modelBuilder);
if (modelBuilder == null)
{
throw new ArgumentNullException("modelBuilder");
}
modelBuilder.Entity<App>().HasKey(m => new { m.AppId, m.FacebookId });
modelBuilder.Entity<IdentityUser>().ToTable("Admins");
modelBuilder.Entity<IdentityUser>().
Property(p => p.Id).HasColumnName("AdminId");
modelBuilder.Entity<Admin>()
.ToTable("Admins")
.Property(p => p.Id).HasColumnName("AdminId");
modelBuilder.Entity<IdentityUserLogin>().ToTable("Logins")
.HasKey(m => new { m.ProviderKey, m.UserId, m.LoginProvider })
.Property(m => m.UserId)
.HasColumnName("AdminId");
modelBuilder.Entity<IdentityUserRole>().ToTable("AdminRoles")
.HasKey(m => new { m.RoleId, m.UserId })
.Property(m => m.RoleId)
.HasColumnName("AdminRoleId");
modelBuilder.Entity<IdentityUserRole>().Property(m => m.UserId)
.HasColumnName("AdminId");
//modelBuilder.Entity<IdentityUserRole>().HasRequired(m => m.UserId);
//modelBuilder.Entity<IdentityUserLogin>().HasRequired(m => m.UserId);
modelBuilder.Entity<IdentityUserClaim>().ToTable("Claims")
.HasKey(m=> m.Id)
.Property(m => m.Id)
.HasColumnName("ClaimId");
modelBuilder.Entity<IdentityRole>().ToTable("Roles")
.HasKey(m => m.Id).Property(m => m.Id).HasColumnName("RoleId");
}
where the "Admin" Class is my name for the ApplicationUser Default class plus my own implementation of it
public class Admin : IdentityUser
{
[Required]
public virtual List<App> Apps { get; set; }
public bool? IsPremium { get; set; }
[DataType(DataType.Date)]
public DateTime? LastPublishDateTime { get; set; }
}
here are my other domain classes
public class App
{
[Key]
[Column("AppId", Order = 1)]
public virtual int AppId { get; set; }
[Required]
[Key]
[Column("FacebookId", Order = 2)]
public virtual string FacebookId { get; set; }
[Required]
public virtual string Secret { get; set; }
public virtual List<User> Users { get; set; }
public virtual List<Post> Posts { get; set; }
[Required]
public virtual Admin Admin { get; set; }
}
public class Post
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int PostId { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual string Link { get; set; }
public virtual string Image { get; set; }
public virtual bool IsSpecial { get; set; }
[Required]
public virtual App App { get; set; }
[Required]
public virtual DateTime? PublishDate { get; set; }
}
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int UserId { get; set; }
[MaxLength(500)]
public virtual string FacebookId { get; set; }
[MaxLength(500)]
public virtual string Token { get; set; }
[Required]
public virtual App App { get; set; }
}

Entity Framework Fluent API

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

Entityframework port from model first to code first

I am still on my quest to port from a Model First to Code First implementation of EntityFramework. I have made significant progress, with the help of Eranga. I have run into another snag, and I just cant explain what is hapening. I have two Entity objects Topic and Course
A Topic can have one Course that is required
A Course can have 0 or more topics
when i execute the following linq it generates wierd SQL
var topics = from o in db.Topics where o.ParentTopic == null &&
o.Course.Id == c.Id select o;
The SQL generated is
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[ShortDescription] AS [ShortDescription],
[Extent1].[LongDescription] AS [LongDescription],
[Extent1].[Property] AS [Property],
[Extent1].[Difficulty] AS [Difficulty],
[Extent1].[Weight] AS [Weight],
[Extent1].[Course_Id] AS [Course_Id],
[Extent1].[ParentTopic_Id] AS [ParentTopic_Id],
[Extent1].[Course_Id1] AS [Course_Id1]
FROM [dbo].[Topics] AS [Extent1]
WHERE ([Extent1].[ParentTopic_Id] IS NULL) AND ([Extent1].[Course_Id] = #p__linq__0)
Notice that there is an added field called Course_Id1 that is not in my object and not declared as a foreign key. I thought that in OnModelCreating() I had specified the parent child relationship correctly from both sides (I would have thought you only needed to do it from either side), but i cant get EntityFramework not to generate the extra field that obviously does not exist in the database. Remember my database was originally created using a ModelFirst approach.
Can anyone explain where the extra field is comming from????
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Topic
modelBuilder.Entity<Topic>()
.HasRequired(m => m.Course)
.WithMany(m=>m.Topics)
.HasForeignKey(m => m.Course_Id);
modelBuilder.Entity<Topic>()
.HasOptional(m => m.ParentTopic)
.WithMany(m => m.ChildTopics)
.HasForeignKey(m => m.ParentTopic_Id);
//////// lots of code removed for brevity. //////
modelBuilder.Entity<Course>()
.HasMany(m=>m.Topics)
.WithRequired(m => m.Course)
.HasForeignKey(m => m.Course_Id);
}
public partial class Topic
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string Property { get; set; }
public double? Difficulty { get; set; }
public double? Weight { get; set; }
[JsonIgnore]
public virtual Course Course { get; set; }
public int Course_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Question> Questions { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> ChildTopics { get; set; }
[JsonIgnore]
public virtual Topic ParentTopic { get; set; }
public int? ParentTopic_Id { get; set; }
[JsonIgnore]
public virtual ICollection<RTIQueueEntryData> RTIQueueEntryData { get; set; }
[JsonIgnore]
public virtual ICollection<Intervention> Interventions { get; set; }
[JsonIgnore]
public virtual ICollection<RtiStudentGroup> RtiStudentGroups { get; set; }
}
public partial class Course
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public string Version { get; set; }
public string Year { get; set; }
public string ImportedId { get; set; }
[Required]
public string LocalCourseNumber { get; set; }
[Required]
public string NCESCourseNumber { get; set; }
[Required]
public string StateCourseNumber { get; set; }
public int? Grade { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
[JsonIgnore]
public virtual Department Department { get; set; }
public int DepartmentId { get; set; }
[JsonIgnore]
public virtual ICollection<StudentGroup> StudentGroups { get; set; }
[JsonIgnore]
public virtual ICollection<CutPointTemplate> CutPointTemplates { get; set; }
[JsonIgnore]
public virtual School School { get; set; }
public int School_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Staff> RTIStaff { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> Topics { get; set; }
}
You have another relationship between Course and Topic created by convention due to this navigation property:
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
EF will put an (invisible, not exposed) end of the relationship into the Topic class. By default the relationship is one-to-many. Hence you get an additional foreign key property in the Topics table (= Course_Id1).

Resources