These are my tables:
Is there a standard way to apply the DELETE ON CASCADE between them ?
In one-to-many relationship I had no problem, but in this case I had to manually remove with a method written by me.
As you showed on the picture you have only two tables. It is not possible to set relationship between them as many-to-many (only if you will add new columns to this tables when new pairs will appear, but this is very bad practice). You should create third table, which will contain pairs of their primary keys. And at your migration you will be able to specify cascadeDelete to true between each of main tables and this third one. See below:
Models:
public class BugReport
{
public BugReport()
{
dublicates = new HashSet<DublicateBugReport>();
}
public int ID { get; set; }
public virtual ICollection<DublicateBugReport> dublicates { get; set; }
}
public class DublicateBugReport
{
public DublicateBugReport()
{
reports = new HashSet<BugReport>();
}
public int ID { get; set; }
public virtual ICollection<BugReport> reports { get; set; }
}
Piece of Migration:
public override void Up()
{
CreateTable(
"BugReports",
c => new
{
ID = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ID) ;
CreateTable(
"DublicateBugReports",
c => new
{
ID = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ID) ;
CreateTable(
"DublicateBugReportBugReports",
c => new
{
DublicateBugReport_ID = c.Int(nullable: false),
BugReport_ID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.DublicateBugReport_ID, t.BugReport_ID })
//pay attention! - cascadeDelete: true
.ForeignKey("DublicateBugReports", t => t.DublicateBugReport_ID, cascadeDelete: true)
.ForeignKey("BugReports", t => t.BugReport_ID, cascadeDelete: true)
.Index(t => t.DublicateBugReport_ID)
.Index(t => t.BugReport_ID);
}
Related
public class User
{
[Key]
public string userID { get; set; }
public string username { get; set; }
public virtual ICollection<Log> logs { get; set; }
public User() { }
}
This is my class that I use for my DbSet<User> but the initial migration only picks up the userID and userName
public override void Up()
{
CreateTable(
"dbo.Logs",
c => new
{
logID = c.String(nullable: false, maxLength: 128),
logString = c.String(),
logDatetime = c.DateTime(nullable: false),
User_userID = c.String(maxLength: 128),
})
.PrimaryKey(t => t.logID)
.ForeignKey("dbo.Users", t => t.User_userID)
.Index(t => t.User_userID);
CreateTable(
"dbo.Users",
c => new
{
userID = c.String(nullable: false, maxLength: 128),
username = c.String(),
})
.PrimaryKey(t => t.userID);
}
So because it does not make that field, I cannot add to the logs because its non-existent can anyone explain to me how to get the table to add the ICollection<Logs> logs?
ICollection logs is not a field.
It is an in memory collection that gets populated from the logs table.
I am trying to formulate one to one mapping but Entity framework always forming wrong foreign keys. I have tried all possible combinitions for stting up directions using Fluent API. But I have no idea why this is doing so.
My Model heirarchy is :
(Principal Table) Packaging
(Dependent Table) FortressPackaging.
Their respective models are
public class Packaging
{
public int PackagingId { get; set; }
[Display(Name = "Weight")]
public double? Weight { get; set; }
.................................
..........................
//navigation property....
public virtual FortressPackaging FortressPackaging { get; set; }
}
public class FortressPackaging
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FortressPackagingId { get; set; }
[StringLength(2)]
[Index("IX_FortressPackaging_FortressKey", 1, IsUnique = true)]
public string FortressKey { get; set; }
[Index("IX_FortressPackaging_PackagingId", 2, IsUnique = true)]
public int PackagingId { get; set; } // Foreign key to Packaging ( Principal Table)
public virtual Packaging Packaging { get; set; }
}
I followed following Fluent API
modelBuilder.Entity<FortressPackaging>()
.HasKey(Pr => Pr.FortressPackagingId)
.Property(Pr => Pr.FortressPackagingId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<FortressPackaging>()
.HasRequired(Pr => Pr.Packaging);
and when I performed migration, I got the following script.
public override void Up()
{
CreateTable(
"dbo.FortressPackaging",
c => new
{
FortressPackagingId = c.Int(nullable: false, identity: true),
FortressKey = c.String(maxLength: 2),
PackagingId = c.Int(),
})
.PrimaryKey(t => t.FortressPackagingId)
.ForeignKey("dbo.Packaging", t => t.FortressPackagingId) // SHOULD BE t=>t.PackagingId instead of t.FortressPackagingId
.Index(t => t.FortressPackagingId)
.Index(t => t.FortressKey, unique: true, name: "IX_FortressPackaging_FortressKey")
.Index(t => t.PackagingId, unique: true, name: "IX_FortressPackaging_PackagingId");
}
Here I don't know Why it is mapping FortressPackagingId as foriegn key to the FortressPackaging table while it is already a mapped as primary key which I explicitly marked while model creation.
I want to have following relation.
Packaging may or may not have a FortressPackaging while FortressPackaging always have a corresponding packaging. ( so One->0->One mapping )
How can I have correctly mapped foreign key
Edit : After removing Foreign Key Property from FortressPackaging.
public class FortressPackaging
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FortressPackagingId { get; set; }
[StringLength(2)]
[Index("IX_FortressPackaging_FortressKey", 1, IsUnique = true)]
public string FortressKey { get; set; }
[Index("IX_FortressPackaging_PackagingId", 2, IsUnique = true)]
public int PackagingId { get; set; }
public Packaging Packaging { get; set; }
}
Created migration again with the same contents as before..
public override void Up()
{
CreateTable(
"dbo.FortressPackaging",
c => new
{
FortressPackagingId = c.Int(nullable: false, identity: true),
FortressKey = c.String(maxLength: 2),
PackagingId = c.Int(nullable: false),
})
.PrimaryKey(t => t.FortressPackagingId)
.ForeignKey("dbo.Packaging", t => t.FortressPackagingId)
.Index(t => t.FortressPackagingId)
.Index(t => t.FortressKey, unique: true, name: "IX_FortressPackaging_FortressKey")
.Index(t => t.PackagingId, unique: true, name: "IX_FortressPackaging_PackagingId");
}
Followed : https://stackoverflow.com/a/26012062/273330
Fluent API :
modelBuilder.Entity<FortressPackaging>()
.HasRequired(Pr => Pr.Packaging)
.WithMany()
.HasForeignKey(Pr=>Pr.PackagingId)
.WillCascadeOnDelete(false);
Migration Script created in result of above fluent api :
public override void Up()
{
CreateTable(
"dbo.FortressPackaging",
c => new
{
FortressPackagingId = c.Int(nullable: false, identity: true),
FortressKey = c.String(maxLength: 2),
PackagingId = c.Int(nullable: false),
})
.PrimaryKey(t => t.FortressPackagingId)
.ForeignKey("dbo.Packaging", t => t.PackagingId)
.Index(t => t.FortressKey, unique: true, name: "IX_FortressPackaging_FortressKey")
.Index(t => t.PackagingId, unique: true, name: "IX_FortressPackaging_PackagingId");
AddColumn("dbo.Packaging", "FortressPackaging_FortressPackagingId", c => c.Int());
CreateIndex("dbo.Packaging", "FortressPackaging_FortressPackagingId");
AddForeignKey("dbo.Packaging", "FortressPackaging_FortressPackagingId", "dbo.FortressPackaging", "FortressPackagingId");
}
I don't know why it is creating totally new columns inside Packaging table.
I've been wasting two days now to try solve this problem but have yet to find a solution.
In my code that saves an entity with a relationship, I get this error when reaching ctx.SaveChanges():
Unable to determine a valid ordering for dependent operations.
Dependencies may exist due to foreign key constraints, model
requirements, or store-generated values.
Shipment.cs
[ForeignKey("ShipmentNumber")]
public int? DefaultShipmentNumber { get; set; }
public virtual ShipmentNumber ShipmentNumber { get; set; }
ShipmentNumber.cs
[Column("shipment_id")]
[ForeignKey("Shipment")]
public byte ShipmentId { get; set; }
public virtual Shipment Shipment { get; set; }
To avoid circular references, ShipmentNumber belonging to Shipment is nullable (optional), whereas ShipmentNumber's dependency on Shipment is required.
I first create a Shipment, add it and then attach a ShipmentNumber to it and add it to table as well.
Here's the fluent API code:
modelBuilder.Entity<Shipment>()
.HasOptional<ShipmentNumber>((shipment) => shipment.ShipmentNumber)
.WithMany();
Shipment has one "true" ShipmentNumber, but many ShipmentNumbers can link to the same Shipment, hence the WithMany() call (relation without a navigator property). In theory, both relations should always return one entity, but I know EF won't allow me a 1:1 relation here, so I'm using optional.
Here's the actual code:
shipment = tracker.Shipment;
ctx.Shipments.Add(shipment);
shipment.ShipmentNumber = new ShipmentNumber { Number = tracker.ShipmentNumber };
ctx.ShipmentNumbers.Add(shipment.ShipmentNumber);
ctx.SaveChanges();
If someone knows how to make it properly save the entity along with the relation, please do tell. I'm totally stuck at the moment.
Well, I don't know why you want a 1:n relationship in database and a 1:0.1 relationship in the model.
Case 1
If you want to make a 1:1 relationship, you should declare your model as follows:
public class Shipment
{
public int ShipmentId { get; set; }
//NO FK here
public virtual ShipmentNumber ShipmentNumber { get; set; }
}
public class ShipmentNumber
{
public int ShipmentId { get; set; } //ShipmentNumber PK is Also Shipment FK
public virtual Shipment Shipment { get; set; }
}
Mapping:
modelBuilder.Entity<Shipment>()
.HasKey(i => i.ShipmentId);
modelBuilder.Entity<ShipmentNumber>()
.HasKey(i => i.ShipmentId);
modelBuilder.Entity<Shipment>()
.HasRequired(i => i.ShipmentNumber)
.WithRequiredPrincipal(i => i.Shipment)
.WillCascadeOnDelete(false);
Generated Migration:
CreateTable(
"dbo.Shipments",
c => new
{
ShipmentId = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ShipmentId);
CreateTable(
"dbo.ShipmentNumbers",
c => new
{
ShipmentId = c.Int(nullable: false),
})
.PrimaryKey(t => t.ShipmentId)
.ForeignKey("dbo.Shipments", t => t.ShipmentId)
.Index(t => t.ShipmentId);
Case 2
If you want to make a 1:n relationship:
public class Shipment
{
public int ShipmentId { get; set; }
public virtual ICollection<ShipmentNumber> ShipmentNumbers { get; set; }
}
public class ShipmentNumber
{
public int ShipmentNumberId { get; set; }
public int ShipmentId { get; set; }
public virtual Shipment Shipment { get; set; }
}
Mapping:
modelBuilder.Entity<Shipment>()
.HasKey(i => i.ShipmentId);
modelBuilder.Entity<ShipmentNumber>()
.HasKey(i => i.ShipmentNumberId);
modelBuilder.Entity<Shipment>()
.HasMany(i => i.ShipmentNumbers)
.WithRequired(i => i.Shipment)
.HasForeignKey(i => i.ShipmentId)
.WillCascadeOnDelete(false);
Generated Migration:
CreateTable(
"dbo.Shipments",
c => new
{
ShipmentId = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ShipmentId);
CreateTable(
"dbo.ShipmentNumbers",
c => new
{
ShipmentNumberId = c.Int(nullable: false, identity: true),
ShipmentId = c.Int(nullable: false),
})
.PrimaryKey(t => t.ShipmentNumberId)
.ForeignKey("dbo.Shipments", t => t.ShipmentId)
.Index(t => t.ShipmentId);
Another problem is the code you are using to add items to database.
ctx.Shipments.Add(shipment);
shipment.ShipmentNumber = new ShipmentNumber { Number = tracker.ShipmentNumber };
//this line is not necessary
ctx.ShipmentNumbers.Add(shipment.ShipmentNumber);
ctx.SaveChanges();
When you add a new Shipment all dependant objects will be inserted to database, if necessary.
I am using EntityFramework and ASP.NET identity. I have derived from IdentityUser and IdentityGroup to store extra fields for my application.
I want to call properties: User.Groups and Group.Users, a many-to-many relationship, and have EntityFramework automatically create the linking table, GroupUsers.
My first attempt had the following:
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
public virtual ICollection<ApplicationGroup> Groups { get; set; }
// ...
}
public class ApplicationGroup : IdentityGroup<ApplicationUser>
{
public virtual ICollection<ApplicationGroupRole> Roles { get; set; }
}
public class IdentityGroup<TUser, TKey> : IGroup<TKey>
where TUser : IdentityUser
where TKey : IEquatable<TKey>
{
public virtual ICollection<TUser> Users { get; set; }
// ...
}
And the DBMigration looked something like
CreateTable(
"UMS.ApplicationGroupApplicationUsers",
c => new
{
ApplicationGroup_Id = c.String(nullable: false, maxLength: 128),
ApplicationUser_Id = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.ApplicationGroup_Id, t.ApplicationUser_Id })
.ForeignKey("UMS.ApplicationGroups", t => t.ApplicationGroup_Id, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.ApplicationUser_Id, cascadeDelete: true)
.Index(t => t.ApplicationGroup_Id)
.Index(t => t.ApplicationUser_Id);
In particular, note the linking table has two indexes, one for each foreign key.
However, I wanted to name the linking table explicitly, so in my DBContext I added:
modelBuilder.Entity<ApplicationUser>().ToTable("Users");
modelBuilder.Entity<ApplicationGroup>().ToTable("Groups")
.HasMany(x => x.Users)
.WithMany(x => x.Groups)
.Map(x =>
{
x.ToTable("GroupUsers");
x.MapLeftKey("UserId");
x.MapRightKey("GroupId");
});
However, this gives me an automatic migration with only 1 index:
CreateTable(
"UMS.GroupUsers",
c => new
{
UserId = c.String(nullable: false, maxLength: 128),
GroupId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.UserId, t.GroupId })
.ForeignKey("UMS.Groups", t => t.UserId, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.GroupId, cascadeDelete: true)
.Index(t => t.UserId);
Is this just a bug in EntityFramework? This appears to only happen when one type has a collection of the other via a derived type. Is it possible to keep an explicitly named linking table whilst automatically creating both indexes?
This may not resolve the problem you are having, however, it will correct your code. In your case, as per definition, the "Left Key" should be "GroupId" and the "Right Key" should be "UserId". Check this link. Notice the code you posted, you have got them mixed:
.ForeignKey("UMS.Groups", t => t.UserId, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.GroupId, cascadeDelete: true)
Your code should look like this:
modelBuilder.Entity<ApplicationGroup>().ToTable("Groups")
.HasMany(x => x.Users)
.WithMany(x => x.Groups)
.Map(x =>
{
x.ToTable("GroupUsers");
x.MapLeftKey("GroupId");
x.MapRightKey("UserId");
});
I have two tables that are connect N to N:
[Table("Backoffice_Roles")]
public class Role
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid RoleId { get; set; }
public ICollection<User> Users { get; set; }
}
[Table("Backoffice_Users")]
public class User
{
// Primary key
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }
public ICollection<Role> Roles { get; set; }
}
This all works fine and it creates 3 tables: Backoffice_Roles, Backoffice_Users and RoleUsers.
Is there a way to rename RoleUsers to Backoffice_RoleUsers ?
I tried renaming the table manually in the migration file but it gives this error:
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred
while saving entities that do not expose foreign key properties for
their relationships. The EntityEntries property will return null
because a single entity cannot be identified as the source of the
exception. Handling of exceptions while saving can be made easier by
exposing foreign key properties in your entity types. See the
InnerException for details. --->
System.Data.Entity.Core.UpdateException: An error occurred while
updating the entries. See the inner exception for details. --->
System.Data.SqlClient.SqlException: Invalid object name
'dbo.RoleUsers'.
This the migration without changing the name of the last table manually:
public override void Up()
{
CreateTable(
"dbo.Backoffice_Users",
c => new
{
UserId = c.Guid(nullable: false, identity: true),
})
.PrimaryKey(t => t.UserId);
CreateTable(
"dbo.Backoffice_Roles",
c => new
{
RoleId = c.Guid(nullable: false, identity: true),
})
.PrimaryKey(t => t.RoleId);
CreateTable(
"dbo.RoleUsers",
c => new
{
Role_RoleId = c.Guid(nullable: false),
User_UserId = c.Guid(nullable: false),
})
.PrimaryKey(t => new { t.Role_RoleId, t.User_UserId })
.ForeignKey("dbo.Backoffice_Roles", t => t.Role_RoleId)
.ForeignKey("dbo.Backoffice_Users", t => t.User_UserId)
.Index(t => t.Role_RoleId)
.Index(t => t.User_UserId);
}
Use following mapping to provide name for junction table:
modelBuilder.Entity<Role>()
.HasMany(r => r.Users)
.WithMany(u => u.Roles)
.Map(m => m.ToTable("Backoffice_RoleUsers"));
You can provide mappings by overriding OnModelCreating method of your DbContext class.