title: Entity Framework and MVC 3: The relationship could not be changed because one or more of the foreign-key properties is non-nullable
body: <p>I have been trying to use one View for updating an object and all its child collections (based on one-to-many relationships in an SQL Server database with an Entity Framework model).</p>
  
  <p>It was suggested I should use AutoMapper, and I tried that and got it to work. (see <a href="https://stackoverflow.com/questions/5126603/trying-to-use-automapper-for-model-with-child-collections-getting-null-error-in/5130861#5130861">Trying to use AutoMapper for model with child collections, getting null error in Asp.Net MVC 3</a> ).</p>
  
  <p>But the solution is really hard to maintain. And when I try the simple one I had to begin with, using an entity object directly as the model (a "Consultant" object, the parent of all the child collections), I am able to get all the correct changed data back in the POST, and I can use UpdateModel to get them, including child collections. Simple. Granted, UpdateModel only worked after creating a custom model binder from a tip here at SO:</p>
  
  <p>From my custom model binder:</p>
  
  <pre><code>public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
          {
              bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
  
              return base.BindModel(controllerContext, bindingContext);
          }
  
          protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
          {
              ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
              propertyMetadata.Model = value;
              string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);
  
              // Try to set a value into the property unless we know it will fail (read-only 
              // properties and null values with non-nullable types)
              if (!propertyDescriptor.IsReadOnly)
              {
                  try
                  {
                      if (value == null)
                      {
                          propertyDescriptor.SetValue(bindingContext.Model, value);
                      }
                      else
                      {
                          Type valueType = value.GetType();
  
                          if (valueType.IsGenericType &amp;&amp; valueType.GetGenericTypeDefinition() == typeof(EntityCollection&lt;&gt;))
                          {
                              IListSource ls = (IListSource)propertyDescriptor.GetValue(bindingContext.Model);
                              IList list = ls.GetList();
  
                              foreach (var item in (IEnumerable)value)
                              {
                                  list.Add(item);
                              }
                          }
                          else
                          {
                              propertyDescriptor.SetValue(bindingContext.Model, value);
                          }
                      }
  
                  }
                  catch (Exception ex)
                  {
                      // Only add if we're not already invalid
                      if (bindingContext.ModelState.IsValidField(modelStateKey))
                      {
                          bindingContext.ModelState.AddModelError(modelStateKey, ex);
                      }
                  }
              }
          }
  </code></pre>
  
  <p>Here's my simple Edit POST method:</p>
  
  <pre><code>    [HttpPost]
      [ValidateInput(false)] //To allow HTML in description box
      public ActionResult Edit(int id, FormCollection collection)
      {
  
          Consultant consultant = _repository.GetConsultant(id);
          UpdateModel(consultant);
          _repository.Save();
  
          return RedirectToAction("Index");
      }
  </code></pre>
  
  <p>But after that UpdateModel worked. The problem is, at the next stage, when trying to call SaveChanges on the context, that fails. I'm getting this error:</p>
  
  <blockquote>
    <p>The operation failed: The relationship
    could not be changed because one or
    more of the foreign-key properties is
    non-nullable. When a change is made to
    a relationship, the related
    foreign-key property is set to a null
    value. If the foreign-key does not
    support null values, a new
    relationship must be defined, the
    foreign-key property must be assigned
    another non-null value, or the
    unrelated object must be deleted.</p>
  </blockquote>
  
  <p>I don't understand what is wrong. I'm seeing all the correct values in the Consultant object posted, I just can't save it to database. The route of AutoMapper in this case (although an interesting tool) is not working well, it's complicating my code immensely and making the application, which should be rather simple, a nightmare to maintain.</p>
  
  <p>Can anyone offer any insight into why I'm getting this error and how to overcome it?</p>
  
  <p>UPDATE:</p>
  
  <p>Reading some posts here, I found one that seemed slightly related: <a href="https://stackoverflow.com/questions/2720512/how-to-update-model-in-the-database-from-asp-net-mvc2-using-entity-framework">How to update model in the database, from asp.net MVC2, using Entity Framework?</a> . I don't know if it relates to this, but when I inspected the Consultant object after POST it seems this object itself has entitykey, but the individual items in a collection do not (EntityKeySet = null). Each item however does have the correct id. I don't pretend to understand any of this with the EntityKey, so please explain if it has any bearings on my issue, and if so, how to resolve it...</p>
  
  <p>UPDATE 2:</p>
  
  <p>I thought of something that might have something to do with my problems: The View is using a technique described by Steven Sanderson (see <a href="http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/" rel="nofollow noreferrer">http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/</a> ), and when debugging it seems to me as if UpdateModel has trouble matching the items in a collection in the View with the ones in the actual Consultant object. I'm wondering if this has to do with the indexing in this technique. Here's the helper from that code (I can't follow it very well myself, but it uses a Guid to create indexes, which might be the problem):</p>
  
  <pre><code>public static class HtmlPrefixScopeExtensions
      {
          private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
  
          public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
          {
              var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
              string itemIndex = idsToReuse.Count &gt; 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
  
              // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
              html.ViewContext.Writer.WriteLine(string.Format("&lt;input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" /&gt;", collectionName, html.Encode(itemIndex)));
  
              return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
          }
  
          public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
          {
              return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
          }
  
          private static Queue&lt;string&gt; GetIdsToReuse(HttpContextBase httpContext, string collectionName)
          {
              // We need to use the same sequence of IDs following a server-side validation failure,  
              // otherwise the framework won't render the validation error messages next to each item.
              string key = idsToReuseKey + collectionName;
              var queue = (Queue&lt;string&gt;)httpContext.Items[key];
              if (queue == null)
              {
                  httpContext.Items[key] = queue = new Queue&lt;string&gt;();
                  var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
                  if (!string.IsNullOrEmpty(previouslyUsedIds))
                      foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
                          queue.Enqueue(previouslyUsedId);
              }
              return queue;
          }
  
          private class HtmlFieldPrefixScope : IDisposable
          {
              private readonly TemplateInfo templateInfo;
              private readonly string previousHtmlFieldPrefix;
  
              public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
              {
                  this.templateInfo = templateInfo;
  
                  previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
                  templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
              }
  
              public void Dispose()
              {
                  templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
              }
          }
      }
  </code></pre>
  
  <p>But then again, I wouldn't have thought this should be the problem since the hidden input contains the id in the value attribute, and I thought UpdateModel just looked at the name of the field to get Programs (the collection) and Name (the property), and then the value to the the id...? And then again there's seems to be some mismatch during update. Anyway, here's the generated html from FireBug also:</p>
  
  <pre><code>&lt;td&gt;
              &lt;input type="hidden" value="1" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Id" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true"&gt; 
              &lt;input type="text" value="Visual Studio" name="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" id="Programs_cabac7d3-855f-45d8-81b8-c31fcaa8bd3d__Name"&gt;
              &lt;span data-valmsg-replace="true" data-valmsg-for="Programs[cabac7d3-855f-45d8-81b8-c31fcaa8bd3d].Name" class="field-validation-valid"&gt;&lt;/span&gt;
          &lt;/td&gt;
  </code></pre>
  
  <p>Anyone know if this is the problem? And if so, how can I work around it to be able to easily update the collections with UpdateModel? (While still being able to add or remove items in the View before POST, which was the purpose of this technique to begin with).</p>