Entity Framework duplicate primary key - c#

I'm using Entity Framework 6 and sqlite to store and retrieve some data. When the following code block runs, an exception is thrown on db.SaveChanges().
using (var db = new RepoDatabase(DBPath))
{
var folder1 = new Folder
{
Name = "name1",
Subtitle = "subtitle",
ParentFolder = null,
IsHidden = false,
IsSimple = true,
QueryOrFilePath = "query"
};
var folder2 = new Folder
{
Name = "name2",
Subtitle = "subtitle",
ParentFolder = folder1,
IsHidden = false,
IsSimple = true,
QueryOrFilePath = "query"
};
db.Folders.Add(folder1);
db.Folders.Add(folder2);
db.SaveChanges();
}
Exception:
'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll
Additional information: Unable to determine the principal end of the 'RhythmRepository.Folder_ParentFolder' relationship. Multiple added entities may have the same primary key.
From my understanding, this problem often occurs when the ID is used directly for foreign keys, but that doesn't seem to be the case here, as the type of "ParentFolder" is "Folder".
The folder type is set to auto-increment in the database, and has the attribute:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
The underlying entity structure:
class Folder
{
#region database fields
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FolderID { get; set; }
public string Name { get; set; }
public string Subtitle { get; set; }
[ForeignKey("FolderID")]
public Folder ParentFolder { get; set; }
public bool IsHidden { get; set; }
public bool IsSimple { get; set; }
public string QueryOrFilePath { get; set; }
#endregion
}
And the SQL query to create the table:
CREATE TABLE IF NOT EXISTS Folders
(
FolderID INTEGER PRIMARY KEY AUTOINCREMENT,
Name varchar(255) NOT NULL,
Subtitle varchar(255) NULL,
ParentFolderID INT NULL,
IsHidden BIT NOT NULL,
IsSimple BIT NOT NULL,
QueryOrFilePath varchar(255) NOT NULL,
FOREIGN KEY (ParentFolderID) REFERENCES Folders(FolderID)
);

The error is in the part
[ForeignKey("FolderID")]
public Folder ParentFolder { get; set; }
This makes EF think that FolderID is the foreign key to the parent Folder. In reality, it's ParentFolderID. So change your class definition and mapping to
class Folder
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FolderID { get; set; }
public int? ParentFolderID { get; set; }
[ForeignKey("ParentFolderID")]
public Folder ParentFolder { get; set; }
...
}
If FolderID is the foreign key, EF concludes that there is a 1:1 association between a folder and its parent. Normally, a 1:1 association is implemented by a primary key that's also the foreign key to the parent. I.e the child's primary key copies its parent's primary key. When parent and child are the same entity class, two records of the same table would have to have the same primary key --impossible.

Related

Primary key value in Entity Framework

Entity layout contains int value of venue (VenueId prop), its own id and other information.
CONSTRAINT [FK_Venue_Layout] FOREIGN KEY ([VenueId]) REFERENCES [dbo].[Venue] ([Id])
When I trying to add two layouts with the same VenueId, I'm getting this error
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Saving or accepting changes failed because more than one entity of type 'DataAccess.Models.LayoutModel' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration."
My entity code:
[Table("Layout")]
public class LayoutModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int VenueId { get; set; }
public string Description { get; set; }
}
Insertion code:
var layouts = new List<LayoutModel>
{
new LayoutModel { VenueId = 1, Description = "First layout" },
new LayoutModel { VenueId = 1, Description = "Second layout" },
};
_context.Layouts.AddRange(layouts);
_context.SaveChanges();
I'm not allowed to use navigation properties
Id column or property is marked as identity column in the definition of LayoutViewModel
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
So, no need to assign it explicitly as it will be populated by Database automatically after the row is inserted into Layout table. Please update your layouts population as below to remove Id assignment:
var layouts = new List<LayoutModel> {
new LayoutModel { /*Id = 1,*/ VenueId = 1, Description = "First layout" },
new LayoutModel { /*Id = 2, */ VenueId = 1, Description = "Second layout" }
};
// code smell
foreach(var layout in layouts)
{
context.Entry(layout).State = EntityState.Added;
}
_context.Layouts.AddRange(layouts);
_context.SaveChanges();
Also, please update your LayoutModel as below:
public class LayoutModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 0)]
public int Id { get; set; }
[Key]
[Column(Order = 1)]
//[ForeignKey("Venue")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int VenueId { get; set; }
//public virtual VenueModel Venue { get; set; } //Please correct Venue property type
}
Also, please verify whether Venue is loaded into _context.Layouts or not.

EF Core 3: owned property insert fails when SaveChanges()

My problem: inserting an entity with an owned property fails.
I have a Restaurant entity with an Address owned property. When I try to create an new entity and insert into the database, an exception is thrown at SaveChanges:
Cannot insert the value NULL into column 'RestaurantId', table 'AppRefDB.dbo.Addresses'; column does not allow nulls. INSERT fails.
What I did
My table Address looks like this:
CREATE TABLE [dbo].[Addresses]
(
[RestaurantId] INT NOT NULL,
[Number] NVARCHAR(8) NULL,
[Street] NVARCHAR(150) NOT NULL,
[Zip] NVARCHAR(10) NOT NULL,
[Town] NVARCHAR(50) NOT NULL,
[Site] NVARCHAR(150) NULL ,
CONSTRAINT [PK_Addresses]
PRIMARY KEY ([RestaurantId]),
CONSTRAINT [FK_Address_Restaurants_RestaurantId]
FOREIGN KEY ([RestaurantId]) REFERENCES [Restaurants] ([Id])
ON DELETE CASCADE
)
where RestaurantId is the primary key and FK from Restaurant table.
And
CREATE TABLE [dbo].[Restaurants]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
CONSTRAINT [FK_Restaurants_TCategories]
FOREIGN KEY ([IdCategory]) REFERENCES [Categories]([Id])
)
I defined my property like this in OnModelCreating:
modelBuilder.Entity<Restaurant>()
.OwnsOne(p => p.Address)
.ToTable("Addresses");
And I save like this:
await _dbContext.Set<Restaurant>()
.AddAsync(restaurant, cancellationToken);
_dbContext.SaveChangesAsync();
What I am looking for
What should I change in order to EF understand RestaurantId should get the newly created Id from Restaurant table before inserting the Address?
I am using EF Core 3.
Update works fine, I just have a problem with creating an new restaurant/address
EDIT: my model
public class Restaurant
{
[Key]
public int Id { get; set; }
[Required, StringLength(50)]
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
[Required, StringLength(150)]
public string Street { get; set; }
[StringLength(8)]
public string Number { get; set; }
[Required, StringLength(10)]
public string Zip { get; set; }
[Required, StringLength(50)]
public string Town { get; set; }
[StringLength(150)]
public string Site { get; set; }
}
Edit2 :
I tested a synchronous version as well
In this case, you have a Class object which has a collection of addresses.
using (var context = new YourContext())
{
var model= new Restaurant{ Name = "McDonald's" };
model.addresses.Add(new addresses{ street="test",.... });
model.addresses.Add(new addresses{ street="test",.... });
context.Restaurant.Add(model);
context.SaveChanges();
}
this would solve your problem.
You can add in both your classes
public ICollection<YourEntity> YourEntity{ get; set; }
or you can use foreign keys.
[ForeignKey("Restaurant")]
public long RestaurantId { get; set; }
public Restaurant Restaurant{ get; set; }
if you add this in your address entity you need to first create one resturant then add addresses separately
In fact this is probably a bug in EF 3.0
I tested with EF 3.1 (preview) and it is working fine

SQLite and SQLite-Extensions can not perform OneToOne relationsip

I'm using SQLite-Net-Extensions. I'm attempting to define a OneToOne relationship so that when my Accounts model is loaded it will also include the Campaign so that I can access the campaign name.
The problem is Accounts.Campaign is always null. I have data in both tables.
Here's my tables in SQLite:
CREATE TABLE `campaigns` (
`Id` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT UNIQUE
);
and
CREATE TABLE `accounts` (
`Id` INTEGER PRIMARY KEY AUTOINCREMENT,
`CampaignId` INTEGER,
`MobileNumber` TEXT UNIQUE,
`Password` TEXT
);
Below are my models:
namespace SMA.Models
{
[SQLite.Table("accounts")]
class Accounts
{
[PrimaryKey, AutoIncrement]
public Int32 Id { get; set; }
[ForeignKey(typeof(Campaigns))]
public Int32 CampaignId { get; set; }
[MaxLength(11)]
public string MobileNumber { get; set; }
[MaxLength(50)]
public string Password { get; set; }
[OneToOne("Id")]
public Campaigns Campaign { get; set; }
}
}
and
namespace SMA.Models
{
[Table("campaigns")]
class Campaigns
{
[PrimaryKey, AutoIncrement]
public Int32 Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
}
I run the following code to fetch all of the accounts:
var accounts = this.db.Table<SMA.Models.Accounts>().ToList();
Also tried ...
var accounts = this.db.Query<Account>("SELECT * FROM accounts");
And ...
var accounts = this.db.Query<Account>("SELECT * FROM accounts JOIN campaigns ON accounts.CampaignID = campaigns.ID");
When I inspect accounts the account data is there, but Accounts.Campaign is null. I can't seem to see what I'm doing wrong.
Try using SQLite-Net Extension read methods instead of plain sqlite.net Query or Table. For example:
var accounts = this.db.GetAllWithChildren<Account>();
Also, make sure that you're either setting foreign keys manually or using SQLite-Net Extensions to write relationships to database.
To make SQLite-Net Extensions methods available make sure that you're importing SQLiteNetExtensions.Extensions namespace:
import SQLiteNetExtensions.Extensions;
If they're still not available, make sure that there are no duplicated sqlite.net libraries in your packages.

unable to set other specified column as primary key instead of Id column to table using entity framework code first

I have two table
Product and Order
public class Product
{
string name;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid productId { get; set; } // i want this to be primary key instead default Id
}
public class Order
{
string name;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid orderId { get; set; } // i want this to be primary key instead default Id and also want to add productId inside this table as foreign key
}
I have used following code to use code first.
DatabaseContext.cs
class DatabaseContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
Program.cs
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<DatabaseContext>());
using (var context = new DatabaseContext())
{
context.Product.Add(new Product() { Name = "mytest" });
context.SaveChanges();
}
Console.WriteLine("Database Created!!!");
Console.ReadKey();
}
getting exception
Additional information: Unable to determine composite primary key ordering for type '.Customer'. Use the ColumnAttribute (see http://go.microsoft.com/fwlink/?LinkId=386388) or the HasKey method (see http://go.microsoft.com/fwlink/?LinkId=386387) to specify an order for composite primary keys.
EF supports only properties so you should change your fields to properties. When you do that use {class Name} + Id as the property name and it will be picked up as the key property by convention. Alternatively you can use the [Key] attribute on a property to let EF know it should be the key property. You can find more about EF attributes here.

MVC creating a list as a model property not being added as foreign key to DB with code first entity framework

I am starting a code first EF MVC project. Below is the code for a Message model that I am creating. Is it possible to create a tags property that is a list of tags (another model I created) like I am attempting below?
public class Message
{
public int Id { get; set; }
public string Text { get; set; }
public byte[] Attachment { get; set; }
[Required]
public MessageBoard MessageBoard { get; set; }
[Required]
public virtual List<Tag> Tags { get; set; }
}
After attempting the update-database -verbose command, I see that it does not add a Tags class to my database. The console shows this db command for messages:
CREATE TABLE [dbo].[Messages] (
[Id] [int] NOT NULL IDENTITY,
[Text] [nvarchar](max),
[Attachment] [varbinary](max),
[MessageBoard_Id] [int] NOT NULL,
CONSTRAINT [PK_dbo.Messages] PRIMARY KEY ([Id])
)
How can I create this foreign key relationship between messages and tags?
I assume that you one Many to Many relationship to reuse existing tags.
First of all you have to add to your Tag class a reference to Message
public virtual List<Message> Messages { get; set; };
Then in your model configuration class you have to set the relation many to many, with the following code :
modelBuilder.Entity<Message>()
.HasMany<Tag>(m => m.Tags)
.WithMany(t => t.Messages)
.Map(mt =>
{
mt.MapLeftKey("MessageID");
mt.MapRightKey("TagId");
mt.ToTable("MessagesTag"); //Name of table many to many
});
And don't forget to add class Tag in your DBContext too.
public DbSet<Tag> Tag { get; set; }

Categories

Resources