How to figure out what exact entity resulted in DbContext.SaveChanges FK conflict failure - sql-server

With Entity Framework Core (in case it matters, I'm using version 2.2), I update a number of entities in the DB context and call SaveChanges. In case saving fails because of the foreign key issue, I can't figure out what exact entities have foreign key issues. Is it possible to figure this out?
When Foreign Key conflict happens, SQL Server returns an error with code 547 and text The UPDATE statement conflicted with the FOREIGN KEY constraint "<Constraint>". The conflict occurred in database "<Database name>", table "<Table name>", column '<Column name>' without reference to exact invalid rows.
I have looked at DbUpdateException properties, but no luck:
var dbUpdateEx = ex as DbUpdateException;
var erroredEntries = dbUpdateEx.Entries;
// The documentation says that Entries property "Gets the entries that were involved in the error",
// but in my case it's always empty.
Also, I have tried validating entities manually before save, but such validation does not find FK conflicts. Code sample:
var valProvider = new ValidationDbContextServiceProvider(dbContext);
var valContext = new ValidationContext(entity, valProvider, null);
var entityErrors = new List<ValidationResult>();
var result = Validator.TryValidateObject(entity, valContext, entityErrors, true);
When I save more than 100 records and the save fails because of FK conflict in a single entity, it's quite frustrating to look for this single invalid record by hand.

Related

ASP.NET Core issue with Update record in Sql

I am using ASP.NET Core and SQL Server . When trying to update a record in the database I am getting this following error message:
Database operation expected to affect 1 row(s) but actually affected 39 row(s).
I will past the code below, but the part that is very confusing is that the "where" statement is using the tables primary key -- there is no way that there is more than 1 record with the same key -- which I verified several times.
using (var transaction = _ctx.Database.BeginTransaction())
{
var sql = #"Update [Policies] SET DateInvalid = #DateTimeNow Where EntryNum = #EntryNum";
_ctx.Database.ExecuteSqlCommand(sql,
new SqlParameter("#DateTimeNow", DateTime.Now),
new SqlParameter("#EntryNum", existing.EntryNum) );
_ctx.SaveChanges();
transaction.Commit();
}

web2py: FOREIGN KEY constraint failed when it shouldn't

I have this table:
db.define_table('block',
Field('ore_id', 'reference ore'),
Field('location', type='integer', required=True, notnull = True),
Field('x', type='integer'),
Field('y', type='integer'),
Field('block_mass_tn', type='double')
)
and this:
db.define_table('block_processing_line_1',
Field('block_location', 'reference block'),
Field('processing_time_d', type='double'),
Field('concentrate_tn', type='double'),
Field('concentrate_quality', type='double')
)
In the table block I have 100 entries, id and location running from 1-100. When I add a new record to the latter table with block_location being 1, it accepts it, but when I try to add 2 or 3 or so on, it won't accept it and it says "FOREIGN KEY constraint failed". I have other tables, which have that identical field: Field('block_location', 'reference block'), but they don't have any problem with values above 1. What is wrong here?
One other thing that comes in mind is that I had the same problem before with that table and I realized I had made a typo: Field('block_location', 'reference ore'), so I referenced a wrong table and that table ore only had one entry (thus accepting only 1 and nothing else). But now even though I fixed it, the problem persists. Could there be some traces of that former line somewhere in the DAL? I truncated both tables (block and block_processing_line_1after fixing.
#Anthony:
Have you compiled the app or turned off migrations? If neither, maybe
try dropping the block_processing_line_1 table.
This solved it! I dropped the table and saved, "undropped" the table and saved, and after that it worked as it should. So this line:
db.block_processing_line_1.drop()

Cayenne, Postgres: primary key generation

I'm using Cayenne 3.2M1 and Postgres 9.0.1 to create a database. Right now I'm having problems with the primary key generation of Cayenne since I have tables with more than one primary key and as far as I've read Cayenne cant generate more that one primary key per table. So I want the Postgres to do that work.
I have this table:
CREATE TABLE telefonocliente
(
cod_cliente integer NOT NULL DEFAULT currval('cliente_serial'::regclass),
cod_telefono integer NOT NULL DEFAULT nextval('telefonocliente_serial'::regclass),
fijo integer,
CONSTRAINT telefonocliente_pkey PRIMARY KEY (cod_cliente, cod_telefono)
)
WITH (
OIDS=FALSE
);
TelefonoCliente telefono = context.newObject(TelefonoCliente.class);
telefono.setFijo(4999000);
context.commitChanges();
and this is the error I get:
INFO: --- transaction started.
19/11/2013 22:46:17 org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy processSchemaUpdate
INFO: Full or partial schema detected, skipping tables creation
19/11/2013 22:46:17 org.apache.cayenne.log.CommonsJdbcEventLogger logQuery
INFO: SELECT nextval('pk_telefonocliente')
Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.3.2M1 Jul 07 2013 16:23:58] Commit Exception
at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:759)
at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
at org.example.cayenne.Main.main(Main.java:45)
Caused by: org.postgresql.util.PSQLException: ERROR: no existe la relaci?n ≪pk_telefonocliente≫
Position: 16
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:374)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:254)
at org.apache.cayenne.dba.postgres.PostgresPkGenerator.longPkFromDatabase(PostgresPkGenerator.java:79)
at org.apache.cayenne.dba.JdbcPkGenerator.generatePk(JdbcPkGenerator.java:272)
at org.apache.cayenne.access.DataDomainInsertBucket.createPermIds(DataDomainInsertBucket.java:171)
at org.apache.cayenne.access.DataDomainInsertBucket.appendQueriesInternal(DataDomainInsertBucket.java:76)
at org.apache.cayenne.access.DataDomainSyncBucket.appendQueries(DataDomainSyncBucket.java:78)
at org.apache.cayenne.access.DataDomainFlushAction.preprocess(DataDomainFlushAction.java:188)
at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:144)
at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:685)
at org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:651)
at org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:712)
at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:648)
at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:852)
at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:629)
at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
... 2 more
I've been trying the suggestions on Cayenne tutorial "generated columns", "primary key support" but I seems to always get some error.
INFO: SELECT nextval('pk_telefonocliente')
Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.3.2M1 Jul 07 2013 16:23:58] Primary Key autogeneration only works for a single attribute.
I want to know how to solve this.
Thanks in advance
From your description in comments, out of 2 columns comprising the PK of 'telefonocliente', only one is truly independent - 'cod_telefono'. This will be what Cayenne will generate. In case of PosgreSQL, you will need the following sequence in DB for this to happen:
CREATE SEQUENCE pk_telefonocliente INCREMENT 20 START 200;
Now, where does the second PK 'cod_cliente' come from? Since it is also FK to another table, it means it is a "dependent" PK, and must come from a relationship. So first you need to map a many-to-one relationship between 'telefonocliente' and 'cliente'. Check "To Dep Pk" checkbox on the 'telefonocliente' side. Generate a matching ObjRelationship for your Java objects. Now you can use it in your code:
Cliente c = .. // get a hold of this object somehow
TelefonoCliente telefono = context.newObject(TelefonoCliente.class);
telefono.setFijo(4999000);
telefono.setCliente(c); // this line is what will populate 'cod_cliente' PK/FK
That should be it.
The primary key is allowed just to be one per a table! In your case you create a primary key on two columns, that is right, it is defined in an SQL standard and Postgres supports it well.
However there is a not in Cayenne documentation:
Cayenne only supports automatic PK generation for a single column per table.
see http://cayenne.apache.org/docs/3.0/primary-key-generation.html at the bottom of the page.
Probably they can fix it in a newer version or you can put a request to the Cayenne community.

database changed outside of DataSet: duplicate key exception

I'm using a strongly typed DataSet and I fill all Tables of the DataSet at the beginning of the program. I have one table that can be changed by multiple users. Sometimes it happens that this table is changed outside of the dataSet, so that the user does not have the newest version of the table and gets an Exception when trying to insert the same row.
my procedure:
When a user wants to insert a new row I check the dataset for the existence of the row and only insert the row if it is not already there. But in the meantime (between filling and checking) it happens that another user inserts the same row into the database. So I get the SQLException: Violation of PRIMARY KEY constraint (error 2627).
How can I avoid this?
Thats how I check if the row is already part of database:
customersTableAdapter custAdapter = new customersTableAdapter();
AzureDataSet.customersRow custRow= azureDataSet.customers.FindBycustID(hash);
if(custRow==null)
{
try
{
custRow= azureDataSet.customers.NewcustomersRow();
custRow.custID = hash;
azureDataSetcustomers.AddcustomersRow(custRow);
retryPolicy.ExecuteAction(() =>
{
customersTableAdapter.Update(azureDataSet.customers);
});
}
catch
{
//check for error 2627??
}
}
return custRow.custID;
The problem is that TableAdapters do not provide a data synchronisation.
I wrote a stored procedure to insert the data and checked in the stored procedure if the value is already there.
I also catched a duplicate key exception which migth still occur, because of the delay between the check and the insert operation.

PostgreSQL JPA cascade partially working

Again we probably have a very simple problem;
our database look like:
CREATE TABLE Question (
idQuestion SERIAL,
questionContent VARCHAR,
CONSTRAINT Question_idQuestion_PK PRIMARY KEY (idQuestion),
);
CREATE TABLE Answer (
idAnswer SERIAL,
answerContent VARCHAR,
idQuestion INTEGER,
CONSTRAINT Answer_idAnswer_PK PRIMARY KEY (idAnswer),
CONSTRAINT Answer_idQuestion_FK FOREIGN KEY (idQuestion) REFERENCES Question(idQuestion),
);
So a Question have many Answers.
Following in entity generated by Netbeans 7.1.2 we have field:
#OneToMany(mappedBy = "idquestion", orphanRemoval=true, cascade= CascadeType.ALL, fetch= FetchType.EAGER)
private Collection<Answer> answerCollection;
As you can see I've already added all possible orphan removal and cascade instructions for cascade removal of collection. And it's working fine but for one moment:
You can delete a Question and connected Answers only if they were created in previous 'instance' of our Application. If I first create a new Question and even one Answer and then go straight and delete it we got an error like:
root cause
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: update or delete on table "question" violates foreign key constraint "answer_idquestion_fk" on table "answer"
Detail: Key (idquestion)=(30) is still referenced from table "answer".
Error Code: 0
Call: DELETE FROM question WHERE ((idquestion = ?) AND (version = ?))
bind => [2 parameters bound]
Query: DeleteObjectQuery(com.accenture.androidwebapp.entities.Question[ idquestion=30 ])
root cause
org.postgresql.util.PSQLException: ERROR: update or delete on table "question" violates foreign key constraint "answer_idquestion_fk" on table "answer"
Detail: Key (idquestion)=(30) is still referenced from table "answer".
If I restart (rebuild, redeploy) the application it's working though.. why? Thanks!
Try adding the EclipseLink #PrivateOwned extension annotation to your collection mapping.
As for the issue with deletion not working until you restart the app, two things come to mind:
You might be using very long EntityManager sessions where everything stays attached to the EnitityManager. If that's the case, it's the change of EntityManager session forced by a restart that's helping. Consider using shorter EntityManager sessions by calling EntityManager.close() when you're done with a session. Work with detached entities and EntityManager.merge() state back in when you want to modify it.
The second level cache, if any, will be cleared by a redeploy. Try disabling the second level cache and see if that helps.

Resources