I have two entities, Parent and Child, at the client side i create Parent and then call context.submitChanges
At the server side in the InsertParent(Parent parent) i do:
InsertParent(Parent parent)
{
Child child = this.ObjectContext.Childs.CreateObject();
parent.child = child;
if ((parent.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(parent, EntityState.Added);
}
else
{
this.ObjectContext.Parents.AddObject(parent);
}
}
Now i'm having two problems.
Before the if else, Parent.id is 0 and after its still 0 but in the database its populated.
The other one is, Child gets saved but Child.ParentId is 0.
I'm not understanding why.
Whats the correct way to achieve this behaviour? should i call SaveChanges() on the context directly?
Check to make sure the StoreGeneratedPattern property on Parent.Id in your edmx is set to Identity. That should make sure it gets updated with the new value on inserts.
I'd also wrap this in a transaction so you can add your child after the parent id is set.
using(var scope = new TransactionScope()){
ObjectContext.Parents.AddObject(parent);
ObjectContext.SaveChanges(); //parent.id assigned
parent.child = ObjectContext.Child.CreateObject();
ObjectContext.SaveChanges();
scope.Complete(); // commit transaction
}
Yes you should use the SaveChanges() because that's what persist your data into the Database.
Very Easy Example
You also need to check the parents.Id column and childs Id column that they are set as identity and as primary keys, and the relations between the two tables.
Related
I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);
In my application, the interface allows the user to create and delete entities in an Entity Framework model. All changes are added to the ObjectContext, and saved only when the user selects "Save".
My question is best asked by means of the following simple example:
I have a Foreign Key relation between two entity types, which I will call Parent and Child here. The database, in SQLite, is declared as follows:
CREATE TABLE Parents (ID INTEGER PRIMARY KEY);
CREATE TABLE Children
(
ID INTEGER PRIMARY KEY,
ParentID INTEGER NOT NULL
CONSTRAINT FK_ChildrenToParents REFERENCES [Parents](ID)
ON DELETE CASCADE ON UPDATE CASCADE
)
I then build an Entity Framework model on that database, and execute the following actions against it (see code below):
Create a Parent
Save
Delete the Parent
Create a new Parent with the same ID number as the original
Create a Child and provide it with the new Parent's ID number
Save (Fails)
demoDBEntities entities = new demoDBEntities();
Parent originalParent = new Parent { ID = 1 };
entities.Parents.AddObject(originalParent);
entities.SaveChanges(); // First Save
entities.Parents.DeleteObject(originalParent);
Parent newParent = new Parent { ID = 1 };
entities.Parents.AddObject(newParent);
Child newChild = new Child { ID = 2, ParentID = 1 };
entities.Children.AddObject(newChild);
entities.SaveChanges(); // Second Save
I get the following error on the second call to SaveChanges():
"Unable to insert or update an entity because the principal end of the 'demoDBModel.FK_Children_0_0' relationship is deleted."
It seems the problem is that the Entity Framework links the new Child item to the originalParent item, which is then deleted and replaced by the newParent item. This might sound conceited, but happens quite naturally in my application.
Is there any way for me to work around this problem?
PS: I know it's bad practice to re-use ID numbers of DB entries - however, in my case the ID numbers are Asset numbers, which could theoretically be reused. That said, the above scenario typically happens if the client creates a faulty Parent, then deletes it, recreates it, and then creates the Child item.
Instead of setting the foreign key property ParentID you could try to set one of the navigation properties (you must have at least one) and use only one call to AddObject to add the complete object graph to the context:
Either:
//...
entities.Parents.DeleteObject(originalParent);
Child newChild = new Child { ID = 2 };
Parent newParent = new Parent { ID = 1, Children = new List<Child>{ newChild } };
entities.Parents.AddObject(newParent);
entities.SaveChanges();
Or:
//...
entities.Parents.DeleteObject(originalParent);
Parent newParent = new Parent { ID = 1 };
Child newChild = new Child { ID = 2, Parent = newParent };
entities.Children.AddObject(newChild);
entities.SaveChanges();
I have a one to many relationship with two tables, Parent with many Child.
I create a parent and add children to it. Then I either create it (if it's a new parent) or update it (if it exists already.) When I create it, everything works properly. However, if I update it, the children don't update.
using (var Repo = new ParentRepository(context))
{
var key = new AnnualFormKey(prnt.Year, prnt.UserId);
if (Repo.Retrieve(key) == null)
{
prnt.CreatedDate = DateTime.Now;
prnt.CreatedId = 1;
Repo.Create(prnt);
Repo.SaveChanges(); //creates parent and children
}
else
{
prnt.UpdatedDate = DateTime.Now;
prnt.UpdatedId = 2;
Repo.Update(prnt);
Repo.SaveChanges(); //updates parent but not children
}
}
(Note: Update calls _context.Entry(orginal).CurrentValues.SetValues(entity)
Is this a problem with my context or do I need to do something else?
Leave out your Repo.Update(prnt) call unless you're specifically detaching the object in Repo.Retrieve. It will already be being tracked.
Okay, I looked this up more in depth and discovered that Entity Framework doesn't actually update complex entities (as in, it won't save the children). There are lots of complicated workarounds, but mine was very simple. I just deleted the existing entity and created it again (using the updated version).
When I have a parent Entity hold a list of other entity (one to many relationship), I modify this list then call function to save the parent Entity. With the entities has removed from this list, is that the framework will delete them from database? And also the new entity has added to the list will be added to database?
thank for your help!
Assuming you have one to many relationship between Parent and Child, i. e., Parent has ChildList and Child has Parent.
Looking at the cases. If Parent is in the entity context and you add an instance of Child to ChildList of Parent, and save the context, then Child will be added to the database.
Parent parent = new Parent() { Name = "parent1" };
provider.AddToParentSet(parent)
parent.ChildList.Add(new Child() { Name = "child1" });
parent.ChildList.Add(new Child() { Name = "child2" });
parent.ChildList.Add(new Child() { Name = "child3" });
provider.SaveChanges();
If you remove one of the Child from the ChildList of Parent, and save the context, then you will get an exception because of the foreign key constraint.
Parent parent = provider.ParentSet.FirstOrDefault();
parent.ChildList.Remove(parent.ChildList.FirstOrDefault());
provider.SaveChanges();
If you delete one of the Childs that belong to ChildList of Parent from the context and save the context, it will be successful, it will be removed from database.
provider.DeleteObject(parent.ChildList.FirstOrDefault());
provider.SaveChanges();
Above situations are valid for default configuration of an entity model. Entity Framework also provides many options, you can also decide how your entity context behaves.
You just need to try these and such situations on your own. It will be better I think.
I have an entity with self reference (generated by Entity Designer):
public MyEntity: EntityObject
{
// only relevant stuff here
public int Id { get...; set...; }
public MyEntity Parent { get...; set...; }
public EntityCollection<MyEntity> Children { get...; set...; }
...
}
I've written a stored procedure that returns a subtree of nodes (not just immediate children) from the table and returns a list of MyEntity objects. I'm using a stored proc to avoid lazy loading of an arbitrary deep tree. This way I get relevant subtree nodes back from the DB in a single call.
List<MyEntity> nodes = context.GetSubtree(rootId).ToList();
All fine. But when I check nodes[0].Children, its Count equals to 0. But if I debug and check context.MyEntities.Results view, Children enumerations get populated. Checking my result reveals children under my node[0].
How can I programaticaly force my entity context to do in-memory magic and put correct references on Parent and Children properties?
UPDATE 1
I've tried calling
context.Refresh(ClientWins, nodes);
after my GetSubtree() call which does set relations properly, but fetches same nodes again from the DB. It's still just a workaround. But better than getting the whole set with context.MyEntities().ToList().
UPDATE 2
I've reliably solved this by using EF Extensions project. Check my answer below.
You need to assign one end of the relationship. First, divide the collection:
var root = nodes.Where(n => n.Id == rootId).First();
var children = nodes.Where(n => n.Id != rootId);
Now, fix up the relationship.
In your case, you'd do either:
foreach (var c in children)
{
c.Parent = root;
}
...or:
foreach (var c in children)
{
root.Children.Add(c);
}
It doesn't matter which.
Note that this marks the entities as modfied. You'll need to change that if you intend to call SaveChanges on the context and don't want this saved.
The REAL solution
Based on this article (read text under The problem), navigation properties are obviously not populated/updated when one uses stored procedures to return data.
But there's a nice manual solution to this. Use EF Extensions project and write your own entity Materilizer<EntityType> where you can correctly set navigation properties like this:
...
ParentReference = {
EntityKey = new EntityKey(
"EntityContextName.ParentEntitySetname",
new[] {
new EntityKeyMember(
"ParentEntityIdPropertyName",
reader.Field<int>("FKNameFromSP")
)
})
}
...
And that's it. Calling stored procedure will return correct data, and entity object instances will be correctly related to eachother. I advise you check EF Extensions' samples, where you will find lots of nice things.