ForeignKey to the same table with custom column name - c#

I have a model User :
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public int? IDUser_CreatedBy { get; set; }
public User User_CreatedBy { get; set; }
}
User can have its creator (User_CreatedBy) with its ID (IDUser_CreatedBy) which resides in the same table of course but I want to have an opportunity to leave it as null value (User with unknown creator). That's why IDUser_CreatedBy has int? nullable type.
I don't know how to set up my fluent API in order to bind that foreign key IDUser_CreatedBy to primary key IDUser in the same table.
I know that if I delete IDUser_CreatedBy foreign key from that model and add new migration then EF core will make for me shadow property foreign key implicitly but I want to have an opportunity to update my User later (in MVC controller) with custom created column IDUser_CreatedBy and be able to name that column for myself. Besides I don't want that shadow property since I don't have any control over naming that column.
How can I achieve that ?
EDIT
#IvanStoev - thank you for your answer but your example doesn't produce for me a valid migration code :
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users");
migrationBuilder.RenameColumn(
name: "CreatedByIDUser",
table: "Users",
newName: "UserIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_CreatedByIDUser",
table: "Users",
newName: "IX_Users_UserIDUser");
migrationBuilder.AddColumn<int>(
name: "IDUser_CreatedBy",
table: "Users",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Users_IDUser_CreatedBy",
table: "Users",
column: "IDUser_CreatedBy");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_IDUser_CreatedBy",
table: "Users",
column: "IDUser_CreatedBy",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_UserIDUser",
table: "Users",
column: "UserIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_IDUser_CreatedBy",
table: "Users");
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_UserIDUser",
table: "Users");
migrationBuilder.DropIndex(
name: "IX_Users_IDUser_CreatedBy",
table: "Users");
migrationBuilder.DropColumn(
name: "IDUser_CreatedBy",
table: "Users");
migrationBuilder.RenameColumn(
name: "UserIDUser",
table: "Users",
newName: "CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_UserIDUser",
table: "Users",
newName: "IX_Users_CreatedByIDUser");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users",
column: "CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
It does produces a ForeignKey column that I wanted (IDUser_CreatedBy) and a valid index for that key column (IX_Users_IDUser_CreatedBy) but it also adds another ForeignKey (UserIDUser) witch is a shadow property I guess. So as a result I have 2 different ForeignKey columns that refer to one Primerykey column (IDUser) in the same table. In my case migration in fact renames old column (CreatedByIDUser) to new one (UserIDUser) which is a remnant of my previous shadow property ForeignKey column. What I'm looking for is that migration will get rid completely of that previous shadow property and introduse only one, new ForeignKey column (IDUser_CreatedBy). Any suggestions ?
EDIT 2
#viveknuna - thank you for your example but it also doesn't do the job for me.
Migration looks like this :
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users");
migrationBuilder.RenameColumn(
name: "CreatedByIDUser",
table: "Users",
newName: "User_CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_CreatedByIDUser",
table: "Users",
newName: "IX_Users_User_CreatedByIDUser");
migrationBuilder.AddColumn<int>(
name: "IDUser_CreatedBy",
table: "Users",
nullable: true);
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_User_CreatedByIDUser",
table: "Users",
column: "User_CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_User_CreatedByIDUser",
table: "Users");
migrationBuilder.DropColumn(
name: "IDUser_CreatedBy",
table: "Users");
migrationBuilder.RenameColumn(
name: "User_CreatedByIDUser",
table: "Users",
newName: "CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_User_CreatedByIDUser",
table: "Users",
newName: "IX_Users_CreatedByIDUser");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users",
column: "CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
It renames my remnant shadow property ForeignKey a new name but it keeps that shadow property. Migration also adds new column IDUser_CreatedBy which is not a ForeignKey (there's no AddForeignKey and CreateIndex method for that column).
EDIT 3
#Ivan Stoev - you're right. I haven't submitted whole model because I thought it will have no impact on the result but now it's clear it has ... :) First of all - I indeed created a brand new command line project and managed to scaffold the correct migration as you said. Then I realised that my model has another property which is public List<User> UsersAdded { get; set; } which depicts all users that were added by that particular User. And that is the culprit. It adds new column UserIDUser to newly created migration. So the final question is : how can I achieve all that what I need with that new property public List<User> UsersAdded { get; set; } ? because I want to have an opportunity to include User collecion that this User created while getting a User in my MVC controller. As a formality I submit my whole model (correctly this time) :
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public string AvatarPath { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public bool IsWebUser { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public int? IDUser_CreatedBy { get; set; }
public User User_CreatedBy { get; set; }
public List<User> UsersAdded { get; set; }
}

EF Core fluent API gives you a full control over naming the table columns, regardless of whether you use explicit or shadow model property. The unconventional foreign key property is mapped with HasForeignKey fluent API of the relationship configuration.
Your entity model falls into Single Navigation Property with explicit FK property category, which can be mapped like this:
modelBuilder.Entity<User>()
.HasOne(e => e.User_CreatedBy) // reference navigation property
.WithMany() // no collection navigation property
.HasForeignKey(e => e.IDUser_CreatedBy); // foreign key property

Related

C# cannot update database with Entity Framework

I have a problem, unfortunately I can not fix it and can not find a proper solution to this.
Does anyone know what the problem is?
I am trying to update my Tours object with a new TravelCountry.
Migration Code:
migrationBuilder.DropColumn(
name: "TravelCountry",
table: "Tours");
migrationBuilder.AddColumn<int>(
name: "TravelCountryId",
table: "Tours",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "TravelCountries",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_TravelCountries", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Tours_TravelCountryId",
table: "Tours",
column: "TravelCountryId");
migrationBuilder.AddForeignKey(
name: "FK_Tours_TravelCountries_TravelCountryId",
table: "Tours",
column: "TravelCountryId",
principalTable: "TravelCountries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
It fails at "migraitonBuilder.AddForeignKey" with the error:
Cannot add or update a child row: a foreign key constraint fails (gam_db.#sql-1e1c_fe, CONSTRAINT FK_Tours_TravelCountries_TravelCountryId FOREIGN KEY (TravelCountryId) REFERENCES travelcountries (Id) ON DELETE CASCADE)
Heres my Tour object:
public class Tour
{
[Key]
public int Id { get; set; }
[Required]
public TravelCountry? TravelCountry { get; set; }
}
And here is my TravelCountry object:
public class TravelCountry
{
[Key]
public int Id { get; set; }
[Required]
public string? Name { get; set; }
}
you need to add a foreign key to your tour model in order to create a relationship between this twp tables
for example:
public int? CategoryId { get; set; }
[ForeignKey("TravelCountryId")]
also make sure you have all the nugets and dont forget the command update-database in console package manger after adding the migrations
Just one question: why is the entity column [required] whilst the variable in the code can be null? Maybe delete the question marks in the declarations because [required] means that this value cannot be null

How to use custom property name for foreign key column in Entity Framework

I for some reason cannot seem to find this info anywhere, but feel this would be a common thing. I originally had this class:
public class SiteSharingPermission
{
[Required]
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Organization Organization { get; set; }
}
I recently needed to modify this column / property (OrganizationId -> PartnerOrganizationId) but I'm having an issue with my migrations on updating the foreign key column property name.
I tried running migrations, modified the up/down to this:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_SiteSharingPermissions_Organizations_OrganizationId",
table: "SiteSharingPermissions");
migrationBuilder.RenameColumn(
name: "OrganizationId",
table: "SiteSharingPermissions",
newName: "PartnerOrganizationId");
migrationBuilder.AddForeignKey(
name: "FK_SiteSharingPermissions_Organizations_PartnerOrganizationId",
table: "SiteSharingPermissions",
column: "PartnerOrganizationId",
principalTable: "Organizations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_SiteSharingPermissions_Organizations_PartnerOrganizationId",
table: "SiteSharingPermissions");
migrationBuilder.RenameColumn(
name: "PartnerOrganizationId",
table: "SiteSharingPermissions",
newName: "OrganizationId");
migrationBuilder.AddForeignKey(
name: "FK_SiteSharingPermissions_Organizations_OrganizationId",
table: "SiteSharingPermissions",
column: "OrganizationId",
principalTable: "Organizations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
But I keep getting an error when trying to get results from this table:
Unknown column 's.OrganizationId' in field list
Any ideas?
I personally won't prefer making changes to migration file. Instead do something like below.
public class SiteSharingPermission
{
[Required]
public Guid Id { get; set; }
public Guid PartnerOrganizationId { get; set; }
[ForeignKey("PartnerOrganizationId")]
public Organization Organization { get; set; }
}
You can also do it using Fluent API method.

EF Core: duplicated foreign key in migration

It's my first project with using Entity Framework Core and sometimes the relationships are a bit confusing for me. The problem is when I'm creating a database migration, it always add foreign keys I haven't defined. So I can't use the database because inserting new values throws a duplicated entry exception.
But first some code of my config:
public class User : IdentityUser
{
public virtual List<SignatureToUser> Signatures { get; set; }
}
public class SignatureToUser
{
[Key]
[Required]
public int SignatureToUserID { get; set; }
public int SignatureID { get; set; }
[Required]
[ForeignKey("SignatureID")]
public virtual Signature Signature { get; set; }
public string SignatureUserID { get; set; }
[Required]
[ForeignKey("SignatureUserID")]
public virtual User User { get; set; }
}
And that is the builder options I use
modelBuilder.Entity<SignatureToUser>().HasOne(l => l.User).WithOne().OnDelete(DeleteBehavior.ClientNoAction);
modelBuilder.Entity<SignatureToUser>().HasOne(l => l.Signature).WithOne().OnDelete(DeleteBehavior.ClientNoAction);
modelBuilder.Entity<SignatureToUser>().HasIndex(l => l.SignatureUserID).IsUnique(false);
modelBuilder.Entity<SignatureToUser>().HasIndex(l => l.SignatureID).IsUnique(false);
And now the problem: this is the generated migration:
migrationBuilder.CreateTable(
name: "SignatureToUser",
columns: table => new
{
SignatureToUserID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
SignatureID = table.Column<int>(type: "int", nullable: false),
SignatureUserID = table.Column<string>(type: "nvarchar(450)", nullable: false),
UserId = table.Column<string>(type: "nvarchar(450)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_SignatureToUser", x => x.SignatureToUserID);
table.ForeignKey(
name: "FK_SignatureToUser_AspNetUsers_SignatureUserID",
column: x => x.SignatureUserID,
principalTable: "AspNetUsers",
principalColumn: "Id");
table.ForeignKey(
name: "FK_SignatureToUser_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
As you can see there is a foreign key UserId added as a duplicate to my already existing key SignatureUserID.
How can I disable the creation of this additional foreign key?
In my research, I also heard about base classes could be a problem. Is this still a problem? I also tried something like that to trick the base class, but there were no changes.
public class User : IdentityUser
{
[Key]
public string Id { get { return base.Id; } set { base.Id = value; } }
}
Thank you for reading :) I hope anyone has an idea

How to change single primary key to composite primary key with EF Core

I want to delete the single primary key Id column in a SQL table that has already been created and does not contain data and define a composite primary key instead.
My current entity definition is as follows:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public long IpFrom { get; set; }
public long IpTo { get; set; }
...
And I want to update to this:
[Key, Column(Order = 0)]
public long IpFrom { get; set; }
[Key, Column(Order = 1)]
public long IpTo { get; set; }
...
I have already made the following definition using fluent api:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IPv4LocationEntity>()
.HasKey(b => new {b.IpFrom, b.IpTo})
.HasName("PK_TableName");
}
Then I used EF Tool to create migration files and got the following migration file.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_TableName",
table: "TableName");
migrationBuilder.DropColumn(
name: "Id",
table: "TableName");
migrationBuilder.AddPrimaryKey(
name: "PK_TableName",
table: "TableName",
columns: new[] { "IpFrom", "IpTo" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_TableName",
table: "TableName");
migrationBuilder.AddColumn<int>(
name: "Id",
table: "TableName",
type: "int",
nullable: false,
defaultValue: 0)
.Annotation("SqlServer:Identity", "1, 1");
migrationBuilder.AddPrimaryKey(
name: "PK_TableName",
table: "TableName",
column: "Id");
}
}
I get the following error when I want to update the database with the EF tool even though everything seems right to me:
ALTER TABLE DROP COLUMN failed because column 'Id' does not exist in table 'TableName'.
Thanks for your help.
I tried this and running dotnet ef database update does not apply the migration.
Instead, generated SQL file from the migration and execute the SQL queries. Try doing this.
cd ToYourDbModelProject
dotnet ef --startup-project ../PathToStartupProject/ migrations script previous_migration current_migration -o current_migration.sql --context Namespace.To.Your.DbContext

Foreign Key Issues with Application User

I am using entity framework core and code first to make some tables.
I want one table with two Foreign keys, with both being the application user.
I have it built and running, but every time I try to add to the table I get an error
SqlException: Violation of PRIMARY KEY constraint 'PK_ApplicationUser'. Cannot insert duplicate key in object 'dbo.ApplicationUser'. The duplicate key value is (ef20a79d-e819-499d-b006-c32f5cf85577).
This might just be a database design issue, but I feel like that I am doing this should work.
Here is the models:
public class Test
{
public string TestId { get; set; }
[ForeignKey("Giver")]
public string GiverId { get; set; }
public virtual ApplicationUser Giver { get; set; }
[ForeignKey("Getter")]
public string GetterId { get; set; }
public virtual ApplicationUser Getter { get; set; }
}
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
[InverseProperty("Giver")]
public ICollection<CustomModels.Test> Giver { get; set; }
[InverseProperty("Getter")]
public ICollection<CustomModels.Test> Getter { get; set; }
}
Here is the code to create the table:
migrationBuilder.CreateTable(
name: "Tests",
columns: table => new
{
TestId = table.Column<string>(nullable: false),
GetterId = table.Column<string>(nullable: true),
GiverId = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Tests", x => x.TestId);
table.ForeignKey(
name: "FK_Tests_ApplicationUser_GetterId",
column: x => x.GetterId,
principalTable: "ApplicationUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Tests_ApplicationUser_GiverId",
column: x => x.GiverId,
principalTable: "ApplicationUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Tests_GetterId",
table: "Tests",
column: "GetterId");
migrationBuilder.CreateIndex(
name: "IX_Tests_GiverId",
table: "Tests",
column: "GiverId");
This is how I am adding to the DB:
Test test = (new Test
{
Giver = await _userManager.GetUserAsync(User),
Getter = await _userManager.Users.FirstOrDefaultAsync(u => u.Email == "s#gmail.com")
});
_context.Tests.Add(test);
await _context.SaveChangesAsync();
I would like to be able to track which user is giving information and which user is getting information.
We were using just the user email for tracking, but we would have to search for the email every time and I feel like this would be a better long-term solution.
I should be able to add as my "test" rows and repeat users, ie user1 can give to user2 and vise-versa, and that would be 2 different rows.
My guess is _userManager.GetUserAsync(User) isn't operating on the same DbContext as _context. So _context isn't aware of that ApplicationUser and will try to create it. The resolution to this would be to Attach the ApplicationUser before saving.

Categories