Question

I have searched hi and low and I am stuck here.

I am using EF 4.1 in an MVC3 app, with the Service/Repository/UnitOfWork pattern and AutoMapper to map my models and entities.

So I have a really basic situation; I have a collection of ChildProducts that have a collection of PriceTiers.

My view models look like this:

AddEditChildProductModel

public class AddEditChildProductModel
    {
        #region "Fields/Properties"
        public ActionType ActionType { get; set; }
        public string FormAction { get; set; }

        public int ID { get; set; }
        public int ProductID { get; set; }

        public string Sku { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }

        public decimal Cost { get; set; }
        public decimal MSRP { get; set; }
        public decimal RetailPrice { get; set; }

        public int Servings { get; set; }
        public decimal Weight { get; set; }

        public bool Display { get; set; }
        public int DisplayIndex { get; set; }

        public IEnumerable<AddEditPriceTierModel> PriceTiers { get; set; }

        #endregion

        #region "Constructor(s)"

        #endregion

        #region "Methods"

        #endregion
    }

AddEditPriceTierModel

public class AddEditPriceTierModel
    {
        #region "Fields/Properties"
        public int ID { get; set; }
        public int ChildProductID { get; set; }
        public decimal Price { get; set; }
        public int QuantityStart { get; set; }
        public int QuantityEnd { get; set; }
        public bool IsActive { get; set; }
        #endregion

        #region "Constructor(s)"

        #endregion

        #region "Methods"

        #endregion
    }

In the controller action, I am simply trying to map the changed PriceTier properties:

public ActionResult EditChildProduct(AddEditChildProductModel model)
        {
            if (!ModelState.IsValid)
                return PartialView("AddEditChildProduct", model);

            ChildProduct childProduct = productService.GetChildProductByID(model.ID);
            AutoMapper.Mapper.Map<AddEditChildProductModel, ChildProduct>(model, childProduct);
            UnitOfWork.Commit();

            return ListChildProducts(model.ProductID);
        }

And I am getting this error:

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.

When stepping into the action, the models/entities are mapped correctly, I don't get it!!

Was it helpful?

Solution

Eranga is right. I'm guessing your productService does not call AsNoTracking on the ef context before returning the ChildProduct. If not, this means what it returns is still attached to the context. When automapper does its thing, it replaces the whole collection, which can orphan the attached child entities that were not part of the form submission. Since the orphans don't have a non-null foreign key, they must be deleted from the context before calling SaveChanges. If they are not, you get this infamous exception.

On the other hand, if your productService calls AsNoTracking on the context before returning the entity, it will not track changes, and will not try to delete any orphaned items that do not exist in the collection created by automapper.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top