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.
Related
am working on some legacy code , would to add new table to the DB
and it would have two one to zero or one relation ship and I need "for some reason" The ForeignKeys in the new table to be defined as properties and The code :
public class EconomyInfo : Entity
{
/*other props*/
[ForeignKey("LogoImage")]
[Required]
public int LogoImage_Id { get; set; }
public virtual Image LogoImage { get; set; }
[ForeignKey("Organization")]
[Required]
public int Organization_Id { get; set; }
public virtual Organization Organization { get; set; }
}
public class Image : Entity
{
/*other props*/
public virtual EconomyInfo Economyinfo { get; set; }
}
public class Organization : Entity
{
/*other props*/
public virtual EconomyInfo EconomyInfo { get; set; }
}
public class Entity
{
public int Id { get; set; }
}
Like This EF don't generate migration and give the error
EconomyInfo_LogoImage_Source: : Multiplicity is not valid in Role 'EconomyInfo_LogoImage_Source' in relationship 'EconomyInfo_LogoImage'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
and the same for the other navigation property
and if i add those to lines in the the context class:
modelBuilder.Entity<Organization>().HasOptional(o => o.EconomyInfo).WithOptionalPrincipal();
modelBuilder.Entity<Image>().HasOptional(i => i.Economyinfo).WithOptionalPrincipal();
the migration will be generated without errors but it will be strange like :
public override void Up()
{
CreateTable(
"dbo.EconomyInfoes",
c => new
{
Id = c.Int(nullable: false, identity: true),
LogoImage_Id = c.Int(nullable: false),
Organization_Id = c.Int(nullable: false),
Image_Id = c.Int(),
Organization_Id1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Images", t => t.LogoImage_Id, cascadeDelete: true)
.ForeignKey("dbo.Organizations", t => t.Organization_Id)
.ForeignKey("dbo.Images", t => t.Image_Id)
.ForeignKey("dbo.Organizations", t => t.Organization_Id1)
.Index(t => t.LogoImage_Id)
.Index(t => t.Organization_Id)
.Index(t => t.Image_Id)
.Index(t => t.Organization_Id1);
}
i know that i could edit the generated EF migration code to make it specify my needs , but there is no point of wasting hours dealing with EF and then give up and work around it , plus it won't be so practical to work with it later by my colleagues ,thanks in advance
I've just given up and decided to go with the work around as write my own migration for my new entity and tell the context to ignore it from the upcoming generated migration.
So my solution is this:
In the context class :
// Regarding unPossible configuration to make pre defined property as foreign key in one to zero or one relationship
// we wrote our migration and tell the context to ignore our model and its navigation property from other model classes
modelBuilder.Entity<Organization>().Ignore(o => o.EconomyInfo);
modelBuilder.Entity<Image>().Ignore(i => i.Economyinfo);
modelBuilder.Ignore<EconomyInfo>();
Organization class :
public virtual EconomyInfo EconomyInfo { get; set; }
Image class :
public virtual EconomyInfo Economyinfo { get; set; }
Manual written migration :
using System;
using System.Data.Entity.Migrations;
public partial class EconomyInfo : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.EconomyInfoes",
c => new
{
Id = c.Int(nullable: false, identity: true),
LogoImage_Id = c.Int(nullable: false),
Organization_Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Images", t => t.LogoImage_Id)
.ForeignKey("dbo.Organizations", t => t.Organization_Id)
.Index(t => t.LogoImage_Id)
.Index(t => t.Organization_Id);
}
public override void Down()
{
DropForeignKey("dbo.EconomyInfoes", "Organization_Id", "dbo.Organizations");
DropForeignKey("dbo.EconomyInfoes", "LogoImage_Id", "dbo.Images");
DropIndex("dbo.EconomyInfoes", new[] { "Organization_Id" });
DropIndex("dbo.EconomyInfoes", new[] { "LogoImage_Id" });
DropTable("dbo.EconomyInfoes");
}
}
You may refer to this question also for more info How do I specify the foreign key in a one-to-one/zero relationship?
Note: I applied my written migration to database works fine, try to generate new migration, also works as expected, but not tested working with my new model class from code yet. Hopefully nothing will be messed later,
I have a complex object hierarchy in an enterprise application. I'll try and keep it simple, and abstract, yet still representative of what I'm dealing with.
My project deals with several styles of the same type of object. For this, we have implemented the TPT structure for our entity objects:
public abstract class BaseWidget {
public int Id { get; set; }
// etc...
}
// About a dozen concrete implementations already exist and work great!
public class ExistingWidget : BaseWidget {
// Other properties
}
Now I have a new type that I'm doing. We have common properties on the object, but there are a few different sets of details that are required depending on the sub type. For this, I set up TPH, as the properties on that type are the same across all subtypes. The only difference is which details objects are required.
public abstract NewWidgetBase : BaseWidget {
public int EmployeeNumber { get; set; }
public DateTime EffectiveDate { get; set; }
}
public NewWidgetA : NewWidgetBase {
}
public NewWidgetB : NewWidgetBase {
}
I have this mapped in my DbContext like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<NewWidgetBase>()
.Map<NewWidgetA>(w => w.Requires("Discriminator").HasValue("a"))
.Map<NewWidgetB>(w => w.Requires("Discriminator).HasValue("b"));
At this point, I have used an integration test and successfully checked that I can save to both tables.
Now, I want to add in the details:
public class FooDetails {
public int Id { get; set; }
public int NewWidgetId { get; set; }
// ...
[ForeignKey(nameof(NewWidgetId))]
public NewWidgetBase NewWidget { get; set; }
}
public class BarDetails {
public int Id { get; set; }
public int NewWidgetId { get; set; }
// ...
[ForeignKey(nameof(NewWidgetId))]
public NewWidgetBase NewWidget { get; set; }
}
I then add those reference properties to my appropriate NewWidget objects.
public class NewWidgetA {
// ...
public FooDetails Foo { get; set; }
}
public class NewWidgetB {
// ...
public FooDetails Foo { get; set; }
public BarDetails Bar { get; set; }
}
I tried just executing this, assuming that the typical mapping would work, and got the following 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: Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
With that, I understood that it doesn't have the correct Relationship directions and keys mapped. So I went to explicitly set it within the DbContext again:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredDependent();
However, that gives me the error:
System.InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'WidgetId'.
I looked at a "some other" "questions", and none of those answers helped me.
As a last ditch effort, I tried using the overload for .WithRequiredDependent() which takes a Func. However, because it isn't the exact same type as I'm mapping because I have the property as the abstract base, it complains. Therefore, I try casting it like so:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredDependent(f => (NewWidgetA)f.Widget);
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredDependent(f => (NewWidgetB).Widget);
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredDependent(b => (NewWidgetB).Widget);
However, this also gives an error:
The ForeignKeyAttribute on property 'Widget' on type '...Foo' is not valid. The foreign key name 'WidgetId' was not found on the dependent type 'NewWidgetA'. The Name value should be a comma separated list of foreign key property names.
This is leading me to believe that I'm unable to do what I want to do with having abstract properties. Is there a way to map this relationship that I'm missing? I don't want to have a specific reference property for each as I know there are more types coming within a month or two, and the list of properties will get unwieldy.
It's possible, but only with unidirectional (with navigation property only at Widget side) one-to-one Shared Primary Key Association, where the Widget side is the principal and the Details side is the dependent.
Start by removing the navigation and FK properties from Details entities:
public class FooDetails {
public int Id { get; set; }
// ...
}
public class BarDetails {
public int Id { get; set; }
// ...
}
and use the following fluent configuration:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredPrincipal();
Note the WithRequiredPrincipal() call. It's telling EF that (1) the Widget is the principal and (2) there is no navigation property from Details to Widget.
The resulting database schema is something like this:
CreateTable(
"dbo.BaseWidget",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ExistingWidget",
c => new
{
Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.NewWidgetBase",
c => new
{
Id = c.Int(nullable: false),
EmployeeNumber = c.Int(nullable: false),
EffectiveDate = c.DateTime(nullable: false),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.FooDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.BarDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
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);
}
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 a PhoneNumber entity which I'd like to reference across multiple entities. For example, a Contact entity that has many PhoneNumbers, and a Business entity with one PhoneNumber.
public class PhoneNumber
{
[Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Number { get; set; }
}
public class Contact
{
[Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
}
public class Business
{
[Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("PhoneNumber")]
public int PhoneNumberId { get; set; }
public virtual PhoneNumber PhoneNumber { get; set; }
}
I've setup Contact and Business so that they have one way navigation properties. Also, the phone numbers are optional. I've also setup the many relationship for Contact to prevent EF from adding a Contact_Id column when I add a migration. The mapping is as follows (note WithMany() is used since PhoneNumber doesn't have a nav property back to Contact):
modelBuilder.Entity<Contact>().HasMany(r => r.PhoneNumbers).WithMany()
.Map(x => x.MapLeftKey("ContactId").MapRightKey("PhoneId"));
When I add a Contact with multiple phone numbers it gets added fine. There are records for the Contact, the PhoneNumbers table, and the ContactPhoneNumbers link table.
However, the issue I'm struggling with is when I delete a contact. EF correctly deletes the entry in the ContactPhoneNumbers link table, and the Contact entry, but it doesn't delete the entries from the PhoneNumbers table. I've seen examples of mapping with modelBuilder where WillCascadeOnDelete(true) is used, but that option isn't available when using WithMany().
What do I need to do to get that type of cascade delete working correctly? Is it possible with this setup? Or will I need to have a separate PhoneNumbers table for each entity (Contact and Business) to setup a relationship where the respective PhoneNumber table uses a FK (eg., Contact_Id)?
I'm fairly new to EF so any suggestions are welcome. I might be going about this entirely wrong.
EDIT: here's the related migration code...
CreateTable(
"dbo.PhoneNumbers",
c => new
{
Id = c.Int(nullable: false, identity: true),
Number = c.String()
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Contacts",
c => new
{
Id = c.Int(nullable: false, identity: true)
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ContactPhoneNumbers",
c => new
{
ContactId = c.Int(nullable: false),
PhoneId = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.ContactId, t.PhoneId })
.ForeignKey("dbo.Contacts", t => t.ContactId, cascadeDelete: true)
.ForeignKey("dbo.PhoneNumbers", t => t.PhoneId, cascadeDelete: true)
.Index(t => t.ContactId)
.Index(t => t.PhoneId);
CreateTable(
"dbo.Business",
c => new
{
Id = c.Int(nullable: false),
PhoneNumberId = c.Int(nullable: false)
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.PhoneNumbers", t => t.PhoneNumberId, cascadeDelete: true)
.Index(t => t.Id)
.Index(t => t.PhoneNumberId);
In order for the cascading delete to work records that are going to be cascaded must have a foreign key back to the record being deleted. So in your example, You delete a Contact record. Because their is a foreign key from ContactPhoneNumber to Contact, the cascade works. Since there is no foreign key from PhoneNumber to ContactPhoneNumber, (the foreign key goes the other way) the cascade does not continue.
This is because you defined the relationship as a many to many. If you think about trying to perform a cascading delete on your model as you would like, If a ContactPhoneNumber is deleted and then its associated PhoneNumbers are deleted, there could now be other ContactPhoneNumbers that that don't have a valid PhoneNumber (Because there can be many ContactPhoneNumbers to one PhoneNumber). Now these would need to be deleted and this process would continue. Databases don't like this cyclical cascading.
It is not entirely clear to me why you need the many to many relationship, If you truly need it you will not be able to perform a cascade on delete. If you can make your relationship:
1 Contact - * ContactPhoneNumber 1- * PhoneNumber, then you could configure the cascade to work like you want it too.