Entity Framework 4.3.1 how to create associations - entity-framework

my code like below
public class User
{
public int ID { get; set; }
public int BillingAddressID { get; set; }
public Address BillingAddress { get; set; }
public IList<Shipment> Shipments { get; set; }
}
public class Address
{
public int ID { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Shipment
{
public int ID { get; set; }
public string State { get; set; }
public int DeliveryAddressID { get; set; }
public Address DeliveryAddress { get; set; }
public User ShipUser { get; set; }
//[ForeignKey("ShipUser")]
public int ShipUserID { get; set; }
//public int UserId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Shipment> Shipments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Shipment>().HasRequired(u => u.ShipUser)
.WithMany(d => d.Shipments)
.HasForeignKey(c => c.ShipUserID)
.WillCascadeOnDelete(false);
}
}
if i remove the override method,i will get an error "SqlException: Introducing FOREIGN KEY constraint 'FK_Shipments_Users_ShipUserID' on table 'Shipments' 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."
if i remove ShipUserID in Shipment Class,it will work ok,when i see the table that is created by ef,i found a column named Shipment_UserID in table Shipment.I don`t know why.
if rename the class indenty key to UserID,it also work ok.
I try it anyway,but I don`t know the reason, I need some books about EF associations.

If you don't have mapping specified without cascadeDelete=false for one relationship it will create multiple cascade paths if you have tow relationships to user from Shipment.
By convention you can use public
Public User ShipUser { get; set; }
public int ShipUserID { get; set; }
it will use ShipUserID as foreign key by convention.
If you remove ShipUserID Ef need to create his own foreign key to keep the relationship . that is your ' Shipment_UserID'
rename the class indenty key to UserID I don't understand what you meant.
Here is a good tutorial to start with

Related

EF Core may cause cycles or multiple cascade paths failure

I'm trying to set the Model builder cascade property but I'm not getting the syntax correct.
Simple database:
I have a class (ReqForBetaReader):
public class ReqForBetaReader
{
public ReqForBetaReader()
{
Tags = new List<TagForReqBeta>();
}
public Guid Id { get; set; }
public string Title { get; set; }
public string Synopsis { get; set; }
public int WordCount { get; set; }
public Guid AuthorId { get; set; }
public virtual User Author { get; set; }
public DateTime UpdatedOn { get; set; }
public virtual ICollection<TagForReqBeta> Tags { get; set; }
}
I have my TagForReqBeta class
public class TagForReqBeta
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Guid ReqForBetaReaderId { get; set; }
public virtual ReqForBetaReader ReqForBetaReader { get; set; }
[Required]
public Guid UserId { get; set; }
public virtual User User { get; set; }
public DateTime AddedOn { get; set; }
}
So a ReqForBeta can have multiple tags.
When I ran Update-Database, I got the dreaded:
Introducing FOREIGN KEY constraint 'FK_TagsOnRequestForBetaReaders_RequestsForBetaReaders_ReqForBetaReaderId' on table 'TagsOnRequestForBetaReaders' 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.
I believe this means that when I delete a ReqForBeta, I should also delete all the TagForReqBeta as well, but I'm having trouble with the syntax. Any help?
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
Since ReqForBetaReaderId is not nullable, when you delete ReqForBetaReader record you will have to delete TagForReqBeta.
Since as I understand the same tag can be used for several records you have to make ReqForBetaReaderId nullable
[Required]
public Guid? ReqForBetaReaderId { get; set; }
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.ClientSetNull);
and as #IvanStoev noticed you have the same problem with User

Code First: FOREIGN KEY constraint may cause cycles or multiple cascade paths

I'm working on an application related to the education industry, and I have the following set of classes.
public class Person : EntityBase<long>
{
public string Surname { get; set; }
public string First { get; set; }
public string Middle { get; set; }
public Gender Gender { get; set; }
public DateTime Dob { get; set; }
}
public class Student: Person
{
public string AdmissionId { get; set; }
public string StudentId { get; set; }
public List<GuardianLink> Guardians { get; set; }
public StudentStatus Status { get; set; }
}
public class Guardian: Person
{
public GuardianType GuardianType { get; set; }
public List<GuardianLink> Students { get; set; }
}
public class GuardianLink: EntityBase<long>
{
[Required]
public Student Student { get; set; }
[Required]
public Guardian Guardian { get; set; }
public Boolean IsEmergencyContact { get; set; }
public Boolean IsAllowedToPickup { get; set; }
}
When I tried to create the database from these classes, I received an error that this would result in circular cascading deletes. So I added the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Guardian)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Student)
.WithMany()
.WillCascadeOnDelete(false);
}
I expected this to resolve the issue, but I'm still getting an error.
Introducing FOREIGN KEY constraint
'FK_dbo.GuardianLinks_dbo.People_Student_Id' on table 'GuardianLinks'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
I want to configure this so that deleting a student deletes the associated links, but not the guardian, and vice versa when deleting a guardian. Why didn't this resolve the issue? What should I be doing instead?

How to turn off cascading delete for more than one 1 to 1 table relationships to the same table in Entity Framework Code First Migrations

I have the following table structure simplified
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
public class Buyer
{
public int BuyerId { get; set; }
public int UserId { get; set; }
public UserProfile User { get; set; }
public ICollection<Order> Order { get; set; }
}
public class Seller
{
public int SellerId { get; set; }
public int UserId { get; set; }
public UserProfile User { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public int BuyerId { get; set; }
public int SellerId { get; set; }
public string Address { get; set; }
public <OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public int OrderId { get; set; }
public int OrderLineNo { get; set; }
public int StockId { get; set; }
public int Quantity { get; set; }
}
When I run Update_Database command I get the following error
Introducing FOREIGN KEY constraint 'FK_dbo.Buyers_dbo.UserProfile_UserId' on table 'Buyers' may cause cycles or multiple cascade paths.
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
My Buyer and Seller tables have a 1 to 1 relationship with to the UserProfile on UserId so I understand that when you delete a Buyer it can't delete the UserProfile since there might be a Seller and vice versa.
So I want to turn off cascading deletes for just the Buyer to the User and the Seller to the User using the fluid syntax but I can't get something to work so would appreciate if someone could help.
I have looked through the other StackOVerflow questions for the same error but couldn't find one that matches my scenario.
Extra: If I rename the UserId in my Buyer table to DummyUserId then the Uppdate-Database command works but this is not good.
Usually you can disable cascade deletes using .WillCascadeOnDelete(false);
So for your mapping, you may have something similar to the following:
modelBuilder.Entity<Buyer>()
.HasRequired(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);

M:M Mapping - EF 4.3 CodeFirst (Existing Database)

I have two tables (Table A, Table B) joined with a join table (TableAB) with 3 payload columns. By Payload I mean columns apart from Id, TableAId, and TableBId.
I can insert into all tables successfully, but I need to insert data into one of the payload columns on Insert. I'm using EF 4.3, Fluent API. Can anyone help? Thanks in advance.
public class Organisation : EntityBase<int>, IAggregateRoot
{
public string Name { get; set; }
public string Url { get; set; }
public int CountryId { get; set; }
public int? OwnershipTypeId { get; set; }
public int OrganisationStatusId { get; set; }
public virtual ICollection<Feature> Features { get; set; }
public virtual ICollection<OrganisationType> OrganisationTypes { get; set; }
public virtual ICollection<PricePlan> PricePlans { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User: EntityBase<Guid>, IAggregateRoot
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public int? PhoneCallingCodeId { get; set; }
public int? PhoneAreaCode{ get; set; }
public string PhoneLocal { get; set; }
public int? MobileCallingCodeId { get; set; }
public int? MobileAreaCode { get; set; }
public string MobileLocal { get; set; }
public virtual ICollection<Organisation.Organisation> Organisations { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int OrganisationRoleId {get; set;}//Foreign Key - have tried leaving it out, tried it as public virtual Organisation Organisation {get;set;
public bool IsApproved { get; set; }
}
public class SDContext : DbContext
{
public ObjectContext Core
{
get
{
return (this as IObjectContextAdapter).ObjectContext;
}
}
public IDbSet<User> User { get; set; }
public IDbSet<Organisation> Organisation { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Organisation>().HasMany(u => u.Users).WithMany(o => o.Organisations).Map(m =>
{
m.MapLeftKey("OrganisationId");
m.MapRightKey("UserId");
m.ToTable("OrganisationUser");
});
//I have tried specifically defining the foreign key in fluent, but I really need to understand how I can add the payload properties once I access and edit them.
Your mapping is not correct for your purpose. If you want to treat OrganisationUser as an intermediate entity between Organisation and User you must create relationships between Organisation and OrganisationUser and between User and OrganisationUser, not directly between Organisation and User.
Because of the intermediate entity which contains its own scalar properties you cannot create a many-to-many mapping. EF does not support many-to-many relationships with "payload". You need two one-to-many relationships:
public class Organisation : EntityBase<int>, IAggregateRoot
{
// ...
// this replaces the Users collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class User : EntityBase<Guid>, IAggregateRoot
{
// ...
// this replaces the Organisations collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public int OrganisationId { get; set; }
public Organisation Organisation { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
// ... "payload" properties ...
}
In Fluent API you must replace the many-to-many mapping by the following:
modelBuilder.Entity<Organisation>()
.HasMany(o => o.OrganisationUsers)
.WithRequired(ou => ou.Organisation)
.HasForeignKey(ou => ou.OrganisationId);
modelBuilder.Entity<User>()
.HasMany(u => u.OrganisationUsers)
.WithRequired(ou => ou.User)
.HasForeignKey(ou => ou.UserId);
Your derived DbContext may also contain a separate set for the OrganisationUser entity:
public IDbSet<OrganisationUser> OrganisationUsers { get; set; }
It's obvious now how you write something into the intermediate table:
var newOrganisationUser = new OrganisastionUser
{
OrganisationId = 5,
UserId = 8,
SomePayLoadProperty = someValue,
// ...
};
context.OrganisastionUsers.Add(newOrganisastionUser);
context.SaveChanges();
If you want to make sure that each pair of OrganisationId and UserId can only exist once in the link table, it would be better to make a composite primary key of those two columns to ensure uniqueness in the database instead of using a separate Id. In Fluent API it would be:
modelBuilder.Entity<OrganisationUser>()
.HasKey(ou => new { ou.OrganisationId, ou.UserId });
More details about such a type of model and how to work with it is here:
Create code first, many to many, with additional fields in association table

Foreign Keys in Entity Framework - Cycles or multiple cascade paths error

Using EF Code First i have the following, for example:
public class Blog
{
public int BlogID { get; set; }
public string Content { get; set; }
public virtual User User { get; set; }
public virtual ICollection<BlogMeta> BlogMeta { get; set; }
}
public class BlogMeta
{
public int BlogMetaID { get; set; }
public string Content { get; set; }
public virtual User User { get; set; }
public virtual Blog Blog { get; set; }
}
This successfully generates the tables Blog and BlogMeta and creates the foreign key relationship with the User table. After reading this i changed this to the following:
public class Blog
{
public int BlogID { get; set; }
public string Content { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual ICollection<BlogMeta> BlogMeta { get; set; }
}
public class BlogMeta
{
public int BlogMetaID { get; set; }
public string Content { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public int BlogID { get; set; }
public virtual Blog Blog { get; set; }
}
and now it doesn't work. It generates the tables and then throws the following error when trying to create the relationships:
Introducing FOREIGN KEY constraint 'BlogMeta_User' on table 'BlogMeta' may cause cycles or multiple cascade paths.
So what is the advantage of introducing the public int UserID and why does it fail when doing so?
EDIT:
Ok, so i've come across this answer which outlines the difference between Independent Associations and Foreign Key Associations... which it turns out is what i was talking about. So this leaves the question, why does it throw the above error when using foreign key associations?
As Ladislav mentioned, you are defining multiple cascade paths for BlogMeta entity. You'd have to disable cascade for one of your relationships.
You can add the following method to your context class, to diable cascade for your User-BlogMeta relationship:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogMeta>().HasRequired(bm => bm.User).WithMany().WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
You can indicate the other end of relationship (WithMany(u => u.BlogMetas)) if you have defined a colletion of BlogMeta in your User class.