Entity Framework inserts the same entity twice [duplicate] - c#

This question already has answers here:
Duplicate entry for key 'PRIMARY' .Ignoring the spaces for strings
(2 answers)
Closed 5 years ago.
I have the following structure, mapped with Entity Framework 6 using the Database First principle:
Here is the source database:
CREATE TABLE `Foo` (
`Guid` VARCHAR(36),
`Name` VARCHAR(500) NOT NULL,
`Author` VARCHAR(100) NOT NULL,
PRIMARY KEY (`Guid`),
UNIQUE KEY `unique_fooname` (`Name`,`Author`));
CREATE TABLE `FooVersion` (
`Guid` VARCHAR(36),
`Version` INT,
`RefFooGuid` VARCHAR(36) NOT NULL,
PRIMARY KEY (`Guid`),
UNIQUE KEY `unique_fooversion` (`Version`,`RefFooGuid`),
CONSTRAINT `fk_foo_version`
FOREIGN KEY (`RefFooGuid`)
REFERENCES `Foo` (`Guid`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
CREATE TABLE `FooVersionPart` (
`Name` VARCHAR(250) NOT NULL,
`RefFooVersionGuid` VARCHAR(36) NOT NULL,
PRIMARY KEY (`Name`, `RefFooVersionGuid`),
INDEX `fk_fooversion_fooversionpart_idx` (`RefFooVersionGuid` ASC),
CONSTRAINT `fk_fooversion_fooversionpart`
FOREIGN KEY (`RefFooVersionGuid`)
REFERENCES `FooVersion` (`Guid`)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
At one point in my code, I am creating a new foo like this:
var dbContext = new DbContext();
var newVersion = new FooVersion();
newVersion.Guid = Guid.NewGuid().ToString()
newVersion.Parts = sourceParts.Select(s => new FooVersionPart
{
Name = s.Name,
RefFooVersionGuid = newVersion.Guid
}.ToList();
var foo = new Foo
{
Author = "Me"
Guid = Guid.NewGuid().ToString(),
Name = "Foo"
};
dbContext.Foos.Add(foo);
foo.Versions.Add(newVersion);
dbContext.SaveChanges();
I am getting the following error during the SaveChanges:
Duplicate entry 'dim.proran.db.tmp.dataCallistHDay -9e6620f4-227d-44de-b781-5fd67' for key 'PRIMARY'
The errors occurs more specifically when EF is trying to insert one of the FooVersionPart (dim.proran.db.tmp.dataCallistHDay is the Name of that part and 9e6620f4-227d-44de-b781-5fd67 is the -truncated- RefFooVersionGuid of that part).
I have the absolute certainty sourceParts have no duplicate and neither in the database.
Here is the generated SQL:
INSERT INTO Foo [..];
INSERT INTO FooVersion [..];
INSERT INTO FooVersionPart [..];
INSERT INTO FooVersionPart [..];
INSERT INTO FooVersionPart [..];
INSERT INTO FooVersionPart [..];
Etc
The exception always occurs on the same FooVersionPart (dim.proran.db.tmp.dataCallistHDay). It is the 1910th elements of 2435. So EF is not trying to insert twice all parts, just one at the middle.
The weirdest thing is that it worked a while ago, and it does not work anymore with no changes in all the related stuff (no changes in the schema, no library update, no changes in the code). It works well in one of my environment, and it does not work with the same code in my dev environment.
One last thing, it is not specific to that Guid. At each attempt, the Guid is different (not the other inputs, so it still fails on dim.proran.db.tmp.dataCallistHDay), and at each attempt I get the same error.
Do you have any idea of what could cause that?

The exception message (Duplicate entry 'dim.proran.db.tmp.dataCallistHDay -9e6620f4-227d-44de-b781-5fd67' for key 'PRIMARY') combined with the primary key for the table it refers to (PRIMARY KEY ('Name', 'RefFooVersionGuid')) tells us that you are attempting to insert duplicate data into the table, specifically multiple FooVersionParts with the same name.
Now you say you have done a duplicate check on your source data, but what you may not know is that many (all?) SQL database don't count trailing spaces as part of the record. For example this query will actually return a record:
SELECT 1
WHERE 'abc' = 'abc '
So, as you confirmed, your data does have duplicate(s) that won't be spotted by a C# GroupBy but will be caught by the database engine. An easy solution is to trim the data before you group it, a good habit to get into, particularly with data inputted manually.

Related

Microsoft Sync Framework unique index error

I use the MS Sync Framework to sync my SQL Server instance with a local SQL CE file to make it possible working offline with my Windows app.
I use GUIDs as keys. On my table I have a unique index on 2 columns: user_id and setting_id:
usersettings table
------------------
id PK -> I also tried it without this column. Same result
user_id FK
setting_id FK
value
Now I do the following:
I create a new record in this table in both databases - SQL Server and SQL CE with the same user_id and setting_id.
This should work and merge the data together since this can happen in real life. But I get an error when syncing saying the unique key constraint led to an error. The key pair already exists in the table.
A duplicate value cannot be inserted into a unique index. [ Table name = user_settings,Constraint name = unique_userid_settingid ]
Why can't MS sync handle that? It should not try to insert the key pair again. It should update the value if needed.
The issue is if you add the same key pair to different copies of the table, they get different IDs (GUIDs) as primary keys in this usersettings table.
As this is simply a many-to-many table between Users and Settings, there is no need to have that ID as a PK (or even a column at all).
Instead, just use a concatenated key of the two FKs e.g.,
CREATE TABLE [dbo].[usersettings](
[user_id] [UNIQUEIDENTIFIER] NOT NULL,
[setting_id] [UNIQUEIDENTIFIER] NOT NULL,
[value] [varchar](50) NOT NULL,
CONSTRAINT [PK_usersettings] PRIMARY KEY CLUSTERED ([user_id] ASC, [setting_id] ASC) );
Of course, include appropriate field settings (e.g., if you use VARCHARs to store the IDs) and relevant FKs.
As the rows inserted should now be identical on the two copies, it should merge fine.
If you must have a single column as a unique identifier for the table, you could make it meaningful e.g.,
the PK (ID) becomes a varchar (72)
it gets filled with CONCAT(user_ID, setting_id)
As the User_ID and Setting_ID are FKs, you should already have them generated so concatenating them should be easy enough.
Do you get the error during sync, then it should appear as a conflict, that you must solve in code.
https://learn.microsoft.com/en-us/previous-versions/sql/synchronization/sync-framework-2.0/bb734542(v=sql.105)
I also see this in the manual: By default, the following objects are not copied to the client database: FOREIGN KEY constraints, UNIQUE constraints, DEFAULT constraints, and the SQL Server ROWGUIDCOL property. This indicates poor support for your scenario
I suggest you remove the unique constraint from the device table.

Catch duplicate key exception and continue to insert

I have the table in PostgreSQL with many records and unique key
CREATE TABLE parcels
(
Id SERIAL PRIMARY KEY NOT NULL,
Number CITEXT NOT NULL,
UserId INT REFERENCES Stations(Id) NOT NULL,
TimeStampUtc TIMESTAMP WITHOUT TIME ZONE NOT NULL
);
CREATE UNIQUE INDEX ON parcels (Number, UserId, (TimeStampUtc::date));
then I import data from Excel and map it to list. At the end I have something like
await _dbContext.Parcels.AddRangeAsync(parcels);
await _dbContext.SaveChangesAsync();
parcels contains about 20,000 records and can contains records witch violates the unique constraint. In such case I need to skip that records and continue to insert.
Now I got an expected error
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred
while updating the entries. See the inner exception for details. --->
Npgsql.PostgresException: 23505: duplicate key value violates unique
constraint "parcels_number_userid_timestamputc_idx"
How to ignore it and continue to insert?
I found some similar questions like this and this but I don't want to load table to memory, because there are a lot of records to compare
You should be able to do is the following:
create a stored procedure that receives the parcels and does an insert paired with a join on the column with the unique key constraint (= filter out all that already have a value in the where condition)
call the stored procedure from your code and pass the parcels
in case you need an artificial id, there are two ways to do it:
return the ID from the insert function (returning id) that the database created automatically for you
generate the Id on your client (usually only works well when you have GUID columns)

C# with SQLite and foreign key

I want to implement a patients data base for our software, and have an issue with the foreign key statement. I am using the latest SQLite DLLs, with C#.
When I try to run below code:
dbConnection = "Data Source=SQLiteMyDatabase.db;foreign keys=true;";
if (connections == 0)
{
cnn = new SQLiteConnection(dbConnection);
cnn.Open();
this.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS patients ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) NOT NULL;");
this.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS images ( FOREIGN KEY (patientID) REFERENCES patients(id), nameRed VARCHAR(20) NOT NULL PRIMARY KEY;");
}
I get the error:
SQLite error near "FOREIGN": syntax error
Why does this happen?
In order to create a foreign key, you must first create the column:
CREATE TABLE IF NOT EXISTS images (
nameRed VARCHAR(20) NOT NULL PRIMARY KEY,
patientID INT,
FOREIGN KEY (patientID) REFERENCES patients(id)
);
Please note:
I moved the primary key column (nameRed) first in the list of columns created, but that's just convention. It could go second.
You use the data type VARCHAR and SQlite will accept that, but be aware that it gets mapped to type TEXT, the maximum length will be ignored, and you will be able to store other data types in the column. SQLite is a bit funny when it comes to data types (it has a very well thought-out system but it's quite different from most other SQL databases).
To make things more confusing, you are allowed to declare the PRIMARY KEY as a property of the column (although you can also do it after the list of columns as with the FOREIGN KEY).

Entity Framework bug, insert command generation

I am working with PostgreSql DB using Entity Framework:
When I add new item into DB it generates strange code:
INSERT INTO (SELECT "person_contact"."person_id" AS "person_id",
"person_contact"."contact_id" AS "contact_id"
FROM "public"."person_contact" AS "person_contact")
("person_id","contact_id")
VALUES (cast(141792 as int8),cast(289406040 as int8))
So it add
SELECT "person_contact"."person_id" AS "person_id",
"person_contact"."contact_id" AS "contact_id"
FROM "public"."person_contact" AS "person_contact"
instead of table name "public"."person_contact"
How to resolve this Entity Framework bug ???
UPD: Same issue when I try to delete "person_contact" entry. In delete statement instead of table name - select query.
There are several ways to try and fix this:
Firstly, it could be that your model has become corrupt. You could try deleting the model and recreating it. Also see my answer to this question: SQL Server foreign keys messing with entity framework model
Secondly, you say that it only happens with this table. Is there anything special about this table.
Thirdly, you could try a different .net connector for ProgressSQL, see: http://www.devart.com/dotconnect/entityframework.html
These are listed in the order that I would try them.
Most likely you forgot to create primary key on this table.
I've had the same problem and the solution in my case was very simple. The problem was that I had a column named "id", but I forgot to make it Primary Key. The moment I set it as Primary Key everything was OK.
It is very strange, because EF, normaly won't import table without primary key, but when you have column named "id" it assumes that it is a primary key.
The structure of my table was:
*DROP TABLE IF EXISTS "public"."fact_season_tickets";
CREATE TABLE "public"."fact_season_tickets" (
"id" int8 DEFAULT nextval('fact_season_tickets_id_seq'::regclass) NOT NULL,
"season_ticket_id" int8 NOT NULL,
"date_key" int4 NOT NULL,
"station_id" int4 NOT NULL,
"amount" numeric(18,2) DEFAULT 0 NOT NULL,
"status" int4 NOT NULL
)
WITH (OIDS=FALSE)*
The generated by NpgSql INSERT statement was:
*INSERT INTO (SELECT "fact_season_tickets"."id",
"fact_season_tickets"."season_ticket_id",+
"fact_season_tickets"."date_key",
"fact_season_tickets"."station_id",
"fact_season_tickets"."amount",
"fact_season_tickets"."status"
FROM "public"."fact_season_tickets" AS "fact_season_tickets")
("season_ticket_id","date_key","station_id","amount","status")
VALUES (510::int8,20150630,2,18.00::numeric,1)
RETURNING "id"*
The solution was just creating a primary key:
*ALTER TABLE "public"."fact_season_tickets" ADD PRIMARY KEY ("id");*

SubSonic not recognizing SQLite foreign keys

I'm using SubSonic 3.0.0.3 and I can't seem to get the ActiveRecord .tt files to recognize and generate code for the foreign keys and relationships in my SQLite database.
I think it generates everything else just fine, but after looking at other snippets online it looks like there should be more generated code than just single classes in ActiveRecord.cs and Structs.cs for each of my tables. Looking inside Structs.cs, IsForeignKey is always false for every column, even the ones I have a foreign key defined for. Additionally, each Foreign Keys region is empty within each generated ActiveRecord class.
I'm using VS2008 with references to SubSonic 3.0.0.3, System.Data.SQLite 1.0.66.0, and System.Data.SQLite.Linq 2.0.38.0 in my project. I created the database using SQLite Expert Personal 3.1.0.2076. I made some dummy tables to try to test out how SubSonic handles one:many and many:many relationships. Here's the DDL SQLite Expert spits out for my small database:
CREATE TABLE [Person] (
[PersonID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonName] TEXT NOT NULL,
[PersonAge] INT NOT NULL
);
CREATE TABLE [Group] (
[GroupID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[GroupName] TEXT NOT NULL,
[GroupDescription] TEXT NOT NULL
);
CREATE TABLE [Dog] (
[DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonID] INT NOT NULL CONSTRAINT [DogPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
[DogName] TEXT NOT NULL);
CREATE TABLE [GroupPersons] (
[GroupID] INTEGER NOT NULL CONSTRAINT [GroupPersonToGroupFK] REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE,
[PersonID] INTEGER NOT NULL CONSTRAINT [GroupPersonToPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [sqlite_autoindex_GroupPersons_1] PRIMARY KEY ([GroupID], [PersonID]));
I know foreign keys are enabled and work in the database - SQLite Expert says they're on and when I change data in one place, like Person's PersonID, it does indeed change that PersonID in the Dog and GroupPersons tables. I've tried re-adding the database to the project, 'running custom tool' to get the .tt files to execute again, and even deleting them and adding them back. I can get a simple project to build that perform simple querying and insertions, however I tried just now to change the primary key of a single Person, Dog, or Group and x.Save() but System.Data.SQLite threw an exception for all three, saying SQLite error near "WHERE":syntax error. at the Save().
Any suggestions for what I should try to do next?
It seems that the FKTables attribute for each table is not assigned in the file "SQLite.ttinclude". So i add some lines of code and managed to generate foreign key code :
After line 16 (var schema = conn.GetSchema("COLUMNS");), insert :
var schemaForeignKeys = conn.GetSchema("FOREIGNKEYS");
After line 29 (tbl.Name = row["TABLE_NAME"].ToString();), insert :
tbl.FKTables = new List<FKTable>();
var foreignKeyTables = schemaForeignKeys.Select("TABLE_NAME='" + tbl.Name + "'");
foreach (var foreignKeyTable in foreignKeyTables) {
FKTable foreignKey = new FKTable();
foreignKey.ThisTable = foreignKeyTable["TABLE_NAME"].ToString();
foreignKey.ThisColumn = foreignKeyTable["FKEY_FROM_COLUMN"].ToString();
foreignKey.OtherTable = foreignKeyTable["FKEY_TO_TABLE"].ToString();
foreignKey.OtherColumn = foreignKeyTable["FKEY_TO_COLUMN"].ToString();
foreignKey.OtherClass = CleanUp(foreignKey.OtherTable);
foreignKey.OtherQueryable = foreignKey.OtherClass;
tbl.FKTables.Add(foreignKey);
}
And after line 53 (col.IsNullable=row["IS_NULLABLE"].ToString()=="True";), insert :
col.IsForeignKey = tbl.FKTables.Any(x => x.ThisColumn == col.Name);
This is for generate the foreign key code.
Moreover, you have maybe encounter a problem when you have to delete a record which has a column to be a foreign key in an other table ? For exemple :
Person(Id, Name)
Dog(Id, #PersonId)
If you have set the #PersonId foreign key on-delete action to "SET TO NULL", this won't work because foreign key support is disabled by default in SQLite 3.6.23.1 (version used by Data.SQLite 1.0.66.0).
To enable foreign key support, you have to execute this command with each connection :
PRAGMA foreign_keys = ON;
Now, this is not supported by Data.SQLite, but it will (in version 1.0.67.0, http://sqlite-dotnet2.cvs.sourceforge.net/viewvc/sqlite-dotnet2/SQLite.NET/System.Data.SQLite/SQLiteConnection.cs?r1=1.80&r2=1.81).
So you have to wait for the release or you can (like me) download the Data.SQLite source and compile the last version. It work great for me.
Good luck.
And sorry for my english :)
I'm trying to reason this. There seems to be two issues at hand:
Subsonic not recognising your foreign keys
The x.Save() function sending that error message.
SQLite will be enforcing referential integrity on its own, so while Subsonic does not see the foreign references, SQLite does, and that's why your updates go through. Subsonic does not drive SQLite, it is driving itself, which is fine.
I'm attempting to learn what SubSonic is and does. In the mean time, I have this hypothesis: the table definitions are not parsed correctly. If x.Save() is uses automatically generated SQL, it could be that the two issues are really just one.
To validate (or invalidate) this hypothesis, could you try defining the tables thus, giving the foreign keys as table attributes, not attributes of specific fields:
CREATE TABLE [Dog] (
[DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonID] INTEGER NOT NULL,
[DogName] TEXT NOT NULL,
FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE TABLE [GroupPersons] (
[GroupID] INTEGER NOT NULL,
[PersonID] INTEGER NOT NULL,
FOREIGN KEY ([GroupID]) REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ([GroupID], [PersonID]));

Categories

Resources