Mapping reference property to abstract parent - c#

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);

Related

EF6 code first one to zero or one with Predefined FK property

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,

Extra Foreign Key in Code First Migrations

I'm kinda new to Entity Framework and Code First Migration so I hope this is an easy question to answer. I am trying to create a one to one relationship between ApplicationUser (from ASP.NET identity) and Member. I have a Member class:
public class Member
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Address Address { get; set; }
public UserStatus Status { get; set; }
public DateTime CreateDate { get; set; }
public virtual string ApplicationUserID { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
and an ApplicationUserClass:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
}
public virtual Member Member { get; set; }
}
In my DBContext (inherits IdentityDbContext)I have the following configuration:
modelBuilder.Entity<ApplicationUser>()
.HasOptional(t => t.Member).WithOptionalPrincipal();
base.OnModelCreating(modelBuilder);
When I run the code first migration, I'm getting this:
CreateTable(
"dbo.Members",
c => new
{
ID = c.Int(nullable: false, identity: true),
FirstName = c.String(),
LastName = c.String(),
Status = c.Int(nullable: false),
CreateDate = c.DateTime(nullable: false),
ApplicationUserID = c.String(maxLength: 128),
Address_ID = c.Int(),
ApplicationUser_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Addresses", t => t.Address_ID)
.ForeignKey("dbo.AspNetUsers", t => t.ApplicationUser_Id)
.ForeignKey("dbo.AspNetUsers", t => t.ApplicationUserID)
.Index(t => t.ApplicationUserID)
.Index(t => t.Address_ID)
.Index(t => t.ApplicationUser_Id);
Notice that I have 2 foreign keys, ApplicationUserID and ApplicationUser_Id. I want to try to do everything using FluentAPI (i.e. not Data Annotations). How would I configure it so that EF uses ApplicationUserID, the string ID I have in my class? I thought that Class+ID was the convention, so why is it creating another foreign key?
I believe you should update your configuration this way:
modelBuilder.Entity<Member>()
.HasOptional(x => x.ApplicationUser)
.WithMany()
.HasForeignKey(x => x.ApplicationUserID);
This is the way EntityFramework treats one-to-one relationships, you have to map it that way and introduce UNIQUE constraints over your DB table.
More information about this case is here: http://weblogs.asp.net/manavi/associations-in-ef-code-first-ctp5-part-3-one-to-one-foreign-key-associations
Here's the quote from the link:
The reason is simple: Code First (and EF in general) does not natively
support one-to-one foreign key associations. In fact, EF does not
support any association scenario that involves unique constraints at
all. Fortunately, in this case we don’t care what’s on the target side
of the association, so we can treat it like a to-one association
without the many part. All we want is to express “This entity (User)
has a property that is a reference to an instance of another entity
(Address)” and use a foreign key field to represent that relationship.
Basically EF still thinks that the relationship is many-to-one. This
is a workaround for the current EF limitation which comes with two
consequences: First, EF won't create any additional constraint for us
to enforces this relationship as a one to one, we need to manually
create it ourselves. The second limitation that this lack of support
impose to us is more important: one to one foreign key associations
cannot be bidirectional (i.e. we cannot define a User property on the
Address class).

Unable to determine a valid ordering for dependent operations circular reference

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.

Mapping 1 to 0..1 in Entity Framework Code First

I am trying to get 1 to 0..1 mapping to work in Entity Framework Code First, but keep getting the error:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'Id'.
I have a MainLocation with a required Location, but as the Locations can have multiple child Locations, a MainLocation is not required in the Location object.
The MainLocation has a similar relation to MainLocationAddress, but this is a 1 to 1 relation which in turn make up the same db table.
The ER model should look like this:
My entities look like this:
[Table("MainLocation")]
public class MainLocation : IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public virtual MainLocationAddress Address { get; set; }
[Required]
public virtual Location Location { get; set; }
}
[Table("MainLocation")]
public class MainLocationAddress : BaseAddress
{
[Key, ForeignKey("MainLocation")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public virtual MainLocation MainLocation { get; set; }
}
public class Location : IEntity
{
public int Id { get; set; }
public virtual Location ParentLocation { get; set; }
public virtual ICollection<Location> ChildLocations { get; set; }
protected virtual MainLocation MainLocation { get; set; }
internal MainLocation GetMainLocation() { return this.MainLocation; }
internal void SetMainLocation(MainLocation mainLocation) { MainLocation = mainLocation; }
}
I have configured the associations in OnModelCreating in my DbContext class:
modelBuilder.Entity<MainLocation>()
.HasRequired(x => x.Location)
.WithOptional();
modelBuilder.Entity<MainLocation>()
.HasRequired(x => x.Address)
.WithRequiredPrincipal();
PS! The MainLocation property on Location is protected because it should not be accessed directly. Instead I have a service-layer which gets the value from the Location or the inherited value from a ParentLocation. I have tried to change it to public, to see if it made any changes regarding the error I'm getting.
Although I was able to extend .WithOptional() to .WithOptional(x => x.MainLocation), there were still no changes in the stated error.
I have achieved a 1:0-1 association between two entities, Person and User. The requirement, a User must have one and only one Person; while a Person may or may not be associated with a User.
public class Person
{
public int PersonId { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int UserId { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
Define EntityTypeConfiguration classes as follows and include them in the DbContext OnModelCreating method.
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
ToTable("People");
HasKey(p => p.PersonId);
Property(p => p.PersonId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(p => p.User).WithRequired(u => u.Person); // User is option here but
// Person is required in User
}
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("Users");
HasKey(u => u.UserId);
Property(u => u.UserId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Create a unique index in the Users table on PersonId
Property(u => u.PersonId).IsRequired().HasColumnAnnotation("Index",
new IndexAnnotation(new IndexAttribute("IX_PersonId") { IsUnique = true }));
}
}
Put the following lines in the DbContext.OnModelCreating method.
modelBuilder.Configurations.Add(new PersonConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
Run an Add-Migration command and you will get something like the following in the DbMigration,Up method. Make changes as follows.
CreateTable(
"dbo.Users",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
PersonId = c.Int(nullable: false),
Person_PersonId = c.Int(nullable: false), // Delete this
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.People", t => t.Person_PersonId) // change to .ForeignKey("dbo.People", t => t.PersonId)
.Index(t => t.PersonId, unique: true) // append a ';'
.Index(t => t._Person_PersonId); // Delete this
CreateTable(
"dbo.People",
c => new
{
PersonId = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.PersonId)
Modify the Down() method as follows.
Change
DropForeignKey("dbo.Users", "Person_PersonId", "dbo.People");
to
DropForeignKey("dbo.AppUsers", "PersonId", "dbo.People");
Change
DropIndex("dbo.AppUsers", new[] { "Person_PersonId" });
to
DropIndex("dbo.AppUsers", new[] { "PersonId" });
Run the Update-Database command targeting this migration. The resulting Users and People tables will have a one-to-one association on the PersonId foreign key.
I have come to realize that this is not possible to accomplish in EF as it is at the moment (the 1 to 0..1 association).
I have solved it in our solution by letting all the child locations have a reference to the main location. I can still get the top organization by looking for a location with no parent location. So, although it's not quite what we wanted, it doesn't break our business-model.
This scenario works fine on the Update-Database in that the desired database structure showing a one-to-one relationship between the User and People tables. However, for some reason attempts to query the Users table.
I changed the User class by removing the PersonId property.
public class User
{
public int UserId { get; set; }
public virtual Person Person { get; set; }
}
The UserConfiguration class becomes:
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("Users");
HasKey(u => u.UserId);
Property(u => u.UserId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
The Add-Migration produces:
CreateTable(
"dbo.Users",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Person_PersonId = c.Int(nullable: false), // Keep this
})
.PrimaryKey(t => t.UserId)
.ForeignKey("dbo.People", t => t.Person_PersonId)
.Index(t => t._Person_PersonId); // Change to .Index(t => t._Person_PersonId, unique: true);
The Update-Database produces User and People table with one-to-one relationship and the framework uses its own generated Person_PersonId column.

Renaming N to N table in Code First EF

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.

Categories

Resources