Foreign Key Issues with Application User - c#

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.

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

EF Core: Creating a link between items in the same table

Suppose I have a list of companies:
Company
CompanyID
CompanyA
1
CompanyB
2
CompanyC
3
CompanyD
4
Let's say we want to show that CompanyA is a competitor of CompanyB and CompanyC but not CompanyD.
I would like to use EF Core to establish such a link/mapping. I'm assuming I will need to create a mapping table that in DB will look something like this:
Competitor mapping:
CompanyId
CompetitorCompanyID
1
2
1
3
What I am struggling with is how to create such a link in the OnModelCreating(ModelBuilder modelBuilder) method?
I've been able to create maps between entities in 2 different tables, but unable to configure the DBContext class for this use case (where mapped entities live in the same table).
If was me doing I would do it like this
The Company model
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CompanyCompetitorRelation> CompanyRelation { get; set; }
public virtual ICollection<CompanyCompetitorRelation> CompetitorRelation { get; set; }
}
In this model I'm saying that it has 2 relations with the CompanyCompetiorRelation model.
Now for the CompanyCompetitorRelation Model
public class CompanyCompetitorRelation
{
public int CompanyId { get; set; }
public int CompetitorId { get; set; }
public virtual Company CompanyRelation { get; set; }
public virtual Company CompetitorRelation { get; set; }
}
Now here, I'm saying that a CompanyCompetiorRelation has 2 relations with the Company Model.
Now the magic goes on the FluentAPI
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Company>()
.HasKey(e => e.Id);
builder.Entity<CompanyCompetitorRelation>()
.HasKey(e => new { e.CompanyId, e.CompetitorId });
builder.Entity<CompanyCompetitorRelation>()
// CompanyCompetitorRelation property
.HasOne(e => e.CompanyRelation)
// Company property
.WithMany(e => e.CompanyRelation)
// Set the property that is FK for this relation
.HasForeignKey(e => e.CompanyId);
builder.Entity<CompanyCompetitorRelation>()
// CompanyCompetitorRelation property
.HasOne(e => e.CompetitorRelation)
// Company property
.WithMany(e => e.CompetitorRelation)
// Set the property that is FK for this relation
.HasForeignKey(e => e.CompetitorId);
}
On the first set of FK of the builder.Entity<CompanyCompetitorRelation>() I'm saying that it has one CompanyRelation with many of the Company and is foreignkey is the CompanyId
on the second set of FK of the builder.Entity<CompanyCompetitorRelation>()
I'm saying that it as one CompetitorRelation relation with many Company and is foreignkey is the CompetitorId
This is what it has generated
migrationBuilder.CreateTable(
name: "Company",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Company", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CompanyCompetitorRelation",
columns: table => new
{
CompanyId = table.Column<int>(type: "int", nullable: false),
CompetitorId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CompanyCompetitorRelation", x => new { x.CompanyId, x.CompetitorId });
table.ForeignKey(
name: "FK_CompanyCompetitorRelation_Company_CompanyId",
column: x => x.CompanyId,
principalTable: "Company",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CompanyCompetitorRelation_Company_CompetitorId",
column: x => x.CompetitorId,
principalTable: "Company",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_CompanyCompetitorRelation_CompetitorId",
table: "CompanyCompetitorRelation",
column: "CompetitorId");
Hope that this will help out to understand
You can create as many you want if require, you just have point them in the rigth direction on the FluentAPI
I think it's possible to this on the model also, but I like to use the FluentAPI

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

.NET Core creating non-nullable FK constraint with IdentityUser

I am trying to create a relationship from my main IdentityUser to another class, using the EF migrations in .NET Core 2.2. There is an issue I am running into, and that is the migrations are being created with a null FK constraint. This is something I do not want.
I fear, that I should not be manually editing the migration file (to fix this issue), as when I change these models in the future, my manual edit will be overwritten (because my classes are somehow coded incorrectly).
These are the classes I have..
public class ApplicationUser : IdentityUser
{
public List<Purchase> Purchases { get; set; }
}
public class Purchase
{
public int Id { get; set; }
// others
}
The resulting migration file has a nullable: true property set on ApplicationUserId
migrationBuilder.CreateTable(
name: "Purchase",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApplicationUserId = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Purchase", x => x.Id);
table.ForeignKey(
name: "FK_Purchase_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Not correct. So, I add properties in the Purchase class (like I have in other POCOs in my project)..
public class Purchase
{
public int Id { get; set; }
// others
public int ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
and re-run the migration regeneration..
migrationBuilder.CreateTable(
name: "Purchase",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ApplicationUserId = table.Column<int>(nullable: false),
ApplicationUserId1 = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Purchase", x => x.Id);
table.ForeignKey(
name: "FK_Purchase_AspNetUsers_ApplicationUserId1",
column: x => x.ApplicationUserId1,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
I now get the proper non-nullable constraint, but it is a duplicate. (There is a 1 tacked on). My questions are:
Is it safe for me to simply modify the migration file and call it a day?
How can I create a relationship between my ApplicationUser and Purchase classes so the migration file that is generated gives me a non-nullable FK constraint?
1) You can, but each consequent migration will remake this field an d cause you to have an issue. Also since the data type does not match it will not work as expected
2) The id type of the foreign key is wrong. Application user has a primary key of type string but you are using int so it is failing to recognize the correct foreign key.
After you correct that, put the Required attribute on the class. So the final class would look like
using System.ComponentModel.DataAnnotations;
public class Purchase
{
public int Id { get; set; }
// others
[Required]
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
You can generate the migration script using the Add-Migration command, after that you can edit the migration source code and add at least 1 record right after the creation of the new table using:
migrationBuilder.InsertData(
table: "Organizations",
columns: new[] { "OrganizationName", "IsDeleted" },
values: new object[,]
{
{ "Test organization", false }
});
After that you should be able to manage the creation of the new foreign key field, giving 1 as default value.

Categories