In Entity Framework, change tracking is enabled by default. You can also disable change tracking by setting the AutoDetectChangesEnabled property of DbContext to false. If this property is set to true then the Entity Framework maintains the state of entities.
Entity Framework Core Save Changes to the database using the SaveChanges method of DbContext. When we use the SaveChanges it prepares the corresponding insert , update , delete queries. It then wraps them in a Transaction and sends them to the database. If any of the queries fails all the statements are rolled back.
EntityState.Added : EntityState.Modified; context.SaveChanges(); } } Note that when you change the state to Modified all the properties of the entity will be marked as modified and all the property values will be sent to the database when SaveChanges is called.
As far as I know, you have two options.
Null all the child objects, this will ensure EF not to add anything. It will also not delete anything from your database.
Set the child objects as detached from the context using the following code
context.Entry(yourObject).State = EntityState.Detached
Note that you can not detach a List
/Collection
. You will have to loop over your list and detach each item in your list like so
foreach (var item in properties)
{
db.Entry(item).State = EntityState.Detached;
}
Long story short: Use Foreign key and it will save your day.
Assume you have a School entity and a City entity, and this is a many-to-one relationship where a City has many Schools and a School belong to a City. And assume the Cities are already existing in the lookup table so you do NOT want them to be inserted again when inserting a new school.
Initially you might define you entities like this:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public City City { get; set; }
}
And you might do the School insertion like this (assume you already have City property assigned to the newItem):
public School Insert(School newItem)
{
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
// use the following statement so that City won't be inserted
context.Entry(newItem.City).State = EntityState.Unchanged;
context.SaveChanges();
return newItem;
}
}
The above approach may work perfectly in this case, however, I do prefer the Foreign Key approach which to me is more clear and flexible. See the updated solution below:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
}
public class School
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("City_Id")]
public City City { get; set; }
[Required]
public int City_Id { get; set; }
}
In this way, you explicitly define that the School has a foreign key City_Id and it refers to the City entity. So when it comes to the insertion of School, you can do:
public School Insert(School newItem, int cityId)
{
if(cityId <= 0)
{
throw new Exception("City ID no provided");
}
newItem.City = null;
newItem.City_Id = cityId;
using (var context = new DatabaseContext())
{
context.Set<School>().Add(newItem);
context.SaveChanges();
return newItem;
}
}
In this case, you explicitly specify the City_Id of the new record and remove the City from the graph so that EF won't bother to add it to the context along with School.
Though at the first impression the Foreign key approach seems more complicated, but trust me this mentality will save you a lot of time when it comes to inserting a many-to-many relationship (imaging you have a School and Student relationship, and the Student has a City property) and so on.
Hope this is helpful to you.
If you just want to store changes to a parent object and avoid storing changes to any of its child objects, then why not just do the following:
using (var ctx = new MyContext())
{
ctx.Parents.Attach(parent);
ctx.Entry(parent).State = EntityState.Added; // or EntityState.Modified
ctx.SaveChanges();
}
The first line attaches the parent object and the whole graph of its dependent child objects to the context in Unchanged
state.
The second line changes the state for the parent object only, leaving its children in the Unchanged
state.
Note that I use a newly created context, so this avoids saving any other changes to the database.
One of the suggested solutions is to assign the navigation property from the same database context. In this solution, the navigation property assigned from outside the database context would be replaced. Please, see following example for illustration.
class Company{
public int Id{get;set;}
public Virtual Department department{get; set;}
}
class Department{
public int Id{get; set;}
public String Name{get; set;}
}
Saving to database:
Company company = new Company();
company.department = new Department(){Id = 45};
//an Department object with Id = 45 exists in database.
using(CompanyContext db = new CompanyContext()){
Department department = db.Departments.Find(company.department.Id);
company.department = department;
db.Companies.Add(company);
db.SaveChanges();
}
Microsoft enlists this as a feature, however I find this annoying. If the department object associated with company object has Id that already exists in database, then why doesn't EF just associates company object with database object? Why should we need to take care of the association by ourselves? Taking care of the navigation property during adding new object is something like moving the database operations from SQL to C#, cumbersome to the developers.
First you need to know that there are two ways for updating entity in EF.
When you change the relationship of the objects attached to the object context by using one of the methods described above, the Entity Framework needs to keep foreign keys, references, and collections in sync.
If you are working with disconnected objects you must manually manage the synchronization.
In the application I'm building, the EF object model is not being loaded from the database but used as data objects which I'm populating while parsing a flat file.
That means you are working with disconnected object, but it's unclear whether you are using independent association or foreign key association.
Add
When adding new entity with existing child object (object that exists in the database), if the child object is not tracked by EF, the child object will be re-inserted. Unless you manually attach the child object first.
db.Entity(entity.ChildObject).State = EntityState.Modified;
db.Entity(entity).State = EntityState.Added;
Update
You can just mark the entity as modified, then all scalar properties will be updated and the navigation properties will simply be ignored.
db.Entity(entity).State = EntityState.Modified;
Graph Diff
If you want to simplify the code when working with disconnected object, you can give a try to graph diff library.
Here is the introduction, Introducing GraphDiff for Entity Framework Code First - Allowing automated updates of a graph of detached entities.
Sample Code
Insert entity if it doesn't exist, otherwise update.
db.UpdateGraph(entity);
Insert entity if it doesn't exist, otherwise update AND insert child object if it doesn't exist, otherwise update.
db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject));
Best way to do this is in by overriding the SaveChanges function in your datacontext.
public override int SaveChanges()
{
var added = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Added);
// Do your thing, like changing the state to detached
return base.SaveChanges();
}
This worked for me:
// temporarily 'detach' the child entity/collection to have EF not attempting to handle them
var temp = entity.ChildCollection;
entity.ChildCollection = new HashSet<collectionType>();
.... do other stuff
context.SaveChanges();
entity.ChildCollection = temp;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With