private SyncContext CreateContext(RemovalContext removalContext,
                                          OperationType type, AssociationInfo association, Entity owner, Entity target)
        {
            SyncActionSet masterActions = GetSyncActions(association);
            SyncActionSet slaveActions  = GetSyncActions(association.Reversed);
            Entity        master1       = owner;
            Entity        slave2        = target;
            Entity        slave1        = null;
            Entity        master2       = null;

            if (masterActions.GetValue != null)
            {
                slave1 = (Entity)masterActions.GetValue(association, master1);
            }
            if (slave2 != null && slaveActions.GetValue != null)
            {
                master2 = (Entity)slaveActions.GetValue(association.Reversed, slave2);
            }

            var context = new SyncContext(removalContext);

            switch (type)
            {
            case OperationType.Add:
            case OperationType.Set:
                // Setting new value for slave
                if (slave2 != null && !(association.IsLoop && master1 == slave2))
                {
                    context.Enqueue(new SyncAction(slaveActions.Create, association.Reversed, slave2, master1));
                }

                // Breaking existing associations
                if (master2 != null)
                {
                    context.Enqueue(new SyncAction(masterActions.Break, association, master2, slave2));
                }
                if (slave1 != null && slave1 != slave2)
                {
                    context.Enqueue(new SyncAction(slaveActions.Break, association.Reversed, slave1, master1));
                }
                break;

            case OperationType.Remove:
                var currentRemovalContext = Session.RemovalProcessor.Context;
                var isNotYetRemoved       = currentRemovalContext == null || !currentRemovalContext.Contains(slave2);
                if ((!(association.IsLoop && master1 == slave2)) && isNotYetRemoved)
                {
                    context.Enqueue(new SyncAction(slaveActions.Break, association.Reversed, slave2, master1));
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
            return(context);
        }
 public void ProcessRecursively(SyncContext context, RemovalContext removalContext,
                                OperationType type, AssociationInfo association, Entity owner, Entity target,
                                Action finalizer)
 {
     if (context == null)
     {
         // We must create a new context
         context = CreateContext(removalContext, type, association, owner, target);
         context.ProcessPendingActionsRecursively(finalizer);
     }
     else
     {
         // If we are here, provided operation is meaningless -
         // it is passed just because we entered into this method once more
         // after executing N-th operation in context. So it's just right
         // time to execute the next one.
         context.ProcessPendingActionsRecursively(finalizer);
     }
 }
Beispiel #3
0
        internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext)
        {
            if (field.ReflectedType.IsInterface)
            {
                field = TypeInfo.FieldMap[field];
            }
            SystemSetValueAttempt(field, value);
            var    fieldAccessor = GetFieldAccessor(field);
            object oldValue      = GetFieldValue(field);

            // Handling 'OldValue != NewValue' problem for structures
            var o = oldValue as Structure;

            if (o != null)
            {
                oldValue = Activator.CreateStructure(Session, o.GetType(), o.Tuple.ToRegular());
            }

            try {
                var operations = Session.Operations;
                var scope      = operations.BeginRegistration(Operations.OperationType.System);
                try {
                    var entity = this as Entity;
                    if (entity != null)
                    {
                        if (operations.CanRegisterOperation)
                        {
                            operations.RegisterOperation(new EntityFieldSetOperation(entity.Key, field, value));
                        }
                        var entityValue = value as IEntity;
                        if (entityValue != null)
                        {
                            var valueKey = entityValue.Key;
                            Session.ReferenceFieldsChangesRegistry.Register(entity.Key, valueKey, field);
                        }
                    }
                    else
                    {
                        var persistent   = this;
                        var currentField = field;
                        var structure    = persistent as Structure;
                        while (structure != null && structure.Owner != null)
                        {
                            var pair = new Pair <FieldInfo>(structure.Field, currentField);
                            currentField = structure.Owner.TypeInfo.StructureFieldMapping[pair];
                            persistent   = structure.Owner;
                            structure    = persistent as Structure;
                        }
                        entity = persistent as Entity;
                        if (entity != null)
                        {
                            if (operations.CanRegisterOperation)
                            {
                                operations.RegisterOperation(new EntityFieldSetOperation(entity.Key, currentField, value));
                            }
                            var entityValue = value as IEntity;
                            if (entityValue != null)
                            {
                                var valueKey = entityValue.Key;
                                Session.ReferenceFieldsChangesRegistry.Register(entity.Key, valueKey, field);
                            }
                        }
                    }

                    if (fieldAccessor.AreSameValues(oldValue, value))
                    {
                        operations.NotifyOperationStarting(false);
                        scope.Complete();
                        return;
                    }
                    {
                        SystemBeforeSetValue(field, value);
                        operations.NotifyOperationStarting(false);
                        AssociationInfo association = null;
                        entity = value as Entity ?? oldValue as Entity;
                        if (entity != null)
                        {
                            association = field.GetAssociation(entity.TypeInfo);
                        }
                        if (association != null && association.IsPaired)
                        {
                            Key currentKey   = GetReferenceKey(field);
                            Key newKey       = null;
                            var newReference = (Entity)(object)value;
                            if (newReference != null)
                            {
                                newKey = newReference.Key;
                            }
                            if (currentKey != newKey)
                            {
                                Session.PairSyncManager.ProcessRecursively(syncContext, removalContext,
                                                                           OperationType.Set, association, (Entity)this, newReference, () => {
                                    SystemBeforeTupleChange();
                                    fieldAccessor.SetUntypedValue(this, value);
                                    SystemTupleChange();
                                });
                            }
                        }
                        else
                        {
                            // The method of Equals(object, object) wrapped with in a block 'try catch',
                            // because that for data types NpgsqlPath and NpgsqlPolygon which are defined without an initial value it works incorrectly.
                            bool canBeEqual;
                            try {
                                canBeEqual = Equals(value, oldValue);
                            }
                            catch (Exception) {
                                canBeEqual = false;
                            }
                            if (!canBeEqual || field.IsStructure)
                            {
                                SystemBeforeTupleChange();
                                value = AdjustFieldValue(field, oldValue, value);
                                fieldAccessor.SetUntypedValue(this, value);
                                SystemTupleChange();
                            }
                        }

                        if (removalContext != null)
                        {
                            // Postponing finalizers (events)
                            removalContext.EnqueueFinalizer(() => {
                                try {
                                    try {
                                        SystemSetValue(field, oldValue, value);
                                        SystemSetValueCompleted(field, oldValue, value, null);
                                        scope.Complete();
                                    }
                                    finally {
                                        scope.DisposeSafely();
                                    }
                                }
                                catch (Exception e) {
                                    SystemSetValueCompleted(field, oldValue, value, e);
                                    throw;
                                }
                            });
                            return;
                        }

                        SystemSetValue(field, oldValue, value);
                        SystemSetValueCompleted(field, oldValue, value, null);
                    }
                    scope.Complete();
                }
                finally {
                    if (removalContext == null)
                    {
                        scope.DisposeSafely();
                    }
                }
            }
            catch (Exception e) {
                SystemSetValueCompleted(field, oldValue, value, e);
                throw;
            }
        }
        // Constructors

        public SyncContext(RemovalContext removalContext)
        {
            this.removalContext = removalContext;
        }
 private static void OnRemoveReference(AssociationInfo association, IEntity owner, IEntity target, SyncContext syncContext, RemovalContext removalContext)
 {
     ((EntitySetBase)((Entity)owner).GetFieldValue(association.OwnerField)).Remove((Entity)target, syncContext, removalContext);
 }
 private static void OnSetReference(AssociationInfo association, IEntity owner, IEntity target, SyncContext syncContext, RemovalContext removalContext)
 {
     ((Entity)owner).SetFieldValue(association.OwnerField, target, syncContext, removalContext);
 }
        private static void OnClearReference(AssociationInfo association, IEntity owner, IEntity target, SyncContext syncContext, RemovalContext removalContext)
        {
            var nullIsEntity = owner as IHasNullEntity;
            var nullValue    = nullIsEntity == null ? null : nullIsEntity.NullEntity;

            if (nullValue != null || association.OwnerField.IsNullable)
            {
                // If field is non-nullable & null value is real null, we should avoid assigning it,
                // since this will lead to an error on persist in almost any case;
                // but if we won't assign it, it will either fail with ref. constraint violation later,
                // or will succeed, if both target and owner will be removed.
                ((Entity)owner).SetFieldValue(association.OwnerField, nullValue, syncContext, removalContext);
            }
        }
        internal bool Remove(Entity item, SyncContext syncContext, RemovalContext removalContext)
        {
            if (!Contains(item))
            {
                return(false);
            }

            try {
                var operations = Session.Operations;
                var scope      = operations.BeginRegistration(Operations.OperationType.System);
                try {
                    var itemKey = item.Key;
                    if (operations.CanRegisterOperation)
                    {
                        operations.RegisterOperation(new EntitySetItemRemoveOperation(Owner.Key, Field, itemKey));
                    }

                    SystemBeforeRemove(item);

                    int?   index       = null;
                    var    association = Field.GetAssociation(item.TypeInfo);
                    Action finalizer   = () => {
                        var auxiliaryType = association.AuxiliaryType;
                        if (auxiliaryType != null && association.IsMaster)
                        {
                            var combinedTuple = auxilaryTypeKeyTransform.Apply(
                                TupleTransformType.Tuple,
                                Owner.Key.Value,
                                itemKey.Value);

                            var combinedKey = Key.Create(
                                Session.Domain,
                                Session.StorageNodeId,
                                auxiliaryType,
                                TypeReferenceAccuracy.ExactType,
                                combinedTuple);

                            Session.RemoveOrCreateRemovedEntity(auxiliaryType.UnderlyingType, combinedKey, EntityRemoveReason.Association);
                        }

                        var state = State;
                        index = GetItemIndex(state, itemKey);
                        state.Remove(itemKey);
                        Session.EntitySetChangeRegistry.Register(state);
                    };

                    operations.NotifyOperationStarting();
                    if (association.IsPaired)
                    {
                        Session.PairSyncManager.ProcessRecursively(syncContext, removalContext,
                                                                   OperationType.Remove, association, Owner, item, finalizer);
                    }
                    else
                    {
                        finalizer.Invoke();
                    }

                    if (removalContext != null)
                    {
                        // Postponing finalizers (events)
                        removalContext.EnqueueFinalizer(() => {
                            try {
                                try {
                                    index = GetItemIndex(State, itemKey); // Necessary, since index can be already changed
                                    if (!skipOwnerVersionChange)
                                    {
                                        Owner.UpdateVersionInfo(Owner, Field);
                                    }
                                    SystemRemove(item, index);
                                    SystemRemoveCompleted(item, null);
                                    scope.Complete();
                                }
                                finally {
                                    scope.DisposeSafely();
                                }
                            }
                            catch (Exception e) {
                                SystemRemoveCompleted(item, e);
                                throw;
                            }
                        });
                        return(true);
                    }

                    if (!skipOwnerVersionChange)
                    {
                        Owner.UpdateVersionInfo(Owner, Field);
                    }
                    SystemRemove(item, index);
                    SystemRemoveCompleted(item, null);
                    scope.Complete();
                    return(true);
                }
                finally {
                    if (removalContext == null)
                    {
                        scope.DisposeSafely();
                    }
                }
            }
            catch (Exception e) {
                SystemRemoveCompleted(item, e);
                throw;
            }
        }
        internal bool Add(Entity item, SyncContext syncContext, RemovalContext removalContext)
        {
            if (Contains(item))
            {
                return(false);
            }

            try {
                var operations = Session.Operations;
                using (var scope = operations.BeginRegistration(Operations.OperationType.System)) {
                    var itemKey = item.Key;
                    if (operations.CanRegisterOperation)
                    {
                        operations.RegisterOperation(new EntitySetItemAddOperation(Owner.Key, Field, itemKey));
                    }

                    SystemBeforeAdd(item);

                    int?   index       = null;
                    var    association = Field.GetAssociation(item.TypeInfo);
                    Action finalizer   = () => {
                        var auxiliaryType = association.AuxiliaryType;
                        if (auxiliaryType != null && association.IsMaster)
                        {
                            var combinedTuple = auxilaryTypeKeyTransform.Apply(
                                TupleTransformType.Tuple,
                                Owner.Key.Value,
                                itemKey.Value);

                            var combinedKey = Key.Create(
                                Session.Domain,
                                Session.StorageNodeId,
                                auxiliaryType,
                                TypeReferenceAccuracy.ExactType,
                                combinedTuple);

                            Session.CreateOrInitializeExistingEntity(auxiliaryType.UnderlyingType, combinedKey);
                            Session.ReferenceFieldsChangesRegistry.Register(Owner.Key, itemKey, combinedKey, Field);
                        }

                        var state = State;
                        state.Add(itemKey);
                        Session.EntitySetChangeRegistry.Register(state);
                        index = GetItemIndex(state, itemKey);
                    };

                    operations.NotifyOperationStarting();
                    if (association.IsPaired)
                    {
                        Session.PairSyncManager.ProcessRecursively(syncContext, removalContext,
                                                                   OperationType.Add, association, Owner, item, finalizer);
                    }
                    else
                    {
                        finalizer.Invoke();
                    }

                    // removalContext is unused here, since Add is never
                    // invoked in reference cleanup process directly

                    if (!skipOwnerVersionChange)
                    {
                        Owner.UpdateVersionInfo(Owner, Field);
                    }
                    SystemAdd(item, index);
                    SystemAddCompleted(item, null);
                    scope.Complete();
                    return(true);
                }
            }
            catch (Exception e) {
                SystemAddCompleted(item, e);
                throw;
            }
        }