0

I've searched... I promise. The closest I came to an answer is a post where EF self tracking entities are used and I don't understand how to make it apply to my situation.

In my case, I have an existing record where I want to change a value from an int to a NULL.

Table Pseudocode:

PICKLIST_VALUE
Picklist ID (int, PK, Identity, NOT NULL)
PicklistValue (String, NOT NULL)

PERSON
Person_ID (int, PK,Identity, NOT NULL)
Person_Name (varchar(100), NOT NULL)
Person_Prefix (int, FK, NULL)
Person_Suffix (int, FK, NULL)
FOREIGN KEY (Person_Prefix) REFERENCES PICKLIST_VALUES.Picklist_ID
FOREIGN KEY (Person_Suffix) REFERENCES PICKLIST_VALUES.Picklist_ID

I am using EF6, DB first... With a DAL based on Magnus Montin's post at  https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/

In my code I have POCO definitions for each entity (table). A Person can have a prefix (Mr., Mrs., etc.) and a Person can have a suffix (Jr., Sr., etc.) If I accidentally set a suffix and then realize that it's incorrect, I want to be able to remove the suffix:

someperson.Person_Suffix = null;  // Set FK property to null
someperson.PICKLIST_VALUE = null; // Set navigation property to null
someperson.EntityState = EntityStates.Modified;
DAL.UpdatePerson(someperson);

The UpdatePerson method resolves to this bit based on the DAL article:

public virtual void Update(params T[] items)
{
  using (var context = new Entities())
  {
    DbSet<T> dbSet = context.Set<T>();

    foreach (T item in items)
    {
      dbSet.Add(item);
      foreach (DbEntityEntry<IEntity> entry in context.ChangeTracker.Entries<IEntity>())
      {
        IEntity entity = entry.Entity;
        entry.State = GetEntityState(entity.EntityState);
      }
    }

    context.SaveChanges();
  }
}

protected static System.Data.Entity.EntityState GetEntityState(CB.DomainModel.EntityState entityState)
{
  switch (entityState)
  {
    case DomainModel.EntityState.Unchanged:
      return System.Data.Entity.EntityState.Unchanged;
    case DomainModel.EntityState.Added:
      return System.Data.Entity.EntityState.Added;
    case DomainModel.EntityState.Modified:
      return System.Data.Entity.EntityState.Modified;
    case DomainModel.EntityState.Deleted:
      return System.Data.Entity.EntityState.Deleted;
    default:
      return System.Data.Entity.EntityState.Detached;
  }
}

I don't know enough of what's going on under the covers of the EF stuff and what the .edmx and T4 template code does. If I did, I could probably figure this out. But if I change the values from one int to another, the changes are persisted to the database after this call. But if I null the values, as in this example, to "clear" the foreign key reference, the original value comes back.

It must have something to do with the way the object registers with the ChangeTracker once it's added to the dbSet. Prior to that, it's just a plain old object.

So my question is why is the ChangeTracker (or whatever bit of auto-generated code) allowing the FK to change from one int value to another, but it won't persist the null? And how do I "fix" it or work around the issue?

Thanks. J

UPDATE:

The key is definitely nullable. Both in the DB and in the class definition. The link to the other post seems to be a different scenario... They want to automatically null the foreign key when the foreign entity is deleted. (I will take a closer look, just to be sure there isn't an answer there, but it seems to be a different issue.) Just for fun I created a new object and copied the properties over, with the exception of the nav properties and set the FK property I want to remove to null and that did work. The null FK was persisted, and both the nav prop and the foreign key prop were null. So it has something to do with the change tracker and how it is maintaining a reference to the entity object, despite all the POCO-ness of it. So, at the very least, I have a work-around. But I would like to understand what's actually happening and how to address it.

SOLUTION:

I just discovered, this morning, that a change I made to the T4 template must have been over-ridden, at some point, and I lost a line setting ProxyCreationEnabled to false for the DBContext...  Once I realized that and put the fix in, I was able to persist the changes properly.  So, in effect, I was fighting something in the change tracker since it was creating the dynamic proxies for the entities retrieved from the DB.  (I think.)

Digital Camel
  • 175
  • 2
  • 14
  • If the `Person` class has a foreign key property, which isn't nullable (i.e. Person has something like: `public int Person_PrefixId { get; set; }`, then the relationship is required and you **cannot** set `Person_Suffix` to NULL. If you want to make the relationship optional, the foreign key property should be nullable: `public int? Person_PrefixId { get; set; }` – Jakub Jankowski Oct 13 '16 at 18:33
  • You can also verify the foreign key field nullability by opening the EF diagram, select the Person_Suffix or Person_Prefix field and check the Nullable property in the property window. – Bouke Oct 13 '16 at 18:57
  • Why are you setting both the property and the related navigational property to null? – Bouke Oct 13 '16 at 18:58
  • 1
    You set only the navigation property to NULL, not the FK. EF won't set the FK to null, just because you set the navigation property to null (since you may not even have retrieved in the first place). – DevilSuichiro Oct 13 '16 at 20:43
  • Possible duplicate of [How to update FK to null when deleting optional related entity](http://stackoverflow.com/questions/33912625/how-to-update-fk-to-null-when-deleting-optional-related-entity) – DevilSuichiro Oct 13 '16 at 20:45
  • @Bouke - I have had issues, in the past, with key conflicts in some scenarios when nav props are populated. Something to do with multiple references to the same object, or multiple instances of the object. I don't recall the exact scenario, anymore. Nulling the nav prop had fixed it in the past and allowed me to submit my entity for update. I guess I never ran into the FK scenario, before... – Digital Camel Oct 13 '16 at 23:33
  • @DevilSuichiro - I am setting both to null. Are you saying I should only set the nav property to null? How would EF know if it was a change or if it just hadn't been retrieved? Is that stored in the change tracker? What if it hadn't been retrieved, but I still want to update the FK in the DB to null? I'm not fully comprehending your suggestion. – Digital Camel Oct 13 '16 at 23:38
  • it is only "stored" in the ChangeTracker, if you are in a connected scenario and you included the corresponding navigation properties entirely. Otherwise setting the FK to null should be enough, if it isn't, your weird Update method might be the cause of this. – DevilSuichiro Oct 14 '16 at 04:59
  • @DevilSuichiro - Why do you consider the update method to be "weird"? I do retrieve the nav props, in this case. The entities are disconnected while editing and the Update method reconnects them. It certainly appears that the change tracker is retaining some memory of the object, though. I tried updating FK and Nav Prop, then just one, then the other. The only way I can get the update to persist the null is by creating a brand new object. I have the work-around, I just don't understand the reason... – Digital Camel Oct 14 '16 at 17:49
  • no it doesn't. You add the item to the DbSet (which should contain a similar one already) and then set the state depending on the ChangeTracker - which should not do anything if you are really in a disconnected scenario, since you just added the entry (added state), while the entry of the old object is unchanged. I don't really see how this would update your entry. Why don't you just attach the object (get an entry object) and set it to the appropriate state? – DevilSuichiro Oct 14 '16 at 22:52

1 Answers1

0

I just discovered, this morning, that a change I made to the T4 template must have been over-ridden, at some point, and I lost a line setting ProxyCreationEnabled to false for the DBContext...  Once I realized that and put the fix in, I was able to persist the changes properly.  So, in effect, I was fighting something in the change tracker since it was creating the dynamic proxies for the entities retrieved from the DB.  (I think.)

Digital Camel
  • 175
  • 2
  • 14