ObjectContext Error with using .AddRange in EF 6 - c#

I'm trying to find some help on an error I'm getting when using the .AddRange in EF 6. I'm getting the following 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: AcceptChanges cannot continue because
the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are
unique before calling AcceptChanges.
As the error states, my records are actually getting added to the table so I don't know where fix the error.
Doing some research I found a bunch of posts where others say that it has to do with the .edmx file and a primary key on the table. Their suggestion is basically to add the PK and then rebuild the .edmx file. This doesn't fit my scenario for two reasons, one is that I'm using EF 6 with DataBase First so there isn't an .edmx file and second is that this is mapped to and Oracle 11 DB and so the identity is created with a trigger (which seems to work when I look at the added records).
Here is my code I'm using as well as the class for the entity.
using (APIcontext db = new APIcontext())
{
if (listLostTime.Count > 0)
{
db.GROUND_HOURS.AddRange(listLostTime);
db.SaveChanges();
}
}
And the entity class
[Table("GROUND_HOURS")]
public partial class GROUND_HOURS
{
[Key]
public decimal RID { get; set; }
[Required]
[StringLength(8)]
public string EMP_ID { get; set; }
[StringLength(2)]
public string COMPANY_CODE { get; set; }
public DateTime OCCURRENCE_DATE { get; set; }
[Required]
[StringLength(25)]
public string PAY_CODE { get; set; }
public decimal PAY_HOURS { get; set; }
public DateTime INSERT_DATE { get; set; }
}
I'm looking for any suggestions.

Decorate the RID property with the attribute DatabaseGenerated( DatabaseGeneratedOption.Identity )
The problem is that entity framework isn't updating the key value RID with the store generated value prior to accepting changes. In your case, with multiple GROUND_HOURS entities created, each will (presumably) have the default RID value of 0. When EF attempts to accept changes, it recognizes than more than one entity has the same key value and complains.

Thanks to #Moho who gave the ultimate fix. This is how I changed the primary key in my entity class to work and is what I used in my application.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RID { get; set; }
I was also able to fix it another way just to let others know. First off because this is and Oracle DB the RID (which is my Primary Key) was scaffold as a Decimal. That caused the RID to always be a 0 when I added my object to a list without specifically assigning it a value. To work around that I changed the RID property to an nullable INT and then when I created my list I set the RID=NULL.
[Key]
public int? RID { get; set; }
This is what I did when created my list.
foreach (var item in results)
{
GROUND_HOURS lostTime = new GROUND_HOURS();
lostTime.RID = null;
lostTime.EMP_ID = item.EmployeeId.ToString("D8");
lostTime.COMPANY_CODE = item.CompanyCode.Trim();
lostTime.OCCURRENCE_DATE = item.OccurrenceDate;
lostTime.PAY_CODE = item.PayCode.Trim();
lostTime.PAY_HOURS = item.Hours;
listLostTime.Add(lostTime);
}

Related

ORA-00904 Linq generates error in SQL query for Oracle

I use EF Core for Oracle and I try to get list of entities from the DbContext.
I have two model classes:
public class CssKursyModel
{
[Key]
[Column("KW_ID")]
public int Kw_Id { get; set; }
public CssTabeleKursowModel CoursesTables { get; set; }
[Column("KW_WAL_Z_ID")]
public int Kw_Wal_Z_Id { get; set; }
[Column("KW_WAL_NA_ID")]
public int Kw_Wal_Na_Id { get; set; }
}
and
public class CssTabeleKursowModel
{
[Key]
[Column("TK_ID")]
public int Tk_Id { get; set; }
[Column("TK_SYMBOL")]
public string Tk_Symbol { get; set; }
[Column("TK_NAZWA")]
public string Tk_Nazwa { get; set; }
}
but when I try to execute this statement
var t = context.CssCourses.ToList();
it returns an exception
ORA-00904: "s"."CoursesTablesTk_Id": invalid identifier
What can I do to resolve this situation?
When I changed my CSSKursyModel and add ForeignKey atributte, like this
{
[Key]
[Column("KW_ID")]
public int Kw_Id { get; set; }
[ForeignKey("TK_ID")]
public CssTabeleKursowModel CoursesTables { get; set; }
[Column("KW_WAL_Z_ID")]
public int Kw_Wal_Z_Id { get; set; }
[Column("KW_WAL_NA_ID")]
public int Kw_Wal_Na_Id { get; set; }
}
and changed method to
var t = context.CssCourses
.Include(i => i.CoursesTables.Tk_Id)
.ToList();
the error message looks like
ORA-00904: "i"."TK_ID": invalid identifier
The Column name is correct (TK_ID) but execution still ended by ORA-00904 exception
Regards
Oracle stores object (and column) names in UPPERCASE by default, unless you enclosed those names into double quotes at time of creation. Oracle people don't do that, so - if someone created the table for you, I presume they did it that way, i.e. didn't use double quotes.
In that case, you can reference column names using any letter case you want.
It means that you should try with "COURSESTABLESTK_ID" (if that Linq thing requires double quotes; I don't speak it so I can't tell). Otherwise, as far as Oracle itself is concerned, COURSESTABLESTK_ID (without double quotes) would do.
Your code would work if someone created the table as
create table some_table ("CoursesTablesTk_Id" number);
I presume that it was, actually,
create table some_table (CoursesTablesTk_Id number);
which means that
select coursestablestk_id from some_table
or
select COURSEStablesTk_Id from some_table
or
select COURSESTABLESTK_ID from some_table
would do.
In my experience, this is because you have on your code a field that does not exist in the database. The signature of this issue is that the query fails because you have a field referenced as "t".[tableName][FiledName]. When the entity framework is unable to map the field to an existing column on the database it creates a field like that.
To troubleshoot do the following:
Identify the field that is causing the problem:
Sometimes this is difficult because of nested relationships. Try adding the [NoMapped] attribute one field at a time until the error goes away.
Check for typos in the field name. Sometimes you include a field as part of a relationship (like using a linked table without any referential integrity constrain) in this case will not be typo, but just a field that does not exists.
Summing Up: Your mission is to find a field in your object that is not in the table, if there is a typo, fix it, if the field does not exist in the database but you need it, add the [NoMapped] attribute.
Hope this helps.
Regards.

Entity Framework creates non-existing column in query

I face the problem that EF creates a column in the query that does not exist in the Oracle database table.
The simplified model which is created by EF looks like this (I use DB first approach):
public partial class USER
{
public int ID { get; set; }
public string NAME { get; set; }
public int PROCESS_ID { get; set; }
public virtual PROCESS PROCESS { get; set; }
}
public partial class PROCESS
{
public PROCESS()
{
this.USER = new HashSet<User>();
}
public int ID { get; set; }
public virtual ICollection<USER> USER { get; set; }
}
I set up the foreign key constraint in the oracle sql developer.
When I try to get the Users for a selected Process like this:
var users = context.Users.Where(u => u.PROCESS_ID == 0);
It produces following error:
ORA-00904: "Extent1"."R1": invalid ID
So i took a look on the produced SQL:
SELECT
"Extent1".ID,
"Extent1".NAME,
"Extent1".R1,
FROM DB.USER "Extent1"
WHERE "Extent1".R1 = :p__linq__0
Of course this produces an error because R1 isn't a column in the table. But I can't figure out where it comes from. It seems like EF can't map the foreign key properly thats why it's also missing in the generated SQL query?
Maybe someone has a tip for me :)
To follow up my comment, here is a link to the conventions.
The convention for a foreign key is that it must have the same data type as the principal entity's primary key property and the name must follow one of these patterns:
[navigation property name][principal primary key property name]Id
[principal class name][primary key property name]Id
[principal primary key property name]Id
Your convention [navigation property name]_ID isn't on the list.
Encountered the same error recently while working with Oracle using DevArt provider. Turned out it was caused by a column name being longer than 30 chars. OP mentioned that the model posted in his question is a simplified one so it still may be the case.

Entity Framework and duplicates

I use Entity Framework as ORM in my project. Let's suppose I use Code-First pattern and I have two models. Such as
internal class First
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
}
internal class Second
{
public int Id { get; set; }
public First ForeignKeyEntity { get; set; }
// other members
}
And here is code populating database:
List<Second> res = GetData();
using (var ctx = new StatisticContext())
{
foreach (var item in res)
{
ctx.Seconds.Add(item);
}
ctx.SaveChanges();
}
As you can see each instance of class Second has instance of class First in its member ForeignKeyEntity. Obviously some instances of First can be duplicated in res. And when I run this code I get DbUpdateException in ctx.SaveChanges() with inner exception that has the following message:
Violation of PRIMARY KEY constraint 'PK_dbo.First'.
Cannot insert duplicate key in object 'dbo.First'.
The duplicate key value is (29459). The statement has been terminated.
I can not to insert duplicated rows but I don't want to insert duplicates, I would like to insert row only if it doesn't exist. How to solve this problem? How to insert foreign key object only if doesn'tt exist?
The normal way of doing things would be to do a read first with item to see if it exists. If it does then you need to use ctx.Seconds.Update(item);
If your items are already on the context, then you can check the state. it will be either State.Modified or State.Added.
Whats in GetData()

Cannot insert the value NULL into column 'MaintenanceId', table '<tableName>'; column does not allow nulls. INSERT fails EF 6.1

So i'm trying to do a simple add an entry to my db on azure but for some reason the db is not generating my PK for the entry.
Below is all the code used to create the db and do the entry.
This is on EF 6.1 on a console app
Context
public BlizzardDbContext() : base("AzureSqlDb")
{
}
public DbSet<Maintenance> Maintenances { get; set; }
Model
public class Maintenance
{
public Maintenance()
{}
public Maintenance(DateTime start, DateTime end, string info)
{
Start = start;
End = end;
Info = info;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MaintenanceId { get; set; }
public DateTime? Start { get; set; }
public DateTime? End { get; set; }
public string Info { get; set; }
}
Test that failed on save changes
var context = new BlizzardDbContext();
context.Maintenances.Add(new Maintenance() {Start = DateTime.Now, End = DateTime.Now, Info = ""});
context.SaveChanges();
I know it sounds so simple, i've used EF a few times before but cannot figure out what is going wrong this time
and here's the error
"Cannot insert the value NULL into column 'MaintenanceId', table '.dbo.Maintenances'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."
Update: Ended up fixing this by deleting the db and recreating it, I think there was something weird going on with EF, it wasn't updating the db with the migration properly since after recreating it the column was then set to be Identity
Just verify whether the MaintenanceId is identity key or not in DB.
If it is not or you are not sure you can try below option
change
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
to
[DatabaseGenerated(DatabaseGeneratedOption.None)]
.None - That means "The database does not generate values."
Log on to your database and verify that the MaintenanceId is indeed auto-generating the key. EF supports this, and I suspect that something fun happened during migration, if you used that, or during the construction of the table.
Also, make sure that you have not disabled tracking of objects in your DbContext class. It is default on, so unless you explicitly disabled it, you do not have to worry about that one :)

Replacement for sqlite-extensions: InsertWithChildren (too many Variables)

i have a project here, were a big amount of data is read from different sources. In a special logic, a data/object-modell is build with these data. So as a result i retrieve a complete SQLite capable object model.
The data were previously written to the SQLite database using a simple:
_connection.InsertWithChildren(model, true);
But, since the source of the data became bigger, this is not possible anymore, cause the Insert method will throw an "too many variables" exception. ;(
Now, i am looking for an replacement for this method. The difficulty here is that within my model, i nearly always have Foreign-Keys in both directions. Parent has Childs, Childs knows Parent.
Performance is not an issue. I don't care if the function needs 10Seconds or 5Minutes. But does anyone have an idea how to handle the Insert, while all Foreign Keys are filled correctly?
If i use a simple
foreach(var entity in _entityList)
_connection.Insert(entity);
the foreign Keys (IDs) are all Guid.Empty;
best regards and cheers,
Chris
Until issue #64 is fixed you can use ReadOnly properties on lists.
For example:
public class Foo
{
[PrimaryKey]
public Guid Id { get; set; }
[OneToMany(ReadOnly = true)]
public List<Bar> Bars { get; set; }
}
public class Bar
{
[PrimaryKey]
public Guid Id { get; set; }
[ForeignKey(typeof(Foo))]
public Guid ParentId { get; set; }
[ManyToOne]
public Foo ParentFoo { get; set; }
}
Will no longer hit the variable limit issue regardless of the operation executed.
You can now insert the elements safely:
// Insert parent 'foo' element
// This won't insert the children or update their foreign keys
conn.InsertWithChildren(foo);
// Insert all children
// This will also update ParentId foreign key if ParentFoo property is set
conn.InsertAllWithChildren(bars)
Or use plain SQLite.Net methods assigning the foreign keys yourself:
conn.Insert(foo);
foreach (var bar in bars) {
bar.ParentId = foo.Id;
conn.Insert(bar);
}

Categories

Resources