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

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

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().

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

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.

Entity Framework Core 6 Migration Adds Columns Not in Entity

I have a .NET Core 6 project in Visual Studio 2022 using Entity Framework Core 6 Code First. I created a migration that changes some entity properties and adds some foreign keys between some tables in a SQL Server 2019 database. The migration adds some columns to a table and sets up some foreign keys using those columns. SQL Server Management Studio shows these columns to be invalid. The main class is:
namespace VerityLearn.Domain
{
public class UserExamTopicResult
{
public int ExamUserId { get; set; }
public int TopicId { get; set; }
public int CourseId { get; set; }
public int? AssocCourseTopicId { get; set; }
public int SequenceNumber { get; set; }
#region Navigation Properties
public ExamUser ExamUser { get; set; }
public Topic Topic { get; set; }
public Course Course { get; set; }
public ExamTopicCount ExamTopicCount { get; set; }
public virtual AssocCourseTopic AssocCourseTopic { get; set; }
#endregion // Navigation Properties
} // end public class UserExamTopicResult
} // end namespace VerityLearn.Domain
The reference to the ExamTopicCount entity was added and the plan is to set up a foreign key relationship between these entities. The ExamTopicCount class is:
using System.Collections.Generic;
namespace VerityLearn.Domain
{
public class ExamTopicCount
{
public int ExamId { get; set; }
public int TopicId { get; set; }
public bool IsAssociatedCourseTopic { get; set; }
public int TopicCount { get; set; }
#region Navigation Properties
public Exam Exam { get; set; }
public Topic Topic { get; set; }
public List<UserExamTopicResult> UserExamTopicResults { get; set; }
#endregion // Navigation Properties
} // end public class ExamTopicCount
} // end namespace VerityLearn.Domain
The migration uses the UserExamTopicResultConfiguration and ExamTopicCountConfiguration classes.
UserExamTopicResultConfiguration:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using VerityLearn.Domain;
namespace VerityLearn.DataAccess
{
public class UserExamTopicResultConfiguration :
IEntityTypeConfiguration<UserExamTopicResult>
{
public void Configure(EntityTypeBuilder<UserExamTopicResult> builder)
{
builder.ToTable("UserExamTopicResults");
builder.HasKey(uet => new { uet.ExamUserId, uet.TopicId});
builder.Property(uet => uet.ExamUserId)
.HasColumnType("int");
builder.Property(uet => uet.TopicId)
.HasColumnType("int");
builder.Property(uet => uet.CourseId)
.HasColumnType("int")
.IsRequired();
builder.Property(uet => uet.AssocCourseTopicId)
.HasColumnType("int")
.IsRequired(false);
builder.Property(uet => uet.TopicScore)
.HasColumnType("float(24)")
.IsRequired()
.HasDefaultValue(0F);
builder.HasOne(uet => uet.Course)
.WithMany(c => c.UserExamTopicResults)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(uet => uet.ExamUser)
.WithMany(eu => eu.UserExamTopicResults);
builder.HasOne(uet => uet.Topic)
.WithMany(t => t.UserExamTopicResults);
} // end public void Configure(EntityTypeBuilder<UserExamTopicResult> builder)
} // end public class UserExamTopicResultConfiguration : ...
} // end namespace VerityLearn.DataAccess
ExamTopicCountConfiguration:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using VerityLearn.Domain;
namespace VerityLearn.DataAccess
{
public class ExamTopicCountConfiguration : IEntityTypeConfiguration<ExamTopicCount>
{
public void Configure(EntityTypeBuilder<ExamTopicCount> builder)
{
builder.ToTable("ExamTopicCounts");
builder.HasKey(etc => new { etc.ExamId, etc.TopicId });
builder.Property(etc => etc.ExamId)
.HasColumnType("int");
builder.Property(etc => etc.TopicId)
.HasColumnType("int");
builder.Property(etc => etc.IsAssociatedCourseTopic)
.HasColumnType("bit")
.IsRequired()
.HasDefaultValue(false);
builder.Property(etc => etc.TopicCount)
.HasColumnType("int")
.IsRequired()
.HasDefaultValue(0);
builder.HasOne(etc => etc.Exam)
.WithMany(e => e.ExamTopicCounts)
.HasForeignKey(etc => etc.ExamId);
builder.HasOne(etc => etc.Topic);
builder.HasMany(etc => etc.UserExamTopicResults)
.WithOne(uetr => uetr.ExamTopicCount);
} // end public void Configure(EntityTypeBuilder<ExamTopicCount> builder)
} // end public class ExamTopicCountConfiguration : ...
} // end namespace VerityLearn.DataAccess
The migration generated a class, ExamUserUserExamTopicResultExamTopicCount : Migration.
using Microsoft.EntityFrameworkCore.Migrations;
namespace VerityLearn.DataAccess.Migrations
{
public partial class ExamUserUserExamTopicResultExamTopicCount : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TopicCount",
table: "UserExamTopicResults");
// Added column by migration framework
migrationBuilder.AddColumn<int>(
name: "ExamId",
table: "UserExamTopicResults",
type: "int",
nullable: true);
// Added column by migration framework
migrationBuilder.AddColumn<int>(
name: "ExamTopicCountExamId",
table: "UserExamTopicResults",
type: "int",
nullable: true);
// Added column by migration framework
migrationBuilder.AddColumn<int>(
name: "ExamTopicCountTopicId",
table: "UserExamTopicResults",
type: "int",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_UserExamTopicResults_ExamId",
table: "UserExamTopicResults",
column: "ExamId");
migrationBuilder.CreateIndex(
name: "IX_UserExamTopicResults_ExamTopicCountExamId_ExamTopicCountTopicId",
table: "UserExamTopicResults",
columns: new[] { "ExamTopicCountExamId", "ExamTopicCountTopicId" });
migrationBuilder.AddForeignKey(
name: "FK_UserExamTopicResults_Exams_ExamId",
table: "UserExamTopicResults",
column: "ExamId",
principalTable: "Exams",
principalColumn: "ExamId",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_UserExamTopicResults_ExamTopicCounts_ExamTopicCountExamId_ExamTopicCountTopicId",
table: "UserExamTopicResults",
columns: new[] { "ExamTopicCountExamId", "ExamTopicCountTopicId" },
principalTable: "ExamTopicCounts",
principalColumns: new[] { "ExamId", "TopicId" },
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_UserExamTopicResults_Exams_ExamId",
table: "UserExamTopicResults");
migrationBuilder.DropForeignKey(
name: "FK_UserExamTopicResults_ExamTopicCounts_ExamTopicCountExamId_ExamTopicCountTopicId",
table: "UserExamTopicResults");
migrationBuilder.DropIndex(
name: "IX_UserExamTopicResults_ExamId",
table: "UserExamTopicResults");
migrationBuilder.DropIndex(
name: "IX_UserExamTopicResults_ExamTopicCountExamId_ExamTopicCountTopicId",
table: "UserExamTopicResults");
migrationBuilder.DropColumn(
name: "ExamId",
table: "UserExamTopicResults");
migrationBuilder.DropColumn(
name: "ExamTopicCountExamId",
table: "UserExamTopicResults");
migrationBuilder.DropColumn(
name: "ExamTopicCountTopicId",
table: "UserExamTopicResults");
migrationBuilder.AddColumn<int>(
name: "TopicCount",
table: "UserExamTopicResults",
type: "int",
nullable: false,
defaultValue: 0);
}
}
}
After the migration I ran a query on the UserExamTopicResults table and got the following.
I don't understand what is happening and how it can be resolved. Any input would be apprecieated.
Thanks,
Leonard
builder.HasKey(uet => new { uet.ExamUserId, uet.TopicId});
This line are can be removed , the new keyword creates extra 2 property for you. Instead of using new try this maybe :
builder.HasKey(t => t.ExamUserId);
builder.HasKey(t => t.TopicId)
After that to reflect new changes to Migration you can delete Migrations folder and run
dotnet ef database drop
dotnet ef migrations add Initial
dotnet ef update database
I tried these way but i had not any data , so be careful about data loss.
I found my problem. I did not completely add the DbSet and configuration to my DbContext class. It seems that leaving this information out causes the migration processing to make "guesses" concerning how to create or change the database tables. I needed to add the following lines to the DbContext.
/// <summary>
/// AssocCourseTopicExam DbSet
/// </summary>
public DbSet<AssocCourseTopicExam> AssocCourseTopicExams { get; set; }
...
protected override void OnModelCreating(ModelBuilder builder)
{
...
builder.ApplyConfiguration(new ExamAssocCourseTopicConfiguration());
...
}
After making this change the migration proceeded as expected.

Many to Many relationship Entity Framework Core 5

I created a Blazor project and I have a many-to-many relationship between these classes:
public class ItemAttribute
{
[Key]
public int ItemAttributeId { get; set; }
public string Title { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<ItemAttributeCluster> itemAttributeClusters { get; set; }
}
and
public class ItemAttributeCluster
{
[Key]
public int ItemAttributeClusterId { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
public ICollection<ItemAttribute> itemAttributes { get; set; }
}
So far so good, EF generates the Join table ItemAttributeItemAttributeCluster, ok.
Then I try to add a new cluster of ItemAttributes for the first time with my controller:
// Create
[HttpPost]
public async Task<IActionResult> Post(ItemAttributeCluster itemAttributeCluster)
{
_context.ItemAttributeClusters.Add(itemAttributeCluster);
await _context.SaveChangesAsync();
return Ok(itemAttributeCluster);
}
and I get this error:
Cannot insert explicit value for identity column in table 'ItemAttributes' when IDENTITY_INSERT is set to OFF.
What am I doing wrong? Why is EF trying to write something into 'ItemAttributes'? When i´m trying to create a new Cluster on 'ItemAttributesCluster' and the Join Table?
Migration Builder:
Join Table
migrationBuilder.CreateTable(
name: "ItemAttributeItemAttributeCluster",
columns: table => new
{
itemAttributeClustersItemAttributeClusterId = table.Column<int>(type: "int", nullable: false),
itemAttributesItemAttributeId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeItemAttributeCluster", x => new { x.itemAttributeClustersItemAttributeClusterId, x.itemAttributesItemAttributeId });
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributeClusters_itemAttributeClustersItemAttributeClusterId",
column: x => x.itemAttributeClustersItemAttributeClusterId,
principalTable: "ItemAttributeClusters",
principalColumn: "ItemAttributeClusterId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ItemAttributeItemAttributeCluster_ItemAttributes_itemAttributesItemAttributeId",
column: x => x.itemAttributesItemAttributeId,
principalTable: "ItemAttributes",
principalColumn: "ItemAttributeId",
onDelete: ReferentialAction.Cascade);
});
ItemAttributes
migrationBuilder.CreateTable(
name: "ItemAttributes",
columns: table => new
{
ItemAttributeId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributes", x => x.ItemAttributeId);
});
ItemAttributeCluster
migrationBuilder.CreateTable(
name: "ItemAttributeClusters",
columns: table => new
{
ItemAttributeClusterId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Titel = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsMultiChoice = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemAttributeClusters", x => x.ItemAttributeClusterId);
});
If this was an existing schema for the ItemAttribute / Cluster tables and their PK were defined as identity columns, you will need to tell EF to expect them using the [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute alongside the Key designation.
When using a naming convention that EF recognizes like "ItemAttributeId" or "Id" I believe EF will default to assuming these are Identity columns, but with a name like "ItemAttributeCode" I believe it would assume a database generated option of "None" as default.
try to add some navigation properties
public ItemAttributeCluster()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Titel { get; set; }
public bool IsMultiChoice { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttributeClaster))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class ItemAttribute
{
public ItemAttribute()
{
AttributeClusters = new HashSet<AttributeCluster>();
}
[Key]
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Item> Items { get; set; }
[InverseProperty(nameof(AttributeCluster.ItemAttribute))]
public virtual ICollection<AttributeCluster> AttributeClusters { get; set; }
}
public partial class AttributeCluster
{
[Key]
public int Id { get; set; }
public int ItemAttributeId { get; set; }
public int ItemAttributeClasterId { get; set; }
[ForeignKey(nameof(ItemAttributeId))]
[InverseProperty("AttributeClusters")]
public virtual ItemAttribute ItemAttribute { get; set; }
[ForeignKey(nameof(ItemAttributeClasterId))]
[InverseProperty(nameof(ItemAttributeCluster.AttributeClusters))]
public virtual ItemAttributeCluster ItemAttributeClaster { get; set;
}
dbcontext (no any fluent apis at all)
public virtual DbSet<AttributeCluster> AttributeClusters { get; set; }
public virtual DbSet<ItemAttribute> ItemAttributes { get; set; }
public virtual DbSet<ItemAttributeCluster> ItemAttributeClusters { get; set; }
Test
var itemAttributeClaster = new ItemAttributeCluster { Titel="titleClaster2", IsMultiChoice=false};
var itemAttribute = new ItemAttribute{Title="attrTitle" };
var attributeClaster = new AttributeCluster { ItemAttribute = itemAttribute, ItemAttributeClaster = itemAttributeClaster };
_context.AttributeClusters.Add(attributeClaster);
_context.SaveChanges();
it created 1 record in each of 3 tables
I give up on getting this to work with ef. I run several sql`s directly to achieve the same functionality and so far it works, not a satisfactory solution but it needs to be done.

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.