ObjectContext.Attach generates incorrect entity key - entity-framework

I'm using .NET 4.0 with EF POCO's. I have a conceptual model from which a part is shown in the image. Everything works fine, except one thing which I have been almost busy with for 2 days and still don't get it to work!
In my conceptual model, a WebShop can have one or more related WebShopCategory instances. Each WebShopCategory associates a WebShop with a Category and enables me to specify additional properties for a WebShop/Category combination.
See image here: http://postimage.org/image/djrn4xh37/
When creating a new WebShop instance of course I need to save it, which I do in an Update method of a WebShopRepository which uses the EF ObjectContext as shown in the code below:
using (MyEntities entities = new MyEntities()) {
if (webShop.ID == 0) {
entities.Countries.Attach(webShop.Country);
foreach (PaymentMethod paymentMethod in webShop.PaymentMethods) {
entities.PaymentMethods.Attach(paymentMethod);
}
foreach (QualityMark qualityMark in webShop.QualityMarks) {
entities.QualityMarks.Attach(qualityMark);
}
foreach (WebShopCategory category in webShop.Categories) {
entities.Categories.Attach(category.Category);
}
entities.WebShops.AddObject(webShop);
}
entities.SaveChanges();
}
One thing to note is that all related items already exist in the data source. As they are not yet in the ObjectContext, I've attached them before adding the created WebShop to the ObjectContext. This seems to work as expected: all entities that have been attached have an EntityState.Unchaged state which is correct. The added WebShop is in the Added state which also is correct.
However, it can happen that when I attach the Category instances (category.Category) EF gives an exception stating that there is already an item in the object context with the same key. I have found this ObjectStateEntry instance and what I noticed was that the EntityKey only consists of the EntitySet name, and not with the ID also in the key. Other EntityKeys that have been added to the context are all in the form of EntitySetName;ID=value
To me it seems that the entity key is somehow not being determined correctly by the EF, because at the point where it goes wrong I've checked and made sure that the added category is indeed valid: it is a category already known in the sytem and has its primary key (ID) value set to 1 (which is the first added category in the system).
I've tried many ways to fix this but cannot get this to work. Any help would be greatly appreciated!
Thank you all very much in advance!

Related

Entity Framework RIA Services How do I include entities within an included entity

I have the following hierarchy:
AccountCatagory
AccountType
AccoungGroup
GeneralLedger
SubsidiaryLedger
so each AccountCatagory has AccountTypes, each AccountType has AccountGroups...
Which needs to be loaded in to a tree view:
I need to load everything when the account category is loaded and I wrote it like this:
public IQueryable<AccountCatagory> GetAccountCatagories()
{
return this.ObjectContext.AccountCatagories.Include("AccountTypes");
}
Which works fine but only loads AccountTypes. within each AccountCatagory.
Writing an Include on each of the entities GetQuery doesn’t work.
How do I tell RIA services to Include entity when the Included entity also has an include/entity to load?
Here is an example of the way I got mine to work, assuming that all of your foreign keys are set up properly and the proper changes have been made in the metadata file of your Domain Service:
return ObjectContext.Users.Include("AccessRole")
.Include("AccessRole.AccessRoleReports")
I have never tried including objects with so many subtypes, but I assume this may work:
return this.ObjectContext.AccountCatagories.Include("AccountTypes")
.Include("AccountTypes.AccountGroups")
.Include("AccountTypes.AccountGroups.GeneralLedger")
.Include("AccountTypes.AccountGroups.GeneralLedger.SubsidiaryLedger");

Problem with EF STE and Self-Referencing tables

This is my first post here, so I hope everything is fine.
Here is my problem:
I have a table in my database called UserTypes. It has:
ID;
IsPrivate;
Parent_ID;
The relevant ones are the first and the third one.
I have another table called UserTypes_T which has information for the different types, that is language specific. The fields are:
Language_ID;
UserType_ID;
Name;
What I'm trying to achieve is load the entire hierarchy from the UserTypes table and show it in a TreeView (this is not relevant for now). Then, by selecting some of the user types I can edit them in separate edit box (the name) and a combo box (the parent).
Everything works fine until I try to persist the changes in the database. EF has generated for me two entity classes for those tables:
The class for the user types has:
ID;
IsPrivate;
Parent_ID;
A navigational property for the self-reference (0..1);
A navigational property for the child elements;
Another navigational property for the UserTypes_T table (1..*);
The class for the translated information has:
UserType_ID;
Language_ID;
Name;
A navigational property to the UserTypes table (*..1);
A navigational property to the Languages table (*..1);
I get the data I need using:
return context.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
in my WCF Web service. I can add new user types with no problems, but when I try to update the old ones, some strange things happen.
If I update a root element (Parent_ID==null) everything works!
If I update an element where Parent_ID!=null I get the following error:
AcceptChanges cannot continue because the object’s key values conflict with another object in the ObjectStateManager.
I searched all over the internet and read the blog post from Diego B Vega (and many more) but my problem is different. When I change a parent user type, I actually change the Parent_ID property, not the navigational property. I always try to work with the IDs, not the generated navigational properties in order to avoid problems.
I did a little research, tried to see what is the object graph that I get and saw that there were lots of duplicate entities:
The root element had a list of its child elements. Each child element had a back reference to the root or to its parent and so on. You can imagine. As I wasn't using those navigational properties, because I used the IDs to get/set the data I needed, I deleted them from the model. To be specific I deleted points 4 and 5 from the UserTypes entity class. Then I had an object graph with each element only once. I tried a new update but I had the same problem:
The root element was updated fine, but the elements, that had some parents, threw the same exception.
I saw that I had a navigational property in the UserTypes_T entity class, pointing to a user type, so I deleted it too. Then this error disappeared. All the items in the object graph were unique. But the problem remained - I could update my root element with no problems, but when trying to update the children (with no exclusions) I got a null reference exception in the generated Model.Context.Extensions class:
if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))
{
context.AddObject(entityInSet.Item1, entityInSet.Item2);//here!
}
I tried to update only the name (which is in UserTypes_T) but the error is the same.
I'm out of ideas and I've been trying to solve this problem for 8 hours now, so I'll appreciate if someone gives me ideas or share their experience.
PS:
The only way I succeeded updating a child object was using the following code to retrieve the data:
var userTypes = argoContext.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
foreach (UserType ut in userTypes)
{
ut.UserType1 = null;
ut.UserTypes1 = null;
}
return userTypes;
where UserType1 is the navigational property, pointing to the parent user type and UserTypes1 is the navigational property, holding a list of the child element. The problem here was that EF "fixups" the objects and changes the Parent_ID to null. If I set it back again, EF sets the UserTypes1, too... Maybe there is a way to stop this behavior?
OK everybody, I just found what the problem was and I'm posting the answer if anybody else encounters the same issue.
The problem was that I was making some validation on the server in order to see if there isn't a circular reference between the user types. So, my method on the server looked something like:
using (MyEntities context = new MyEntities())
{
string errMsg = MyValidator.ValidateSomething(context.UserTypes,...);
if (!string.IsNullOrEmpty(errMsg)) throw new FaultException(errMsg);
//some other code here...
context.UserTypes.ApplyChanges(_userType);//_userType is the one that is updated
context.UserTypes.SaveChanges();
}
The problem is that when making the validation, the context is filled and when trying to save the changes, there are objects with the same key values.
The solution is simple - to use different context for validating things on the server:
using (MyEntities validationContext = new MyEntities())
{
//validation goes here...
}
using (MyEntities context = new MyEntities())
{
//saving changes and other processing...
}
Another one can be:
using (MyEntities context = new MyEntities())
{
using (MyEntities validationContext = new MyEntities())
{
//validation
}
//saving changes and other processing...
}
That's it! I hope it can be useful to somebody!

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

I have following code to add or update the Entity object. finding the object by primary key, based on the response I am adding or updating the object.
Adding record works, but during update its giving this error message "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
In my MSSQL database I have only one record.
var v = db.Envelopes.Find(model.ReportDate, model.Service);
if (v == null)
{
db.Envelopes.Add(model);
db.SaveChanges();
ViewBag.status = "Record Add successfully";
ModelState.Clear();
}
else
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
}
How can I fix this error message?
As mentioned by #anon you can't attach model once you loaded the entity with the same key. The changes must be applied to attached entity. Instead of this:
db.Entry(model).State = EntityState.Modified;
use this:
db.Entry(v).CurrentValues.SetValues(model);
If an earlier query read the entity to be updated and that's why you're getting the error, you can change that query to AsNoTracking. See the AsNoTracking example in:
http://www.asp.net/entity-framework/tutorials/advanced-entity-framework-scenarios-for-an-mvc-web-application
I assume you are saying that your error occurs here:
db.Entry(model).State = EntityState.Modified;
Once you execute Find(), your Envelope is already being tracked by your context. This means that if you need to change a property, just change it on v, and then call SaveChanges(). Don't worry about setting the state to Modified.
If you set your context to AsNoTracking() this will stop aspmvc tracking the changes to the entity in memory (which is what you want anyway on the web). Don't forget the using statement.
using System.Data.Entity;
db.Envelopes.AsNoTracking().Find(model.ReportDate, model.Service);
I got this from this forum post ->
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application
I'm using this because I have already created a new instance, and populated the properties I need to update.
var key=this.CreateEntityKey("Envelopes",model);
ObjectStateEntry ose;
if(this.ObjectStateManager.TryGetObjectStateEntry(key, out ose)){
var entity=(Page)ose.Entity;
Envelopes.Detach(entity);
}
this.Envelopes.Attach(model);
And another approach to solve the issue is detaching tracked entity and re-attaching the modified one. See my solution here.

EF 4 Self Tracking Entities does not work as expected

I am using EF4 Self Tracking Entities (VS2010 Beta 2 CTP 2 plus new T4 generator). But when I try to update entity information it does not update to database as expected.
I setup 2 service calls. one for GetResource(int id) which return a resource object. the second call is SaveResource(Resource res); here is the code.
public Resource GetResource(int id)
{
using (var dc = new MyEntities())
{
return dc.Resources.Where(d => d.ResourceId == id).SingleOrDefault();
}
}
public void SaveResource(Resource res)
{
using (var dc = new MyEntities())
{
dc.Resources.ApplyChanges(res);
dc.SaveChanges();
// Nothing save to database.
}
}
//Windows Console Client Calls
var res = service.GetResource(1);
res.Description = "New Change"; // Not updating...
service.SaveResource(res);
// does not change anything.
It seems to me that ChangeTracker.State is always show as "Unchanged".
anything wrong in this code?
This is probably a long shot... but:
I assume your Service is actually in another Tier? If you are testing in the same tier you will have problems.
Self Tracking Entities (STEs) don't record changes until when they are connected to an ObjectContext, the idea is that if they are connected to a ObjectContext it can record changes for them and there is no point doing the same work twice.
STEs start tracking once they are deserialized on the client using WCF, i.e. once they are materialized to a tier without an ObjectContext.
If you look through the generated code you should be able to see how to turn tracking on manually too.
Hope this helps
Alex
You have to share assembly with STEs between client and service - that is the main point. Then when adding service reference make sure that "Reuse types in referenced assemblies" is checked.
The reason for this is that STEs contain logic which cannot be transfered by "Add service reference", so you have to share these types to have tracing logic on client as well.
After reading the following tip from Daniel Simmons, the STE starts tracking. Here is the link for the full article. http://msdn.microsoft.com/en-us/magazine/ee335715.aspx
Make certain to reuse the Self-Tracking Entity template’s generated entity code on your client. If you use proxy code generated by Add Service Reference in Visual Studio or some other tool, things look right for the most part, but you will discover that the entities don’t actually keep track of their changes on the client.
so in the client make sure you don't use add service reference to get the proxy instead access service through following code.
var svc = new ChannelFactory<IMyService>("BasicHttpBinding_IMyService").CreateChannel();
var res = svc.GetResource(1);
If you are using STEs without WCF you may have to call StartTracking() manually.
I had the same exact problem and found the solution.
It appears that for the self-tracking entities to automatically start tracking, you need to reference your STE project before adding the service reference.
This way Visual Studio generates some .datasource files which does the final trick.
I found the solution here:
http://blogs.u2u.be/diederik/post/2010/05/18/Self-Tracking-Entities-with-Validation-and-Tracking-State-Change-Notification.aspx
As for starting the tracking manually, it seems that you do not have these methods on the client-side.
Hope it helps...

Using Reflection to Remove Entity from RIA Services EntityCollection?

To facilitate control reuse we created a solution with three separate projects: a control library, Silverlight client, and ASP.NET backend. The control library has no reference to the RIA Services-generated data model classes so when it needs to interact with it, we use reflection.
This has worked fine so far but I've hit a bump. I have a DataGrid control where the user can select a row, press the 'delete' button, and it should remove the entity from the collection. In the DataGrid class I have the following method:
private void RemoveEntity(Entity entity)
{
// Use reflection to remove the item from the collection
Type sourceType = typeof(System.Windows.Ria.EntityCollection<>);
Type genericType = sourceType.MakeGenericType(entity.GetType());
System.Reflection.MethodInfo removeMethod = genericType.GetMethod("Remove");
removeMethod.Invoke(this._dataGrid.ItemsSource, new object[] { entity });
// Equivalent to: ('Foo' derives from Entity)
// EntityCollection<Foo> ec;
// ec.Remove(entity);
}
This works on the client side but on the domain service the following error gets generated during the Submit() method:
"The UPDATE statement conflicted with
the FOREIGN KEY constraint
"********". The conflict occurred in
database "********", table "********",
column '********'. The statement has
been terminated."
One thing I noticed is the UpdateFoo() service method is being called instead of the DeleteFoo() method on the domain service. Further inspection shows the entity is going into the ModifiedEntities ChangeSet instead of the RemovedEntities ChangeSet. I don't know if that's the problem but it doesn't seem right.
Any help would be appreciated, thanks,
UPDATE
I've determined that the problem is definitely coming from the reflection call to the EntityCollection.Remove() method. For some reason calling it causes the entity's EntityState property to change to EntityState.Modified instead of EntityState.Deleted as it should.
Even if I try to remove from the collection by completely circumventing the DataGrid I get the exact same issue:
Entity selectedEntity = this.DataContext.GetType().GetProperty("SelectedEntity").GetValue(this.DataContext, null) as Entity;
object foo = selectedEntity.GetType().GetProperty("Foo").GetValue(selectedEntity, null);
foo.GetType().InvokeMember("Remove", BindingFlags.InvokeMethod, null, foo, new object[] { entity });
As a test, I tried modifying the UpdateFoo() domain service method to implement a delete and it worked successfully to delete the entity. This indicates that the RIA service call is working correctly, it's just calling the wrong method (Update instead of Delete.)
public void UpdateFoo(Foo currentFoo)
{
// Original update implementation
//if ((currentFoo.EntityState == EntityState.Detached))
// this.ObjectContext.AttachAsModified(currentFoo, this.ChangeSet.GetOriginal(currentFoo));
// Delete implementation substituted in
Foo foo = this.ChangeSet.GetOriginal(currentFoo);
if ((foo.EntityState == EntityState.Detached))
this.ObjectContext.Attach(foo);
this.ObjectContext.DeleteObject(foo);
}
I've been researching a similar issue.
I believe the issue is you are calling remove with a reference for an EntityCollections within the DomainContext as the root reference rather than using the DomainContext itself as the root.
So...
ParentEntityCollection.EntityCollectionForTEntity.Remove(TEntity);
Produces the EntityState.Modified instead of EntityState.Deleted
Try instead...
DomainContext.EntityCollectionForTEntity.Remove(TEntity);
I think this will produce the result you are seeking.
Hope this helps.
What is the "column" in the "FOREIGN KEY constraint" error? Is this a field in the grid row and collection that coorosponds to that column? Is it possible that the entity you are trying to remove is a column in the row rather than the row itself which is causing an update to the row (to null the column) rather than to delete the row?
I read your update and looks like you've determined that the problem is the reflection.
Have you tried to take the reflection out of the picture?
As in:
private void RemoveEntity(Entity entity)
{
// Use reflection to remove the item from the collection
Type sourceType = typeof(System.Windows.Ria.EntityCollection<>);
Type genericType = sourceType.MakeGenericType(entity.GetType());
// Make sure we have the right type
// and let the framework take care of the proper invoke routine
if (genericType.IsAssignableFrom(this._dataGrid.ItemsSource.GetType()))
((Object) this._dataGrid.ItemsSource).Remove(entity);
}
Yes, I know it's ugly, but some times...
Edited to add
I've updated the code to remove the is keyword.
Now about using the object to make the call to the Remove method, I believe it might work due the late binding of it.