How can I find where a table comes from, since a table shows up, but is not being declared anywhere? - entity-framework

In my .NET Core 5 app I tried to implement a chat, so I created 3 tables - Chats, Messages and UsersChats and 3 corresponding models Chat, Message, UserChat.
However, a bit after I migrated and proceeded with writing code I saw that there was one more table created - ChatUser with two columns - ChatsId and UsersId and they were being populated with chats and users and the UserChat was all null.
So, I thought that I should delete the ChatUser table and see what happens. It turns out that the this.data.SaveChanges(); throws and exception, because it tries to save the data to the ChatUser table. Therefore I returned the table and tried to find where it comes from, but I have not created a model for it, I have not created a DbSet for it or anything, so it is a mystery to me how it came alive.
Here are my 3 models:
public class Chat
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Message> Messages { get; set; } = new List<Message>();
public ICollection<User> Users { get; set; } = new List<User>();
public IEnumerable<UserChat> UserChats { get; set; }
}
public class Message
{
public int Id { get; set; }
public int ChatId { get; set; }
[JsonIgnore]
public Chat Chat { get; set; }
public string UserId { get; set; }
public User User { get; set; }
public string Content { get; set; }
public DateTime CreatedAt { get; set; }
}
public class UserChat
{
public string UserId { get; set; }
public User User { get; set; }
public int ChatId { get; set; }
public Chat Chat { get; set; }
}
Here is the User model:
public class User : IdentityUser
{
[JsonIgnore]
public ICollection<Chat> Chats { get; set; } = new List<Chat>();
public IEnumerable<UserChat> UserChats { get; set; }
}
The DB Sets:
public DbSet<Chat> Chats { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<UserChat> UsersChats { get; set; }
The Relations:
builder
.Entity<Message>()
.HasOne(m => m.Chat)
.WithMany(c => c.Messages)
.HasForeignKey(m => m.ChatId)
.OnDelete(DeleteBehavior.Restrict);
builder
.Entity<UserChat>()
.HasKey(uc => new { uc.UserId, uc.ChatId });
The method in which the error occurs:
public void AddUserToChat(string userId, string chatName) {
var chatId = this.data
.Chats
.Where(c => c.Name == chatName)
.Select(c => c.Id)
.FirstOrDefault();
var user = this.data
.Users
.Where(c => c.Id == userId)
.FirstOrDefault();
var chat = this.data
.Chats
.Where(c => c.Name == chatName)
.FirstOrDefault();
chat.Users.Add(user);
this.data.SaveChanges();
}
And finally - what I have in my migration:
migrationBuilder.CreateTable(
name: "Chats",
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_Chats", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ChatUser",
columns: table => new {
ChatsId = table.Column < int > (type: "int", nullable: false),
UsersId = table.Column < string > (type: "nvarchar(450)", nullable: false)
},
constraints: table => {
table.PrimaryKey("PK_ChatUser", x => new {
x.ChatsId, x.UsersId
});
table.ForeignKey(
name: "FK_ChatUser_AspNetUsers_UsersId",
column: x => x.UsersId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChatUser_Chats_ChatsId",
column: x => x.ChatsId,
principalTable: "Chats",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Messages",
columns: table => new {
Id = table.Column < int > (type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ChatId = table.Column < int > (type: "int", nullable: false),
UserId = table.Column < string > (type: "nvarchar(450)", nullable: true),
Content = table.Column < string > (type: "nvarchar(max)", nullable: true),
CreatedAt = table.Column < DateTime > (type: "datetime2", nullable: false)
},
constraints: table => {
table.PrimaryKey("PK_Messages", x => x.Id);
table.ForeignKey(
name: "FK_Messages_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Messages_Chats_ChatId",
column: x => x.ChatId,
principalTable: "Chats",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "UserChat",
columns: table => new {
UserId = table.Column < string > (type: "nvarchar(450)", nullable: false),
ChatId = table.Column < int > (type: "int", nullable: false)
},
constraints: table => {
table.PrimaryKey("PK_UserChat", x => new {
x.UserId, x.ChatId
});
table.ForeignKey(
name: "FK_UserChat_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserChat_Chats_ChatId",
column: x => x.ChatId,
principalTable: "Chats",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_ChatUser_UsersId",
table: "ChatUser",
column: "UsersId");
migrationBuilder.CreateIndex(
name: "IX_Messages_ChatId",
table: "Messages",
column: "ChatId");
migrationBuilder.CreateIndex(
name: "IX_Messages_UserId",
table: "Messages",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserChat_ChatId",
table: "UserChat",
column: "ChatId");
}

Turns out that due to the navigational properties I have in Chat EF Core gets confused. The newer versions of EF Core automatically create the mapping table for you, so to fix it you can either remove the navigational properties and create the mapping table yourself, or you keep the navigational properties and leave EF do it's job.

Related

EF Core 6 code first one to zero or one navigation

I have googled as well as searched SO for this extensively, to no avail.
class Account
{
[Key]
[StringLength(80)]
public string AccountID { get; set; }
[StringLength(80)]
public string Name { get; set; }
[StringLength(80)]
public string Email { get; set; }
[ForeignKey("AccountID")]
public virtual Address? Address { get; set; }
}
class Address
{
[Key]
[StringLength(80)]
public string AccountID { get; set; }
[StringLength(80)]
public string City { get; set; }
[StringLength(80)]
public string Street { get; set; }
}
Address should be optional - if it's not there, the application should still work.
The migration builder seems to have the relationship between the tables inverted:
migrationBuilder.CreateTable(
name: "Account",
columns: table => new
{
AccountID = table.Column<string>(type: "varchar(80)", maxLength: 80, nullable: false),
Name = table.Column<string>(type: "varchar", nullable: true),
Email = table.Column<string>(type: "varchar", nullable: true),
},
constraints: table =>
{
table.PrimaryKey("PK_Account", x => x.AccountID);
table.ForeignKey(
name: "FK_Account_Address_AccountID",
column: x => x.AccountID,
principalTable: "Address",
principalColumn: "AccountID");
});
Now when I try to insert data in the Account table, I am getting an error message about a conflict with a foreign key restraint in the Address table. It looks like there must already be a record in the Address table with the account id I am using to insert data in the Account table.
How do I fix this?
Hint: My fluent API does not offer .Optional() or .WithOptional().

Why is Entity Framework (6) code-first inheritance strategy defaulting to TPT not TPH?

According to everything I've read, the default EF code-first inheritance strategy is Table-Per-Hierarchy, but that is not what I'm getting - I'm getting TPT. (I'm on .NET 6.)
For the sake of this post, I've reproduced my issue with MS's own simplistic example:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
public partial class MyContext : DbContext
{
...
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
...
}
But when I run add-migration Add_Blog_Tables I get the migration below...TPT not TPH!
Can anyone think of anything that might be causing my DbContext to default to TPH? And how would I tell EF that I want TPH when it is defaulting to TPT?
Thanks.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Blog",
columns: table => new
{
BlogId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Url = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Blog", x => x.BlogId);
});
migrationBuilder.CreateTable(
name: "RssBlog",
columns: table => new
{
BlogId = table.Column<int>(type: "int", nullable: false),
RssUrl = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_RssBlog", x => x.BlogId);
table.ForeignKey(
name: "FK_RssBlog_Blog_BlogId",
column: x => x.BlogId,
principalTable: "Blog",
principalColumn: "BlogId");
});
}

Introducing FOREIGN KEY constraint - Basic Migration

I tried to make some project in C# with entity framework core 2.1. However, there is a problem that I can't solve, since I don't see anything wrong.
I'm trying to just do a simple migrate in my database.
There is no problem until I write 'Update-Database' to Package manager console. After trying to update database, here is the error message:
Introducing FOREIGN KEY constraint 'FK_Users_Baskets_BasketId' on table 'Users' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
Basket.cs
public class Basket {
[Key]
public int BasketId { get; set; }
public List<ProductByBasket> ProductByBaskets { get; set; }
public string BasketName { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
}
Product.cs
public class Product {
[Key]
public int ProductId { get; set; }
public List<ProductByBasket> ProductByBaskets { get; set; }
public string ProductName { get; set; }
}
ProductByBasket.cs
[Key]
public int ProductByBasketId { get; set; }
public int BasketId { get; set; }
[ForeignKey("BasketId")]
public Basket Basket { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
Migration File
migrationBuilder.CreateTable(
name: "ProductByBaskets",
columns: table => new
{
BasketId = table.Column<int>(nullable: false),
ProductId = table.Column<int>(nullable: false),
ProductByBasketId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductByBaskets", x => new { x.ProductId, x.BasketId });
table.UniqueConstraint("AK_ProductByBaskets_ProductByBasketId", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "ProductId",
onDelete: ReferentialAction.Cascade);
});
ApplicationDbContext.cs
public class ApplicationDbContext : DbContext {
public ApplicationDbContext() { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
string connectionString = #"Data Source=...\SQLEXPRESS; Initial Catalog = db; Integrated Security=true;";
optionsBuilder.UseSqlServer(connectionString);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<ProductByBasket>()
.HasOne(u => u.Basket).WithMany(u => u.ProductByBaskets).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ProductByBasket>()
.HasKey(x => new { x.ProductId, x.BasketId });
modelBuilder.Entity<ProductByBasket>()
.HasOne(pt => pt.Basket)
.WithMany(p => p.ProductByBaskets)
.HasForeignKey(pt => pt.BasketId);
modelBuilder.Entity<ProductByBasket>()
.HasOne(pt => pt.Product)
.WithMany(t => t.ProductByBaskets)
.HasForeignKey(pt => pt.ProductId);
}
public DbSet<Product> Products { get; set; }
public DbSet<Basket> Baskets { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProductByBasket> ProductByBaskets { get; set; }
}
I tried to configure migration file to write. Then, it looks like:
migrationBuilder.CreateTable(
name: "ProductByBaskets",
columns: table => new
{
BasketId = table.Column<int>(nullable: false),
ProductId = table.Column<int>(nullable: false),
ProductByBasketId = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductByBaskets", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Baskets_BasketId",
column: x => x.BasketId,
principalTable: "Baskets",
principalColumn: "BasketId",
onDelete: ReferentialAction.Cascade);
table.PrimaryKey("PK_ProductByBaskets", x => new { x.ProductId, x.BasketId });
table.UniqueConstraint("AK_ProductByBaskets_ProductByBasketId", x => x.ProductByBasketId);
table.ForeignKey(
name: "FK_ProductByBaskets_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "ProductId",
onDelete: ReferentialAction.Cascade);
});
Then I get this error:
Foreign key references invalid table.
What am I doing wrong?

EF Core OnDelete restrict adds additional column

I have models in a many-to-many relationship:
User
public class User
{
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
[StringLength(100)]
public string Username { get; set; }
[Required]
public string PasswordHash { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
public ICollection<UserRole> UserRoles { get; set; }
}
Role
public class Role
{
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(500)]
public string Description { get; set; }
[ForeignKey("CompanyId")]
public Company Company { get; set; }
public ICollection<RolePrivilege> RolePrivileges { get; set; }
}
UserRole
public class UserRole
{
public int Id { get; set; }
public int UserId { get; set; }
public int RoleId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
[ForeignKey("RoleId")]
public Role Role { get; set; }
}
When I created migrations and then tried update-database, it threw an error of multiple cascade paths. The solution to this was to make On Delete, No Action so I added this in OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserRole>()
.HasIndex(e => new { e.UserId, e.RoleId })
.IsUnique();
modelBuilder.Entity<UserRole>()
.HasOne(e => e.User)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<UserRole>().ToTable("UserRoles");
}
Now the tables are created but one thing that I wasn't expecting is its making an extra column. The migration code looks like this after generating:
migrationBuilder.CreateTable(
name: "UserRoles",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<int>(type: "int", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false),
UserId1 = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserRoles", x => x.Id);
table.ForeignKey(
name: "FK_UserRoles_Roles_RoleId",
column: x => x.RoleId,
principalTable: "Roles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserRoles_Users_UserId1",
column: x => x.UserId1,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
As you can see, it added an extra column, UserId1.
What am I doing wrong or how do I prevent this from happening?
This is a result of a typical relationship fluent configuration mistake - using a parameterless overload of Has / With (effectively telling EF that there is no corresponding navigation property) while actually a navigation property exists. In that case EF will map the missing navigation property to another relationship with no navigation property at the other end and default by convention FK property/column name.
To fix the issue, make sure to use the correct overloads which represent the presence/absence of a navigation property (and update them according in case you add/remove navigation property). In your case, replace
.WithMany()
with
.WithMany(e => e.UserRoles)

c# Entity framework foreign key

I'm new here and hope I'll get an answer - I'm searching the internet since two days ...
It's the first time I use the entity framewort in a Windows 10 UWP. I have to classes which are the objects for my database. In one object I use a property with the type of the other.
If I'll try to add a record I'll get a error.
public class budgetcontext : DbContext
{
private string myDB = "Filename=budget_4.db";
public DbSet<category> Categories { get; set; }
public DbSet<transaction> Transactions { get; set; }
public budgetcontext()
{
this.Database.Migrate();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(myDB);
}
}
public class category
{
private string myGuid = Guid.NewGuid().ToString();
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryId { get; set; }
public string Name { get; set; }
public double BudgetDaily { get; set; }
public double BudgetWeekly { get; set; }
public double BudgetMonthly { get; set; }
public double BudgetYearly { get; set; }
public string CategoryGuid
{
get
{
return myGuid;
}
}
}
public class transaction
{
private string myGuid = Guid.NewGuid().ToString();
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int64 TransactionId { get; set; }
public string Text { get; set; }
public category Category { get; set; }
public DateTime TransDateTime { get; set; }
public double Amount { get; set; }
}
If I try to insert a record of the type transaction to the database I'll get the error:
{"SQLite Error 19: 'UNIQUE constraint failed: Categories.CategoryId'."}
The code to insert the object is:
budget_sqlite.category c = cbCategory.SelectedItem as budget_sqlite.category; //Object is selected in a Combo
using (var db = new budget_sqlite.budgetcontext())
{
t.Text = this.txtText.Text;
t.Category = c;
t.TransDateTime = new DateTime(dpDate.Date.Year, dpDate.Date.Month, dpDate.Date.Day, tpTime.Time.Hours, tpTime.Time.Minutes, tpTime.Time.Seconds);
double.TryParse(txtAmount.Text, out value);
if (RBOut.IsChecked == true)
{
value = value * -1;
}
t.Amount = value;
if (t.TransactionId == 0)
{
db.Transactions.Add(t);
}
db.SaveChanges();
}
Here are the migrations:
[DbContext(typeof(budgetcontext))]
partial class budgetcontextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
modelBuilder.Entity("budget_sqlite.category", b =>
{
b.Property<int>("CategoryId")
.ValueGeneratedOnAdd();
b.Property<double>("BudgetDaily");
b.Property<double>("BudgetMonthly");
b.Property<double>("BudgetWeekly");
b.Property<double>("BudgetYearly");
b.Property<string>("Name");
b.HasKey("CategoryId");
b.ToTable("Categories");
});
modelBuilder.Entity("budget_sqlite.transaction", b =>
{
b.Property<long>("TransactionId")
.ValueGeneratedOnAdd();
b.Property<double>("Amount");
b.Property<int?>("CategoryId");
b.Property<string>("Text");
b.Property<DateTime>("TransDateTime");
b.HasKey("TransactionId");
b.HasIndex("CategoryId");
b.ToTable("Transactions");
});
modelBuilder.Entity("budget_sqlite.transaction", b =>
{
b.HasOne("budget_sqlite.category", "Category")
.WithMany()
.HasForeignKey("CategoryId");
});
}
}
public partial class V0001 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Categories",
columns: table => new
{
CategoryId = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
BudgetDaily = table.Column<double>(nullable: false),
BudgetMonthly = table.Column<double>(nullable: false),
BudgetWeekly = table.Column<double>(nullable: false),
BudgetYearly = table.Column<double>(nullable: false),
Name = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Categories", x => x.CategoryId);
});
migrationBuilder.CreateTable(
name: "Transactions",
columns: table => new
{
TransactionId = table.Column<long>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Amount = table.Column<double>(nullable: false),
CategoryId = table.Column<int>(nullable: true),
Text = table.Column<string>(nullable: true),
TransDateTime = table.Column<DateTime>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Transactions", x => x.TransactionId);
table.ForeignKey(
name: "FK_Transactions_Categories_CategoryId",
column: x => x.CategoryId,
principalTable: "Categories",
principalColumn: "CategoryId",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Transactions_CategoryId",
table: "Transactions",
column: "CategoryId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Transactions");
migrationBuilder.DropTable(
name: "Categories");
}
}
Thank you very much for your help.