//[DebuggerStepThrough()]
        protected virtual void DoNotifyPropertySet(object obj, string propertyName, ref object value, object oldValue, bool hasOldValue, ref bool cancel)
        {
            IContext                ctx = this.Context;
            IObjectManager          om  = ctx.ObjectManager;
            IPersistenceEngine      pe  = ctx.PersistenceEngine;
            PropertyCancelEventArgs e;

            if (hasOldValue)
            {
                e = new PropertyCancelEventArgs(obj, propertyName, value, oldValue, this.Context.ObjectManager.GetNullValueStatus(obj, propertyName));
            }
            else
            {
                e = new PropertyCancelEventArgs(obj, propertyName, value, this.Context.ObjectManager.GetPropertyValue(obj, propertyName), this.Context.ObjectManager.GetNullValueStatus(obj, propertyName));
            }
            this.Context.EventManager.OnWritingProperty(this, e);
            if (e.Cancel)
            {
                cancel = true;
                return;
            }
            value = e.NewValue;
            IClassMap    classMap = ctx.DomainMap.MustGetClassMap(obj.GetType());
            IPropertyMap propertyMap;
            string       prevId;
            string       newId;

            propertyMap = classMap.MustGetPropertyMap(propertyName);

            if (propertyMap.ReferenceType != ReferenceType.None && value != null)
            {
                if (propertyMap.ReferenceType == ReferenceType.OneToMany || propertyMap.ReferenceType == ReferenceType.OneToOne)
                {
                    //parent object
                    IInterceptable ivalue = value as IInterceptable;
                    if (ivalue == null)
                    {
                        throw new NPersistException(string.Format("Object is not a NPersist managed object, do not use 'new' on Entities. (Property='{0}', Owner={1})", propertyName, obj));
                    }
                    else
                    {
                        if (ivalue.GetInterceptor().Context != this.Context)
                        {
                            throw new NPersistException(string.Format("Object does not belong to the same context object as the property owner. (Property='{0}', Owner={1})", propertyName, obj));
                        }
                        ObjectStatus valueObjectStatus = om.GetObjectStatus(value);
                        if (valueObjectStatus == ObjectStatus.UpForDeletion || valueObjectStatus == ObjectStatus.Deleted)
                        {
                            throw new DeletedObjectException(string.Format("Object has been deleted. (Object={0})", value), value);
                        }
                    }
                }
                else if (propertyMap.ReferenceType == ReferenceType.ManyToOne || propertyMap.ReferenceType == ReferenceType.ManyToMany)
                {
                    IInterceptableList ivalue = value as IInterceptableList;
                    if (ivalue == null)
                    {
                        throw new NPersistException(string.Format("List is not a NPersist managed list, do not use 'new' to initialize lists, NPersist does this for you. (Property='{0}', Owner={1})", propertyName, obj));
                    }
                    else if (ivalue.Interceptable.GetInterceptor().Context != this.Context)
                    {
                        throw new NPersistException(string.Format("List does not belong to the same context object as the property owner. (Property='{0}', Owner={1})", propertyName, obj));
                    }
                }
            }

            if (propertyMap.IsReadOnly)
            {
                //Let read-only inverse properties through
                if (!(propertyMap.ReferenceType != ReferenceType.None && propertyMap.Inverse.Length > 0 && propertyMap.NoInverseManagement == false))
                {
                    //Special - if someone forgot to make their ManyOne read-only,
                    //why bug them about it? (so don't add an "else" with an exception...)
                    if (propertyMap.ReferenceType != ReferenceType.ManyToOne)
                    {
                        throw new ReadOnlyException("Property '" + classMap.Name + "." + propertyName + "' is read-only!");                         // do not localize
                    }
                }
            }
            PropertyStatus propStatus        = PropertyStatus.Clean;
            ObjectStatus   objStatus         = om.GetObjectStatus(obj);
            bool           hasPropertyStatus = false;

            if (objStatus == ObjectStatus.Deleted)
            {
                throw new DeletedObjectException("The object has been deleted!", obj, propertyName);                 // do not localize
            }
            else if (objStatus == ObjectStatus.UpForDeletion)
            {
                throw new DeletedObjectException("The object has been registered as up for deletion!", obj, propertyName);                 // do not localize
            }

            this.Context.ObjectCloner.EnsureIsClonedIfEditing(obj);

            if (objStatus == ObjectStatus.UpForCreation)
            {
            }
            else if (objStatus == ObjectStatus.Clean)
            {
                propStatus = om.GetPropertyStatus(obj, propertyName);
                if (propStatus == PropertyStatus.NotLoaded)
                {
                    pe.LoadProperty(obj, propertyName);
                }
                if (!(hasOldValue))
                {
                    if (!(om.ComparePropertyValues(obj, propertyName, value, om.GetPropertyValue(obj, propertyName))))
                    {
                        this.Context.UnitOfWork.RegisterDirty(obj);
                    }
                }
            }
            else if (objStatus == ObjectStatus.NotLoaded)
            {
                propertyMap = this.Context.DomainMap.MustGetClassMap(obj.GetType()).MustGetPropertyMap(propertyName);
                if (!(propertyMap.IsIdentity))
                {
                    propStatus        = this.Context.ObjectManager.GetPropertyStatus(obj, propertyName);
                    hasPropertyStatus = true;
                    //it would be sweet to be able to determine beforehand if this property would be part of the span
                    //that is loaded with LoadObject and only call LoadObject if that is the case....
                    if (propStatus == PropertyStatus.NotLoaded)
                    {
                        hasPropertyStatus = false;
                        //this.Context.PersistenceEngine.LoadObject(ref obj);
                        this.Context.IdentityMap.LoadObject(ref obj, true);
                        if (obj == null)
                        {
                            throw new ObjectNotFoundException("Object not found!");                             // do not localize
                        }
                    }
                    if (!hasPropertyStatus)
                    {
                        propStatus = om.GetPropertyStatus(obj, propertyName);
                    }

                    if (propStatus == PropertyStatus.NotLoaded)
                    {
                        pe.LoadProperty(obj, propertyName);
                    }
                }

                if (!(hasOldValue))
                {
                    if (!(om.ComparePropertyValues(obj, propertyName, value, om.GetPropertyValue(obj, propertyName))))
                    {
                        this.Context.UnitOfWork.RegisterDirty(obj);
                    }
                }
            }
            else if (objStatus == ObjectStatus.Dirty)
            {
                propStatus = om.GetPropertyStatus(obj, propertyName);
                if (propStatus == PropertyStatus.NotLoaded)
                {
                    pe.LoadProperty(obj, propertyName);
                }
            }
            if (propertyMap.IsIdentity)
            {
                prevId = om.GetObjectIdentity(obj);
                newId  = om.GetObjectIdentity(obj, propertyMap, value);
                if (prevId != newId)
                {
                    ctx.IdentityMap.UpdateIdentity(obj, prevId, newId);
                }
            }
            om.SetNullValueStatus(obj, propertyName, false);
            om.SetUpdatedStatus(obj, propertyName, true);
            if (hasOldValue)
            {
                ctx.InverseManager.NotifyPropertySet(obj, propertyName, value, oldValue);
                ctx.UnitOfWork.RegisterDirty(obj);
            }
            else
            {
                ctx.InverseManager.NotifyPropertySet(obj, propertyName, value);
            }
        }