private void SuaKH()
{
KhachHang kh = new KhachHang();
kh.MaKH = textBox1.Text;
kh.TenKH = textBox2.Text;
kh.SDT = textBox4.Text;
kh.DiaChi = textBox3.Text;
db.KhachHangs.Attach(kh);
db.Entry(kh).State = EntityState.Modified;
db.SaveChanges();
}
Attaching an entity of type 'WindowsFormsApplication1.Models.KhachHang' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the
Related
I try to implement a XUnit Test for Asp.net Core DBContext, But I got below error.
Message: System.InvalidOperationException : The instance of entity type 'Ads' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
Here is my current code:
public class AdsServiceTest
{
private readonly DbContextOptions<SensingSiteDbContext> _options;
private readonly SensingSiteDbContext _context;
private readonly AdsService _AdsService;
public AdsServiceTest()
{
//initialize db options
_options = new DbContextOptionsBuilder<SensingSiteDbContext>()
.UseInMemoryDatabase()
.Options;
//get service
_context = new SensingSiteDbContext(_options);
//initialize dbcontext
List<Ads> listAds = new List<Ads>() {
new Ads(){ Id=1,AdsName="Ads1", Deleted=false},
new Ads(){ Id=2,AdsName="Ads1", Deleted=false},
new Ads(){ Id=3,AdsName="Ads1", Deleted=false}
};
_context.Advertisements.AddRange(listAds);
//context.Advertisements
BaseLib.SSDbContext<Ads, AdsService> ssDbContent = new BaseLib.SSDbContext<Ads, AdsService>(_context);
_AdsService = ssDbContent.GetService((x, y) => new AdsService(x, y));
}
[Theory]
[InlineData(1)]
public void FindById(int id)
{
Ads adsResult = _AdsService.FindById(id);
Ads adsTarget = _context.Advertisements.Find(adsResult.Id);
Assert.True(adsTarget.Equals(adsResult));
}
//Failed by error System.InvalidOperationException : The instance of entity type 'Ads' cannot be tracked because another instance of this type with the same key is already being tracked
[Fact]
public void Update()
{
Ads adsResult = new Ads() { Id = 1, AdsName = "UpdateAds1" };
_AdsService.UpdateAds(adsResult);
Ads adsTarget = _context.Advertisements.Find(adsResult.Id);
Assert.True(adsTarget.Equals(adsResult));
}
}
There is no problem for Find, but failed on Update. AdsService is implemented to call SensingSiteDbContext. It seems I need to use scope lifetime for SensingSiteDbContext. But, I do not know how to implement it.
I have changed ObjectState for Update.
public virtual void Update(TEntity entity)
{
entity.ObjectState = ObjectState.Modified;
_dbSet.Update(entity);
_context.SyncObjectState(entity);
}
Any help would be appreciated.
You are new'ing up your own entity, when, you should just get that entity you've already added from the context:
Ads adsResult = new Ads() { Id = 1, AdsName = "UpdateAds1" };
_AdsService.UpdateAds(adsResult);
With this code, Entity Framework is saying, "Hey, I already have an entity with that key (check your constructor, you're putting an entity in with that same Id), but this object; I don't know what to do with it (because it came from outside with a key that already exists)".
You can change it to exactly what you're doing in the previous test:
Ads adsResult = _AdsService.FindById(id);
//do your changing here
_AdsService.UpdateAds(adsResult);
Using Entity Framework Core i am trying to save changes twice for the same entity collection.
public async Task Save(IEnumerable<Request> request)
{
var entities = request.Select(x => new MyEntity()
{
FileName = x.FileName,
Status = Status.Downloading
});
// save as downloading
await _dbContext.MyFiles.AddRangeAsync(entities).ConfigureAwait(false);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
// start downloading
await _storage.DownloadFilesAsync(request).ConfigureAwait(false);
// save as downloaded
foreach (var entity in entities)
{
entity.Status = Status.Downloaded;
}
// this save DOES NOT update the entities with status Downloaded
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
The first call to SaveChanges() method creates entities in the DB with status Downloading.
Then i am modifying entity's status to Downloaded and call SaveChanges() again. However this time entities does not get updated in DB.
I thought entities are already loaded in the Db context, so i dont have to reload them before second SaveChanges. Just modifying the property would mark them modified. But its not working.
Update 1
I updated code as below and explicitly set State as Modified
// save as downloaded
foreach (var entity in entities)
{
entity.Status = Status.Downloaded;
// The exception occurs at line below on 2nd loop
_dbContext.Entry(entity).State = EntityState.Modified;
}
Now im getting exception
System.InvalidOperationException: The instance of entity type
'MyEntity' cannot be tracked because another instance of this type
with the same key is already being tracked. When adding new entities,
for most key types a unique temporary key value will be created if no
key is set (i.e. if the key property is assigned the default value for
its type). If you are explicitly setting key values for new entities,
ensure they do not collide with existing entities or temporary values
generated for other new entities. When attaching existing entities,
ensure that only one entity instance with a given key value is
attached to the context. at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey
key, InternalEntityEntry entry) at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry
entry) at
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState
oldState, EntityState newState, Boolean acceptChanges)
The entity has Id property of type int and The Id property also has [Key] attribute.
public class MyEntity
{
[System.ComponentModel.DataAnnotations.Key]
public int Id {get;set;}
public string FileName {get;set;}
public string Status {get;set;}
}
Update 2
So after doing little more debugging, i found the first SaveChanges() operation is creating the records in the database and also assigning unique Ids to each record as expected. The Id is auto incremented identity column in the database.
However the entities are not refreshed with new Ids. so all the entities still have value 0 after first savechanges.
How do i refresh these entities with newly created Ids?
Inside foreach loop i tried reloading as
_dbContext.Entry<OcrFile>(entity).ReloadAsync().ConfigureAwait(false);
but that didn't refresh.
Resolved!!. I have to eagerly load entities using ToList() or ToArray() before i pass them to AddRange method
var entities = request.Select(x => new MyEntity()
{
FileName = x.FileName,
Status = Status.Downloading
}).ToList();
I am not sure that is a bug in EF but i created a separate SO thread for that question
I have a very quick question. When selecting a record in EF 7 RC2, it doesn't seems to include the newly added record.
Here is my code:
public tblCategory AddOrUpdateCat(ref DbLocal ef, int catId, string catName, int catType)
{
tblCategory db = ef.tblCategories.FirstOrDefault(rec => (rec.CatId == catId));
if (db == null)
{
db = new tblCategory();
db.CatId = catId;
ef.tblCategories.Add(db);
}
db.Name = catName;
db.CatType = catType;
ef.SaveChanges();
return db;
}
The second time calling the function with the same CatId throws exception:
Exception thrown: 'System.InvalidOperationException' in Microsoft.EntityFrameworkCore.dll
Additional information: The instance of entity type 'tblCategory' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique
This is because the 2nd time, calling ef.tblCategories.FirstOrDefault(rec => (rec.CatId == catId)) with the same Id will not retrieve the newly added record.
This behavior is different than in RC1. Any idea how to make it so it behaves like in RC1?
Thanks!
Shouldn't that be tblCategory db = ef.tblCategories.FirstOrDefault(rec => (rec.CatId == catId)); or if catId is the primary key, then tblCategory db = ef.tblCategories.Find(catId);
assuming ef is your DbContext
To preface this question: my problem is not because I'm directly setting a key property in my model entity object (which is the cause of the issue in other search results for the same exception message).
I'm making heavy use of composite keys in my application, here's a simplified version of my current DB schema (key fields in *asterisks*):
Tenants( *TenantId*, ... )
Categories( *TenantId*, *CategoryId*, ... )
Documents( *TenantId*, *DocumentId*, CategoryId, ... )
The Documents table has FK relationships with both Tenants and Categories, both using the same Documents.TenantId column. The Documents.CategoryId column is NULLable.
When I do something like this, I get the exception:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
doc.Category = newCategory; <-- exception is thrown on this line, without calling dbContext.SaveChanges() at all.
I believe the exception is because setting Category on the Document instance causes the TenantId property to be set indirectly by EF (because it's part of the Documents -> Categories FK association.
What is the solution?
Workaround Update
I'm able to hack it by creating the new Category entities then saving them, to get the IDENTITY values back, then setting the Document properties directly:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
dbContext.SaveChanges();
doc.CategoryId = newCategory.CategoryId
dbContext.SaveChanges();
But ideally I'd like this to work in a single call to SaveChanges() and using the Entity Model Navigation Properties instead of scalar attribute properties.
For this initial problem, I worked-around it using the "Workaround Update" I posted to my original posting.
However this problem happened again for a different entity type (again, with a composite key involved in a foreign-key) and I noticed that EF throws the exception even if you call dbContext.Entry() on any entity in the graph while the new entity is in the Added state - but it does not throw the exception again if you re-call Entry() or even SaveChanges(), and in fact it saves the new entities correctly in spite of the initial exception - so I'm thinking this might just be a bug in EF.
Here's essentially what I have now:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
doc.CategoryId = newCategory.CategoryId
try {
dbContext.Entry( doc );
}
catch(InvalidOperationException) {
}
dbContext.SaveChanges();
It's ugly, but works - and avoids having to call SaveChanges twice.
When updating myItem1 the related entity Entity2 doesn't update but EF tries to add a new Entity2. It throws a primary key constraint error. MyItem has a many-to-one relationship with Entity2
public HttpResponseMessage PutMyItem(MyItem myitem)
{
if (ModelState.IsValid)
{
MyItem myItem1 = db.MyItems.First(m => m.MyItemId == myitem.MyItemId);
myItem1.Name = myitem.Name;
myItem1.Entity2 = myitem.Entity2;
db.ObjectStateManager.ChangeObjectState(myItem1, EntityState.Modified);
try
{
db.SaveChanges();
}
Looks like this line attaches Entity2 to the context in the Added state. Setting the state of myItem to Modified doesn't affect its child entities...
myItem1.Entity2 = myitem.Entity2;
If you are sure this is a valid entity that already exists in the database, change its state to Modified...
db.ObjectStateManager.ChangeObjectState(myItem1.Entity2, EntityState.Modified);
Also, there shouldn't be a need to mark myItem1 as Modified, as it should already in Modified state when its Name property was set.