Many to Many Relationships not saving - entity-framework

I have two entities with a fairly standard Many to Many relationship that I created in EF 5 Code First. These are Service and ServiceItem. The Service entity contains a collection of ServiceItems and the ServiceItem contains a collection of Services. I can create, change and save data to either of the entities basic properties with no problems. When I try to add a ServiceItem to a Service or a Service to a ServiceItem it seems to work, but nothing is saved. I have verified that all the proper database tables are created, including a ServiceItemService table with the cross keys. The database ServiceItemService table doesn't get any entry when I add the items. There is no error and everything else seems to work perfectly.
I am a bit stumped and could use some help. Below are the classes.
The Service class;
public class Service
{
//Default constructor
public Service()
{
//Defaults
IsActive = true;
ServicePeriod = ServicePeriodType.Monthly;
ServicePeriodDays = 0;
ServiceItems = new Collection<ServiceItem>();
}
public int ServiceID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<ServiceItem> ServiceItems { get; set; }
public string TermsOfService { get; set; }
public ServicePeriodType ServicePeriod { get; set; }
public int ServicePeriodDays { get; set; }
public bool IsActive { get; set; }
}
The ServiceItem class;
public class ServiceItem
{
public ServiceItem()
{
IsActive = true;
}
public int ServiceItemID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<Service> Services { get; set; }
public string UserRole { get; set; }
public bool IsActive { get; set; }
}
This is the Fluent mapping I did while trying to debug this issue. The same problem happened before and after adding this mapping.
public DbSet<Service> Services { get; set; }
public DbSet<ServiceItem> ServiceItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>()
.HasMany(p => p.ServiceItems)
.WithMany(r => r.Services)
.Map(mc =>
{
mc.MapLeftKey("ServiceItemID");
mc.MapRightKey("ServiceID");
mc.ToTable("ServiceItemService");
});
}
Here is the code I use to save the Service item that includes 2-3 ServiceItems in the Service.ServiceItems collection. I have carefully verified that the ServiceItems were in the proper collection.
db.Entry(dbService).State = EntityState.Modified;
db.SaveChanges();
The dbService object doesn't seem to get affected in any way. The ServiceItems are still in the proper collection, but no update are made to the ServiceItemService database table. Any advice would be very welcome.
-Thanks

It is expected that nothing happens.
What you want to change or add is a relationship between the entities Service and ServiceItem. But you cannot manipulate relationships by setting the state of an entity to Modified. This only updates scalar and complex properties but no navigation properties (= relationships). (For example setting the state of a Service entity to Modified will mark Service.Title and Service.Description, etc. as modified and ensure that those properties are saved to the database. But it doesn't care about the content of Service.ServiceItems.)
The only exception where you can change a relationship by setting the state to Modified are Foreign Key Associations. These are associations that have foreign key properties exposed in your model entity and they can only occur for one-to-many or one-to-one associations. Many-to-many relationships are always Independent Associations which means they can never have a foreign key property in an entity. (Because the FKs are in the join table, but the join table is not an entity and "hidden" from your model classes.)
There is a way to directly manipulate relationships for a many-to-many association but it requires to go down to the ObjectContext and its RelationshipManager which is - in my opinion - pretty advanced and tricky.
The usual and straight-forward way to add and remove relationship entries to/from a many-to-many association is by just adding items to and removing items from the collections while the entities are attached to the context. EF's change tracking mechanism will recognize the changes you have done and generate the appropriate INSERT, UPDATE and DELETE statements when you call SaveChanges.
The exact procedure depends on if you also want to save Service and/or ServiceItem as new entities or if you only want to add relationships between existing entities. Here are a few examples:
service should be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:
using (var context = new MyContext())
{
var service = new Service();
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.Services.Add(service);
context.SaveChanges();
}
Adding the "root" service of the object graph is enough because EF will recognize that all other entities in the graph are not attached to the context and assume that they have to be INSERTed into the database.
service already exists and should NOT be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
EF recognizes here (when SaveChanges is called) that service is attached but the other entities are not. No INSERT for service happens but the serviceItem1/2 will be INSERTed together with the relationship entries.
service already exists and should NOT be INSERTed, all serviceItems already exist and should NOT be INSERTed, but the relationships between the entities should be INSERTed into the join table:
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem { ServiceItemID = 23 };
context.ServiceItems.Attach(serviceItem1);
var serviceItem2 = new ServiceItem { ServiceItemID = 37 };
context.ServiceItems.Attach(serviceItem2);
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
For completeness: How to remove relationships between existing entities?
using (var context = new MyContext())
{
var service = context.Services
.Include(s => s.ServiceItems) // load the existing Items
.Single(s => s.ServiceID == 15);
var serviceItem1 = service.ServiceItems
.Single(s => s.ServiceItemID == 23); // query in memory, no DB query
var serviceItem2 = service.ServiceItems
.Single(s => s.ServiceItemID == 37); // query in memory, no DB query
service.ServiceItems.Remove(serviceItem1);
service.ServiceItems.Remove(serviceItem2);
context.SaveChanges();
}
The two relationship rows in the join table that link service 15 with serviceItem 23 and 37 will be deleted.
Alternativly instead of calling Attach you can load the existing entities from the database. It will work as well:
var service = context.Services.Single(s => s.ServiceID == 15);
And the same for existing ServiceItems.

Related

EF6:How to include subproperty with Select so that single instance is created. Avoid "same primary key" error

I'm trying to fetch (in disconnected way) an entity with its all related entities and then trying to update the entity. But I'm getting the following error:
Attaching an entity of type 'Feature' failed because another entity of the same type already has the same primary key value.
public class Person
{
public int PersonId { get; set; }
public string Personname { get; set }
public ICollection Addresses { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public Person Person { get; set; }
public ICollection<Feature> Features { get; set; }
}
// Many to Many: Represented in database as AddressFeature (e.g Air Conditioning, Central Heating; User could select multiple features of a single address)
public class Feature
{
public int FeatureId { get; set; }
public string Featurename { get; set; }
public ICollection<Address> Addresses { get; set; } // Many-To-Many with Addresses
}
public Person GetCandidate(int id)
{
using (MyDbContext dbContext = new MyDbContext())
{
var person = dbContext.People.AsNoTracking().Where(x => x.PersonId == id);
person = person.Include(prop => prop.Addresses.Select(x => x.Country)).Include(prop => prop.Addresses.Select(x => x.Features));
return person.FirstOrDefault();
}
}
public void UpdateCandidate(Person newPerson)
{
Person existingPerson = GetPerson(person.Id); // Loading the existing candidate from database with ASNOTRACKING
dbContext.People.Attach(existingPerson); // This line is giving error
.....
.....
.....
}
Error:
Additional information: Attaching an entity of type 'Feature' failed because another entity of the same type already has the same primary key value.
It seems like (I may be wrong) GetCandidate is assigning every Feature within Person.Addresses a new instance. So, how could I modify the GetCandidate to make sure that the same instance (for same values) is bing assisgned to Person.Addresses --> Features.
Kindly suggest.
It seems like (I may be wrong) GetCandidate is assigning every Feature within Person.Addresses a new instance. So, how could I modify the GetCandidate to make sure that the same instance (for same values) is bing assisgned to Person.Addresses --> Features.
Since you are using a short lived DbContext for retrieving the data, all you need is to remove AsNoTracking(), thus allowing EF to use the context cache and consolidate the Feature entities. EF tracking serves different purposes. One is to allow consolidating the entity instances with the same PK which you are interested in this case, and the second is to detect the modifications in case you modify the entities and call SaveChanges(), which apparently you are not interested when using the context simply to retrieve the data. When you disable the tracking for a query, EF cannot use the cache, thus generates separate object instances.
What you really not want is to let EF create proxies which hold reference to the context used to obtain them and will cause issues when trying to attach to another context. I don't see virtual navigation properties in your models, so most likely EF will not create proxies, but in order to be absolutely sure, I would turn ProxyCreationEnabled off:
public Person GetCandidate(int id)
{
using (MyDbContext dbContext = new MyDbContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var person = dbContext.People.Where(x => x.PersonId == id);
person = person.Include(prop => prop.Addresses.Select(x => x.Country)).Include(prop => prop.Addresses.Select(x => x.Features));
return person.FirstOrDefault();
}
}

EF Model with collection and navigation to a particular element

How to configure a EF6 migration with a model class having?
A collection o items
A navigation property to one particular item
public class MyModel
{
[Key]
public int Id { get; set; }
// My collection of elements
public virtual ICollection<MyCollectionElement> MyCollection { get; set; }
// Optional navigation to a particular element from the collection
[ForeignKey("CurrentElement")]
public int? CurrentElementId { get; set; }
public virtual MyCollectionElement CurrentElement { get; set; }
}
public class MyCollectionElement
{
[Key]
public int Id { get; set; }
// Required navigation to MyClass
[ForeignKey("MyModel")]
public int MyModelID { get; set; }
public virtual MyModel Model { get; set; }
}
Configuration
modelBuilder.Entity<MyModel>()
.HasMany(x => x.MyCollection)
.WithRequired(x => x.Model)
.HasForeignKey(x => x.MyModelID)
.WillCascadadeOnDelete(false);
Throws several errors on Update-Database, like
Unable to determine a valid ordering for dependent operations.
I would like a solution which doesn't involve a boolean IsCurrent in MyCollectionElement to make another query later and find which element is the current; instead, I would like to store the current element's id with my model, like exposed.
Also, I don't mind making int CurrentElementId non nullable (required) if it's easier.
Thanks.
This chicken-and-egg problem always looms when there are circular relationships. The error...
Unable to determine a valid ordering for dependent operations.
...is not thrown when the database is created. The database can be created just fine. It occurs when you try to insert a MyModel record and a MyCollectionElement referring to one another in the same unit of work. In the Seed method you probably have something like
var element = new MyCollectionElement();
var model = new MyModel();
model.MyCollection.Add(element);
model.CurrentElement = element;
The statement model.MyCollection.Add(element); requires model to be inserted first, so element can refer to it in its foreign key. But model.CurrentElement = element; requires element to be inserted first.
You can only avoid this situation by calling SaveChanges twice, and wrapping everything in a TransactionScope if you want the assignments to be transactional:
using(var ts = new TransactionScope())
{
using(var db = new MyContext()
{
var element = new MyCollectionElement();
var model = new MyModel();
model.MyCollection.Add(element);
db.MyModels.Add(model);
db.SaveChanges();
model.CurrentElement = element;
db.SaveChanges();
}
ts.Complete();
}
This also means that int CurrentElementId should remain nullable.

EF many-to-many relationship and data duplication

I have a trouble with EF (6.1.3)
I have created next classes (with many-to-many relationship):
public class Record
{
[Key]
public int RecordId { get; set; }
[Required]
public string Text { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
[Key]
public int TagId { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Record> Records{ get; set; }
}
And method:
void AddTags()
{
Record[] records;
Tag[] tags;
using (var context = new AppDbContext())
{
records = context.Records.ToArray();
}//remove line to fix
tags = Enumerable.Range(0, 5).Select(x => new Tag()
{
Name = string.Format("Tag_{0}", x),
Records= records.Skip(x * 5).Take(5).ToArray()
}).ToArray();
using (var context = new AppDbContext()){ //remove line to fix
context.Tags.AddRange(tags);
context.SaveChanges();
}
}
If I use two contexts, the records (which were added to created tags) will be duplicated. If I remove marked rows - problem disappears.
Is there any way to fix this problem without using the same context?
If you can, better reload entities or not detach them at all. Using multiple context instances in application is overall making things much more complicated.
The problem for you comes from the Entity Framework entity change tracker. When you load entitites from your DbContext and dispose that context, entities get detached from entity change tracker, and Entity Framework has no knowledge of any changes made to it.
After you reference detached entity by an attached entity, it (detached entity) immediately gets into entity change tracker, and it has no idea that this entity was loaded before. To give Entity Framework an idea that this detached entity comes from the database, you have to reattach it:
foreach (var record in records) {
dbContext.Entry(record).State = EntityState.Unchanged;
}
This way you will be able to use records to reference in other objects, but if you have any changes made to these records, then all these changes will go away. To make changes apply to database you have to change state to Added:
dbContext.Entry(record).State = EntityState.Modified;
Entity Framework uses your mappings to determine row in database to apply changes to, specifically using your Primary Key settings.
A couple examples:
public class Bird
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
public class Tree
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class BirdOnATree
{
[Column(Order = 0), Key, ForeignKey("Bird")]
public int BirdId { get; set; }
public Bird Bird { get; set; }
[Column(Order = 1), Key, ForeignKey("Tree")]
public int TreeId { get; set; }
public Tree Tree { get; set; }
public DateTime SittingSessionStartedAt { get; set; }
}
Here's a small entity structure so that you could see how it works. You can see that Bird and Tree have simple Key - Id. BirdOnATree is a many-to-many table for Bird-Tree pair with additional column SittingSessionStartedAt.
Here's the code for multiple contexts:
Bird bird;
using (var context = new TestDbContext())
{
bird = context.Birds.First();
}
using (var context = new TestDbContext())
{
var tree = context.Trees.First();
var newBirdOnAtree = context.BirdsOnTrees.Create();
newBirdOnAtree.Bird = bird;
newBirdOnAtree.Tree = tree;
newBirdOnAtree.SittingSessionStartedAt = DateTime.UtcNow;
context.BirdsOnTrees.Add(newBirdOnAtree);
context.SaveChanges();
}
In this case, bird was detached from the DB and not attached again. Entity Framework will account this entity as a new entity, which never existed in DB, even though Id property is set to point to existing row to database. To change this you just add this line to second DbContext right in the beginning:
context.Entry(bird).State = EntityState.Unchanged;
If this code is executed, it will not create new Bird entity in DB, but use existing instead.
Second example: instead of getting bird from the database, we create it by ourselves:
bird = new Bird
{
Id = 1,
Name = "Nightingale",
Color = "Gray"
}; // these data are different in DB
When executed, this code will also not create another bird entity, will make a reference to bird with Id = 1 in BirdOnATree table, and will not update bird entity with Id = 1. In fact you can put any data here, just use correct Id.
If we change our code here to make this detached entity update existing row in DB:
context.Entry(bird).State = EntityState.Modified;
This way, correct data will be inserted to table BirdOnATree, but also row with Id = 1 will be updated in table Bird to fit the data you provided in the application.
You can check this article about object state tracking:
https://msdn.microsoft.com/en-US/library/dd456848(v=vs.100).aspx
Overall, if you can avoid this, don't use object state tracking and related code. It might come to unwanted changes that are hard to find source for - fields are updated for entity when you don't expect them to, or are not updated when you expect it.

EF Code First Detached Entity not updating object reference

I'm posting the exact entity:
public class Person : ContactableEntity
{
public Plan Plan { get; set; }
public int Record { get; set; }
public int PersonTypeValue { get; set; }
}
I'm using the following code to update in a disconected context fashion:
public void Update(DbSet MySet, object Obj)
{
MySet.Attach(Obj);
var Entry = this.Entry(Obj);
Entry.State = EntityState.Modified;
this.SaveChanges();
}
This is a method exposed by my dbContext
Called this way:
PersistentManager.Update(PersistentManager.Personas,UpdatedPersona);
The problem is, EF will update any property but the referenced Plan object.
Can someone tell me where is the mistake?
In advance : the entity reaches the point of update with all the properties correctly set.
EF just fails to update the FK in the Database (no exception though)
Update:
tried solving the problem like this but it didn't work:
PersistentMgr.Contacts.Attach(Obj);
PersistentMgr.Entry(Obj).State = EntityState.Modified;
PersistentMgr.Entry(Obj.Plan).State = EntityState.Modified;
PersistentMgr.SaveChanges();
You need...
this.Entry(person).State = EntityState.Modified;
this.Entry(person.Plan).State = EntityState.Modified;
...because when you set the state of the person to Modified the person gets attached to the context in state Modified but related entities like person.Plan are attached in state Unchanged.
If the relationship between Person and Plan has been changed while the entities were detached it is more difficult (especially, like in your model, when no foreign key is exposed as property ("independent association")) to update the entities correctly. You basically need to load the original object graph from the database, compare it with detached graph if relationships have been changed and merge the changes into the loaded graph. An example is here (see the second code snippet in that answer).
Edit
Example to show that it works (with EF 5.0):
using System.Data;
using System.Data.Entity;
using System.Linq;
namespace EFModifyTest
{
public class Person
{
public int Id { get; set; }
public Plan Plan { get; set; }
public int Record { get; set; }
public int PersonTypeValue { get; set; }
}
public class Plan
{
public int Id { get; set; }
public string SomeText { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Person> Contacts { get; set; }
public DbSet<Plan> Plans { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
// Create a person with plan
using (var ctx = new MyContext())
{
ctx.Database.Initialize(true);
var plan = new Plan { SomeText = "Old Text" };
var person = new Person { Plan = plan, Record = 1, PersonTypeValue = 11 };
ctx.Contacts.Add(person);
ctx.SaveChanges();
}
// see screenshot 1 from SQL Server Management Studio
Person detachedPerson = null;
// Load the person with plan
using (var ctx = new MyContext())
{
detachedPerson = ctx.Contacts.Include(c => c.Plan).First();
}
// Modify person and plan while they are detached
detachedPerson.Record = 2;
detachedPerson.PersonTypeValue = 12;
detachedPerson.Plan.SomeText = "New Text";
// Attach person and plan to new context and set their states to Modified
using (var ctx = new MyContext())
{
ctx.Entry(detachedPerson).State = EntityState.Modified;
ctx.Entry(detachedPerson.Plan).State = EntityState.Modified;
ctx.SaveChanges();
}
// see screenshot 2 from SQL Server Management Studio
}
}
}
Screenshot 1 from SQL Server Management Studio (before the modification, Person table is left, Plan table is right):
Screenshot 2 from SQL Server Management Studio (after the modification, Person table is left, Plan table is right):
If it doesn't work for you there must be an important difference to my test model and code. I don't know which one, you must provide more details.
Edit 2
If you change the relationship from Person to another (existing) Plan you must load the original and then update the relationship. With independent associations (no FK property in model) you can update relationships only by using change tracking (aside from more advanced modifications of relationship entries in the ObjectContext change tracker):
var originalPerson = this.Contacts.Include(c => c.Plan)
.Single(c => c.Id == person.Id);
this.Plans.Attach(person.Plan);
this.Entry(originalPerson).CurrentValues.SetValues(person);
originalPerson.Plan = person.Plan;
this.SaveChanges();

Entity framework, referential integrity constraint violation occurred error on updating entity from disconnected area

I have below model
public class Order
{
[Key]
public virtual string OrderNo {get;set;}
public virtual IList<OrderItem> Items {get;set;}
}
public class OrderItem
{
[Key]
public virtual string ItemNo {get; set;}
public virtual string ParentItemNo {get;set;}
public virtual string OrderNo {get;set;}
public virtual OrderItem ParentItem {get;set;}
public virtual IList<OrderItem> ChildItems {get;set;}
public virtual IList<ItemProperty> ItemProperties {get;set;}
public virtual Order Order {get;set;}
}
public class ItemProperty
{
[Key]
public virtual string PropertyNo {get; set;}
public virtual string ParentPropertyNo {get;set;}
public virtual string OrderItemNo {get;set;}
public virtual ItemProperty ParentProperty {get;set;}
public virtual IList<ItemProperty> ChildProperties {get;set;}
public virtual OrderItem OrderItem {get;set;}
}
I'm running on disconnected area (with service our disconnected from the Entity Framework context)
I create a order and save to database
client:
service.CreateOrder(new Order() { OrderN="fksjdf1" });
server:
using(EfDbContext context = new EfDbContext())
{
context.Orders.Add(order);
context.SaveChanges();
}
I need to add one or more OrderItems to previously added order
client:
var order = service.GetOrder("fksjdf1");
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty()
PropertyNo="p1",
OrderItem = item1
})
order.Items.Add(item1);
var item2 = new OrderItem();
item2.ItemNo="i2";
item2.Order=order;
item2.OrderNo=order.OrderNo;
item2.ItemProperties.Add(new ItemProperty()
PropertyNo="p2",
OrderItem = item2
});
item2.ItemProperties.Add(new ItemProperty()
PropertyNo="p3",
OrderItem = item2
})
order.Items.Add(item2);
service.UpdateOrder(order);
server:
using(EfDbContext context = new EfDbContext())
{
DbEntityEntry dbEntityEntry = context.Entry(order);
if (dbEntityEntry.State == EntityState.Detached)
{
// ERROR
context.Set<Order>().Attach(order);
}
dbEntityEntry.State = EntityState.Modified;
context.SaveChanges();
}
Error: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
Why I see this error? Why I not update this entity?
How can i use entity framework from disconnected area?
EDIT 1:
public Order GetOrder(string orderNo)
{
using (EfDbContext context = new EfDbContext())
{
context.Configuration.ProxyCreationEnabled = false;
var order = context.Orders
.Include(o => o.OrderItems
.Select(z => z.ItemProperties
.Select(y => y.ChildProperties)))
.Where(o => o.OrderNo == orderNo)
.FirstOrDefault();
}
}
You probably have this error because you don't set OrderItemNo in ItemProperty, like so:
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty {
PropertyNo="p1",
OrderItem = item1,
OrderItemNo = item1.ItemNo // = "i1"
});
order.Items.Add(item1);
// and the same for item2
When the order gets attached to the context, related order items and item properties get attached as well. The navigation property OrderItem of ItemProperty refers to an entity with a key you supplied ("i1"), but the foreign key OrderItemNo doesn't have this key value. That's the inconsistency the exception is complaining about.
Edit
Unfortunately it is not that easy to update a detached object graph in the database. Setting the state of an entity to Modified only marks this entity as Modified and no related entity.
Your GetOrder method makes things complicated because you are eagerly loading already existing items and more related stuff to the client, then add new OrderItems on client side and send this new modified object graph back to the server. On server side you now have the problem that you must handle the already existing items differently (don't insert them into the database) than the new items (insert them into the database). To do this, you need to detect which items are old and which are new. If you don't transport some flag in the entities themselves that would indicate if the entity is new or not, you must query the database again and compare the object graph loaded from the DB with the detached object graph sent from the client. A general sketch how it can be done is here: https://stackoverflow.com/a/5540956/270591 (That's only for a parent with child collection. If grandchild collections are involved - like in your model - it is getting more complex.)
In my opinion you can simplify the whole procedure if you would not only have this global service.UpdateOrder(order) method that tries to handle all possible changes in the object graph but also some specialized methods that leverage the knowledge you have about the change of the graph - in this case a specialized method for adding new OrderItems to an existing order. It could look like this:
var order = service.GetOrder("fksjdf1");
var newOrderItems = new List<OrderItem>();
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.OrderNo=order.OrderNo; // leave the Order property null
item1.ItemProperties.Add(new ItemProperty { PropertyNo="p1" });
newOrderItems.Add(item1);
// the same for item2
newOrderItems.Add(item2);
service.AddNewOrderItems(newOrderItems);
And the service method would be:
public void AddNewOrderItems(List<OrderItem> newOrderItems)
{
using(EfDbContext context = new EfDbContext())
{
foreach (var newOrderItem in newOrderItems)
context.Set<OrderItem>().Add(newOrderItem);
context.SaveChanges();
}
}