Following an introductory tutorial for the new DomainService in Silverlight 4 RIA, I got an unexpected exception. When I perform an update the property EntitiesInError[index].EntityConflict.PropertyNames throws the following exception:
InvalidOperationException: PropertyNames are not available for delete conflicts.
Service method executed:
public void UpdateSr_Supplier(sr_Supplier currentsr_Supplier)
{
// UPDATE the existing sr_Supplier
this.ObjectContext.sr_Supplier.AttachAsModified(currentsr_Supplier, this.ChangeSet.GetOriginal(currentsr_Supplier));
}
From the answer on this thread I gather that I should rather use Silverlight-enabled services with custom service objects (DataContract and DataMember), then assign the values of these custom service objects to the actual server objects (generated from the DB model, be that Linq to Sql or the Entity Data Model), and manually call SubmitChanges() on the DataContext.
PropertyNames will throw an exception if the error you're dealing with is a deletion conflict, this is normal. You should be looking for the underlying database error, which may be neglecting to set a non-null value or a primary key violation, etc.
Related
How can I update my database rows and create new rows if they do not exist with the operations defined in my DB context? I am using the unit of work pattern in my code as described here
I am using the DefaultIfEmpty() method to create new objects if they do not exist in the database.
// this code runs every 10 sec
foreach(/* possible values for property in contract entity */)
{
var contract = unitOfWork.OptionsContractRepository
.Get(/* loop variable match */)
.DefaultIfEmpty(new OptionsContract { /* set default property values */}).Single();
// contract has a list of 'marks'
var mark = contract.OptionsMarks.Where(/* property filter */).DefaultIfEmpty(new OptionsMark { /* set default property values */}).Single();
// update some properties in 'mark'...
mark.OptionsContract = contract; // set 'contract' for the 'mark'
unitOfWork.OptionsContractRepository.Insert(contract);
}
unitOfWork.Save();
I want the code to not worry about whether the object exists in the database and just create new ones on the fly. I am getting errors when I try to save everything to the database:
System.InvalidOperationException: '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 'UlyssesPriceModel2.OptionsContract' 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.'
How can I do this with the unit of work pattern?
I am using AutoMapper and trying out AutoMapper.Collection.EntityFramework, specifically the Persist<T> method.
My "source" is a fairly large object graph that has been converted (by AutoMapper) into some EntityFramework entities. The parent entity is called Log.
In my experimental test, I do the following:
var mapper = collectionConfig.CreateMapper();
var persistence = dbContext.Logs.Persist(mapper);
var testLog = logs.First(); // "logs" is the output of an AutoMapper.Map of a collection.
persistence.InsertOrUpdate<Log>(testLog);
Assert.IsTrue(dbContext.ChangeTracker.HasChanges());
What happens is an exception at the ChangeTracker.HasChanges call:
System.InvalidOperationException: The property 'Id' is part of the
object's key information and cannot be modified.
The stack trace is:
System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInProperty(Int32
ordinal, Boolean detectOnlyComplexProperties, Boolean detectOnly)
at
System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInProperties(Boolean
detectOnlyComplexProperties) at
System.Data.Entity.Core.Objects.ObjectStateManager.DetectChangesInScalarAndComplexProperties(IList`1
entries) at
System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Core.Objects.ObjectContext.DetectChanges() at
System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean
force) at
System.Data.Entity.Infrastructure.DbChangeTracker.HasChanges()
This is a fairly well-known and well-documented exception: it happens when you have an existing EntityFramework entity object and attempt to change the value of one of its primary key property fields.
But I'm not doing that anywhere.
I never set an Id value anywhere in my code. (The value for the Id property comes from elsewhere and is set by AutoMapper when the list of Log objects is created. Because of the nature of the data I'm reasonably sure that an entry for the testLog object does already exist in the database.)
I've been able to save whole collections of Log entries, created by AutoMapper, to the database, through EF, so I don't think my Log entities or the object graph have the problem. I think it's something AutoMapper.Collection.EntityFramework is somehow doing.
I did try the Persist method with a different, simpler entity, with many fewer child entities, and didn't have this problem. But I can't even tell from this error which object in the graph has the supposedly-changed Id value: half the objects in this object graph have a primary key called Id.
I can confirm that the actual value of testLog.Id isn't changed by the InsertOrUpdate. But my attempts to examine an Entry<Log> for testLog or even to look at dbContext.Logs.Local all cause the same exception to be thrown.
So: anyone have an idea why this is happening?
Thanks to Tyler Carlson of AutoMapper.Collection, I have an answer.
persistence.InsertOrUpdate<Log>(testLog);
The problem stemmed from testLog already being of type Log, which is the same type I'm using in EntityFramework. As Tyler said:
Automapper doesn't support mapping to itself, as it causes so many
problems.
[...]
What you should be doing is passing
the dto into the persist call, not the entity it mapped to. That
mapping will happen in the InsertOrUpdate call.
We also discovered that if your Entity and Dto objects share a common base class, and that base class contains the definition of the key fields, this will also happen.
So in my situation, where my Entity and Dto contained 95% of the same fields, derived from a common base class, I had to take the Id property that was the primary key out of the base class, and define it separately in the two classes. That ensured that, while the properties had the same names, they weren't in any sense the same property from a Reflection point of view.
If you're curious about the details, the discussion is here: AutoMapper.Collection Issue 40.
I have a entity object, that has via FK referened other entity object. In my example batch.Equipment references Equipment entity. If I try to insert the object using this code:
var batch = new Batch();
batch.Equipment = session.Load<Equipment>(someEquipmentId);
session.Save(batch);
Everything is working fine, but I would expect that, if it happens that Equipment with the someEquipmentId doesn't exist, nhibernate would throw me ObjectNotFoundException, instead it throws GenericAdoException saying that there was violation of foreign key, which is obvious because the someEquipmentId doesn't exist in database, so the batch cannot be inserted with that equipment id, but I thought nhibernate would handle that for me.
So the question is, is there a way (some mapping attributes or something) that would make nhibernate throw ObjectNotFoundException in such cases, or do I have to do session.Get(someEquipmentId) and check it for null? I mean I can do that, but it gives me in my opinion unecessary db roundtrips, feels like repetitive code and I don't like checking for every reference like that as there are many and the cases where this actually happens are rare and really exception, I prefer putting it whole in try catch and processing the ObjectNotFoundException in one place. I need it to report back to user, why the insert failed, specifying which entity doesn't exist (requirement).
The answer here is pretty straightforward: Load(id) is a contract, representing the existing value. So, if the passed id value could be wrong (not existing), you do not trust it: you must use Get(id) and check the null.
Please, do read this article: Ayende - NHibernate – The difference between Get, Load and querying by id, some extract:
Get() and Load() are here for a reason, they provide a way to get an entity by primary key. That is important for several aspects, most importantly, it means that NHibernate can apply quite a few optimizations for this process.
But there is another side to that, there is a significant (and subtle) difference between Get and Load.
Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
...
Get, however, is different. Get will return null if the object does not exist. Since this is its contract, it must return either the entity or null, so it cannot give you a proxy if the entity is not known to exist. Get will usually result in a select against the database, but it will check the session cache and the 2nd level cache first to get the values first.
session.Load(id) will never return null. It will return a proxy instead in your case, because the id doesn't exist. Load is purposed to NOT hit the database, but to load the object from cache.
"Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
Why is this useful? Well, if you know that the value exist in the database, and you don’t want to pay the extra select to have that, but you want to get that value so we can add that reference to an object, you can use Load to do so:
s.Save(
new Order
{
Amount = amount,
customer = s.Load<Customer>(1)
}
);
The code above will not result in a select to the database, but when we commit the transaction, we will set the CustomerID column to 1. This is how NHibernate maintain the OO facade when giving you the same optimization benefits of working directly with the low level API." - Ayende Rahien
http://ayende.com/blog/3988/nhibernate-the-difference-between-get-load-and-querying-by-id
I encountered some strange issue using entity framework and I am not sure what's the moral.
Should I use a separate context object for every create, update and delete operation?
I have an entity, that has a unique constraint. I receive values from a different thread and want to store them in an entity and persist them to the database. If I am using one DbContext and adding an entity fails because of unique constraint violation, all following attempts to store an valid entity will fail, too. This is because the invalid entity is still in the DbContext's set.
The problem reduces to this, I think:
/*
* m_context is a memeber variable of the class and created once
* new MyEntity("unique") => creates new entity and sets the unique attribute to the passed value
* the entity's key is set automatically
*/
//works fine
try { m_context.MyEntities.Add(new MyEntity("unique"); m_context.SaveChanges() } catch{}
//fails because of unique constraint violation => this is okay
try { m_context.MyEntities.Add(new MyEntity("unique"); m_context.SaveChanges() } catch{}
//fails, too => not okay and not expected
try { m_context.MyEntities.Add(new MyEntity("unique2"); m_context.SaveChanges() } catch{}
Exceptions are System.Data.Infrastructure.DbUpdateException (an error occurred while updating the entries) -> System.Data.UpdateException (an error occurred while updating the entries) -> System.Data.SQLite.SQLiteException (constraint failed column ... is not unique)
=> In the scenario above twice, with separate context (see below), once, only.
I think the problem will occur with every database provider and is not related to SQLite.
So there are at least two solutions:
Remove the second entity manually from the DbSet in MyContext
Use a seperate context for each create operation
I tend to use the second. This is because I saw a lot of using statements in tutorials/code.
I never understood the reason for this massive recreation of the same object, but this might be a (the) reason. Is this true, or am I completely wrong.
This problem also exists for update and delete operations. And another reasons for failure might be a foreign key violation.
So what's the best practice here? Separate context object per operation? But is the object really that light-weight? And should I apply the "one context per operation" to read operations, too?
As already mentioned, the fix is obvious, but the moral is not totally clear to me, and I am interested in what's the best practice here. Thanks!
In my application, I have a method that I call from code which seeds the database and this works fine.
I Have just created a new big method which also adds a lot more data to the database.
When I call this, it appears to work fine the first time it has run, but, if I run it again within a few minutes of the previous attempt, I get 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.
I am unsure how to fix this, can anyone advise anything? (other than not to run this within a few minutes of the last attempt!).
This Error usually occurs when you try to save an entity model which has not defined well the primary key (composite key) as like in the database.
Define the keys in EntityTypeConfiguration file like
this.HasKey(f => new { f.ID1, f.ID2 });
Possible duplicate of:
InvalidOperationException when calling SaveChanges in .NET Entity framework
Autonumber with Entity Framework
Here are u useful links on working with self-tracking entities and the Object State Manager
http://msdn.microsoft.com/en-us/library/bb896269.aspx
http://msdn.microsoft.com/en-us/library/ff407090.aspx