147

When I save an entity with entity framework, I naturally assumed it would only try to save the specified entity. However, it is also trying to save that entity's child entities. This is causing all sorts of integrity problems. How do I force EF to only save the entity I want to save and therefore ignore all child objects?

If I manually set the properties to null, I get an error "The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable." This is extremely counter-productive since I set the child object to null specifically so EF would leave it alone.

Why don't I want to save/insert the child objects?

Since this is being discussed back and forth in the comments, I'll give some justification of why I want my child objects left alone.

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. In the case of the child objects, many of these refer to lookup tables defining various properties of the parent table. For example, the geographic location of the primary entity.

Since I've populated these objects myself, EF assumes these are new objects and need to be inserted along with the parent object. However, these definitions already exist and I don't want to create duplicates in the database. I only use the EF object to do a lookup and populate the foreign key in my main table entity.

Even with the child objects that are real data, I needs to save the parent first and get a primary key or EF just seems to make a mess of things. Hope this gives some explanation.

Mark Micallef
  • 2,643
  • 7
  • 28
  • 36
  • As far as I know you will have to null the child objects. – Johan Aug 22 '14 at 06:48
  • Hi Johan. Doesn't work. It throws errors if I null the collection. Depending on how I do it, it complains about keys being null or that I collection has been modified. Obviously, those things are true, but I did that on purpose so it would leave alone the objects it's not supposed to touch. – Mark Micallef Aug 22 '14 at 06:51
  • Euphoric, that is completely unhelpful. – Mark Micallef Aug 22 '14 at 06:51
  • @Euphoric Even when not changing child objects, EF still attempts to insert them by default and not ignore them or update them. – Johan Aug 22 '14 at 06:57
  • What really annoys me is that if I go out of my way to actually null those objects, it then complains rather than realising that I want it to just leave them alone. Since those child objects are all optional (nullable in the database), is there some way to force EF to forget that I had those objects? i.e. purge its context or cache somehow? – Mark Micallef Aug 22 '14 at 07:01
  • @Johan No. That is not default EF behavior. EF saves child objects only when they change. If you claim otherwise, please provide some specific examples, where EF saves objects right after you load them. – Euphoric Aug 22 '14 at 07:01
  • @Euphoric I want it to leave those child objects alone. I could give a long reason why, but suffice to say, I only want to save the parent object regardless of what EF assumes. It has nothing to do with using it right or wrong, it's about achieving the business objective that I need to achieve. – Mark Micallef Aug 22 '14 at 07:03
  • @Euphoric When you are using two different context's. Basically one to load and then one to save. A Context shouldn't stay open all that time. – Johan Aug 22 '14 at 07:04
  • C'mon guys, there must be some way to make EF behave sensibly. I've tried combinations of detaching child entities, cloning the main and creating new database connections, yet EF somehow hangs on to all its junk and complains. There must be some way to completely purge EF and force it to look only at what I give it. At this rate, I'm going to have to build my own set of objects and write SQL/linq to do the inserts I need, which is a huge step backwards. – Mark Micallef Aug 22 '14 at 08:39
  • 1
    @MarkyMark, this is EF behaving as EF is intended. It is behaving entirely responsibly. It sounds like you are trying to bend the framework into achieving your own unique objectives, in that situation a framework is not always the best way to go? You wouldn't use a hammer when you really needed a wrench would you. – BenjaminPaul Aug 22 '14 at 09:04
  • You may find this helps: Why does Entity Framework Reinsert Existing Objects into My Database? msdn.microsoft.com/en-us/magazine/dn166926.aspx – Colin Aug 22 '14 at 13:50

14 Answers14

76

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.

lenglei
  • 1,168
  • 10
  • 10
  • Great answer, helped me a lot! But would it not be better to state the minimum value of 1 for City_Id using `[Range(1, int.MaxValue)]` attribute? – Dan Rayson Nov 28 '17 at 16:00
  • Like a dream!! Thanks a Million! – CJH Dec 24 '18 at 09:52
  • 1
    This would remove the value from the School object in the caller. Does SaveChanges reload the value of null navigation properties? Otherwise the caller should reload the City object after calling the Insert() method if it needs that info. This is the pattern I have used often, but I am still open to better patterns if anyone has a good one. – Ross Brasseaux May 19 '19 at 02:38
  • 1
    This was really helpful to me. EntityState.Unchaged is exactly what I needed to assign an object that represents a lookup table foreign key in a large object graph that I was saving as a single transaction. EF Core throws a less than intuitive error message IMO. I cached my lookup tables that rarely change for performance reasons. You're assumption is that, because the PK of the lookup table object is the same as what's in the database already that it knows its unchanged and to just assign the FK to the existing item. Instead of tries to insert the lookup table object as new. – tnk479 Sep 19 '19 at 20:51
  • No combination of nulling or setting the state to unchanged is working for me. EF insists it needs to insert a new child record when I just want to update a scalar field in the parent. – BobRz Mar 06 '20 at 18:28
  • Great answer! I was struggling hard to solve something like this! – Henrique Jul 31 '20 at 12:14
  • Works great for one to many or one to one. Not a good solution for many to many. For many to many better to set the entity state to modified – aaa Aug 08 '21 at 15:15
  • Looks like the FK approach is the ultimate solution for this silly behavior on EF. Hope EF-Core has resolved this in convenient manner – FLICKER Oct 21 '21 at 20:33
  • 1
    @ienglei... 4 years has passes by and your suggestion works yet... I Prefer the first solution... i'm using Entity Framework Core 6... thanks for this great advice! – Gabriel Simas Feb 16 '22 at 14:03
74

As far as I know, you have two options.

Option 1)

Null all the child objects, this will ensure EF not to add anything. It will also not delete anything from your database.

Option 2)

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;
}
Johan
  • 8,068
  • 1
  • 33
  • 46
  • Hi Johan, I tried detatching one of the collections and it threw the following error: The entity type HashSet`1 is not part of the model for the current context. – Mark Micallef Aug 22 '14 at 06:57
  • @MarkyMark Don't detach the collection. You will have to loop over the collection and detach it object for object (I'll update my answer now). – Johan Aug 22 '14 at 07:00
  • Hi Johan. This seems to work! I'm detatching all the collections and will see how it goes (it's a very complex model so will take a little while). But on the surface, seems this is the solution. :-) – Mark Micallef Aug 22 '14 at 07:12
  • 24
    Unfortunately, even with a list of loops to detatch everything, EF seems to be still trying to insert into some of the related tables. At this point, I'm ready to rip EF out and switch back to SQL which at least behaves sensibly. What a pain. – Mark Micallef Aug 22 '14 at 08:29
  • @MarkyMark Strange. Have you made sure that you are detaching the object you are getting an error message for? – Johan Aug 22 '14 at 08:48
  • it works, meaning it is not insering in the childreen tables but it is adding null to the FK from the table I am adding.... very frustating – RollRoll Apr 16 '16 at 19:16
  • What I have found is that adding the Id of the child and detaching the entity itself works best. i.e. Parent object has a property Child and ChildId. Setting the ChildId, and detaching the Child will create the relationship but will not try to update the Child itself. – Peter May 18 '17 at 14:57
  • 2
    Can I use context.Entry(yourObject).State before even adding it? – Tommehh Mar 29 '18 at 07:01
  • @ThomasKlammer I am facing the same issue and could not find any solution yet. Did you find a nice solution to your issue? – mirind4 May 05 '18 at 06:46
  • 1
    @mirind4 I did not use state. If I insert an object, I make sure all childs are null. On update I get the object first without the childs. – Tommehh May 14 '18 at 14:13
  • EntityState.Detached did not work for me but EntityState.Unchanged did work. – crichavin Jul 13 '20 at 19:31
  • Thanks Johan, I thought `Detached` was right, but wanted to check first. @crichavin: Careful there—I don't think that's doing quite what you think it is. Since state checking can happen automatically, I think it's entirely possible that the state can be restored to `Modified`, or some other value indicating a change, later. – Auspex Jun 21 '21 at 15:46
34

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.

keykey76
  • 341
  • 3
  • 4
  • 3
    This answer has the advantage that if someone comes along later and adds a child object, it won't break existing code. This is an "opt in" solution where the others require you to explicitly exclude child objects. – Jim Feb 27 '19 at 22:06
  • 1
    This no longer works in core. "Attach: Attaches every reachable entity, except where a reachable entity has a store-generated key and no key value is assigned; these will be marked as added." If children are new as well they will be Added. – mmix Apr 30 '20 at 09:49
  • easy and clean. no headache! Thanks. – FLICKER Apr 13 '21 at 22:22
  • Fantastic! Does what I need when dealing with existing employee entities that are loaded from a different instance of my db context. No fuss saving the primary keys to my main table. – Mr.Technician Jun 11 '21 at 16:53
  • Thank you. Worked, It saved lot of time. Simple solution. – iprashant Nov 28 '21 at 14:32
16

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.

  • 2
    I agree 100% it is ridiculous that EF attempts to create a new record when the ID is provided. If there was a way to fill select list options with the actual object we'd be in business. – T3.0 Jul 10 '20 at 18:09
  • 1
    I also agree and why add the additional read on the database. reads generates locks and locks slow down stuff. not an issue when debugging bet catastrophic on a busy application. – Walter Verhoeven Aug 19 '22 at 07:38
15

First you need to know that there are two ways for updating entity in EF.

  • Attached objects

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.

  • Disconnected objects

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));
    
Peter Morris
  • 20,174
  • 9
  • 81
  • 146
Yuliam Chandra
  • 14,494
  • 12
  • 52
  • 67
3

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();
    }
Wouter Schut
  • 907
  • 2
  • 10
  • 22
2

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;
Klaus
  • 21
  • 1
2

I've got the same problem when I trying to save profile, I already table salutation and new to create profile. When I insert profile it also insert into salutation. So I tried like this before savechanges().

db.Entry(Profile.Salutation).State = EntityState.Unchanged;

0

What we have done is before adding the parent to the dbset, disconnect the child collections from the parent, making sure to push the existing collections to other variables to allow working with them later, and then replacing the current child collections with new empty collections. Setting the child collections to null/nothing seemed to fail for us. After doing that then add the parent to the dbset. This way the children are not added until you want them to.

Shaggie
  • 111
  • 1
  • 8
0

I had a similar challenge using Entity Framework Core 3.1.0, my repo logic is quite generic.

This worked for me:

builder.Entity<ChildEntity>().HasOne(c => c.ParentEntity).WithMany(l =>
       l.ChildEntity).HasForeignKey("ParentEntityId");

Please note "ParentEntityId" is the foreign key column name on the child entity. I added the above mentioned line of code on this method:

protected override void OnModelCreating(ModelBuilder builder)...
slfan
  • 8,950
  • 115
  • 65
  • 78
0

If you want to set specific object without their relations do track. Use this option.

public void Insert(School newItem)
{

    using (var context = new DatabaseContext())
    {
        context.Entry(newItem).State = EntityState.Added;
        context.SaveChanges();
    }
}

Read more for a better Knolage at EF Core Documentation about Tracking

0

For those looking for a solution for Entity Framework Core, there is an Ignore function:

    public void Configure(EntityTypeBuilder<Parent> entity)
    {
        entity.Property(e => e.ChildId);

        entity.Ignore(e => e.Child);
    }
Daniel
  • 920
  • 1
  • 11
  • 22
-1

I know it's old post however if you are using code-first approach you may achieve the desired result by using following code in your mapping file.

Ignore(parentObject => parentObject.ChildObjectOrCollection);

This will basically tell EF to exclude "ChildObjectOrCollection" property from model so that it is not mapped to database.

Syed Danish
  • 106
  • 1
  • 9
  • 1
    What context is "Ignore" being used in? It doesn't appear to exist in the presumed context. – T3.0 Jul 10 '20 at 17:46
-1

This is the working code of Entity Framework Core .net 5 that saves just in parent table with no errors. I am saving the list, you can just save the object as well.

Changing to this, starts giving me another error: The entity type <typename> was not found. Ensure that the entity type has been added to the model

note: YouObject is the entity

error code: 
_context.yourObject.AttachRange(yourObjectList);
_context.Entry(_context.yourObject).State = EntityState.Added;
await _context.SaveChangesAsync().ConfigureAwait(false);

So, this is what I did: (Working Code below)

Step 1: I made my child components as null.

yourObjectList.ForEach(req => { req.ChildObject = null; });

Step 2: Changed the above code to this:

working code:

_context.yourObject.UpdateRange(yourObjectList);
await _context.SaveChangesAsync().ConfigureAwait(false); 
KushalSeth
  • 3,265
  • 1
  • 26
  • 29
  • This is a totally different situation than in the question. And your "fix" would be a very bad idea in that situation. – Gert Arnold Mar 19 '22 at 10:01
  • what is the problem with this code? @GertArnold – KushalSeth Mar 19 '22 at 10:26
  • You sever a relationship and then save the entities. Think of what woud happen if the child objects were known entities (as in the question). Apart from that, it's not clear why you have unknown entities in your model and how the objects get there. – Gert Arnold Mar 19 '22 at 10:30
  • In short, your answer only tells how you solved your own, totally different, problem. – Gert Arnold Mar 19 '22 at 10:32