public override void Process(RemovalContext context, AssociationInfo association, Entity removingObject, Entity target, Entity referencingObject, Entity referencedObject)
 {
     if (!context.Contains(target) && referencingObject != referencedObject)
     {
         throw new ReferentialIntegrityException(association, removingObject, referencingObject, referencedObject);
     }
 }
示例#2
0
 /// <summary>
 /// Builds the mapping name for the auxiliary type
 /// associated with specified <see cref="AssociationInfo"/>.
 /// </summary>
 /// <param name="target">The <see cref="AssociationInfo"/> instance to build name for.</param>
 /// <returns>Auxiliary type mapping name.</returns>
 public string BuildAuxiliaryTypeMappingName(AssociationInfo target)
 {
     return(ApplyNamingRules(string.Format(AssociationPattern,
                                           target.OwnerType.MappingName ?? target.OwnerType.Name,
                                           target.OwnerField.MappingName ?? target.OwnerField.Name,
                                           target.TargetType.MappingName ?? target.TargetType.Name)));
 }
示例#3
0
        private static SyncActionSet GetSyncActions(AssociationInfo association)
        {
            Func <AssociationInfo, IEntity, IEntity>   getValue = null;
            Action <AssociationInfo, IEntity, IEntity> @break   = null;
            Action <AssociationInfo, IEntity, IEntity> create   = null;

            switch (association.Multiplicity)
            {
            case Multiplicity.OneToOne:
            case Multiplicity.ManyToOne:
                return(new SyncActionSet(
                           ReferentialActions.GetReference,
                           ReferentialActions.ClearReference,
                           ReferentialActions.SetReference));

            case Multiplicity.ManyToMany:
            case Multiplicity.OneToMany:
                return(new SyncActionSet(
                           null,
                           ReferentialActions.RemoveReference,
                           ReferentialActions.AddReference));

            default:
                throw new ArgumentOutOfRangeException("association");
            }
        }
示例#4
0
        public static void BuildReversedAssociation(BuildingContext context, AssociationInfo origin, string fieldName)
        {
            var owner        = origin.TargetType;
            var field        = owner.Fields[fieldName];
            var multiplicity = origin.Multiplicity;

            if (origin.Multiplicity == Multiplicity.OneToMany)
            {
                multiplicity = Multiplicity.ManyToOne;
            }
            else if (origin.Multiplicity == Multiplicity.ManyToOne)
            {
                multiplicity = Multiplicity.OneToMany;
            }
            else if (origin.Multiplicity == Multiplicity.ZeroToMany)
            {
                multiplicity = field.IsEntity
          ? Multiplicity.ZeroToOne
          : Multiplicity.ZeroToMany;
            }
            else if (origin.Multiplicity == Multiplicity.ZeroToOne)
            {
                multiplicity = field.IsEntity
          ? Multiplicity.ZeroToOne
          : Multiplicity.ZeroToMany;
            }

            var onOwnerRemove  = origin.OnTargetRemove;
            var onTargetRemove = origin.OnOwnerRemove;

            if (onOwnerRemove == null && onTargetRemove == null && field.Associations.Count > 0)
            {
                var inheritedAssociation = field.Associations.FirstOrDefault(a => a.TargetType.UnderlyingType.IsAssignableFrom(origin.OwnerType.UnderlyingType));
                if (inheritedAssociation != null && (inheritedAssociation.OnOwnerRemove != null || inheritedAssociation.OnTargetRemove != null))
                {
                    onOwnerRemove  = inheritedAssociation.OnOwnerRemove;
                    onTargetRemove = inheritedAssociation.OnTargetRemove;
                }
            }
            var association = new AssociationInfo(field, origin.OwnerType, multiplicity, onOwnerRemove, onTargetRemove);

            association.Name = context.NameBuilder.BuildAssociationName(association);
            AssociationInfo existing;

            if (!context.Model.Associations.TryGetValue(association.Name, out existing))
            {
                context.Model.Associations.Add(association);
                association.Ancestors.AddRange(field.Associations);

                var associationsToRemove = field.Associations
                                           .Where(a => a.TargetType == association.TargetType)
                                           .ToList();
                foreach (var toRemove in associationsToRemove)
                {
                    field.Associations.Remove(toRemove);
                }

                field.Associations.Add(association);
            }
        }
        private static IEnumerable <ReferenceInfo> FindReferences(Entity owner, AssociationInfo association, bool reversed)
        {
            Func <Entity, Entity, AssociationInfo, ReferenceInfo> referenceCtor = (o, t, a) => new ReferenceInfo(o, t, a);

            if (reversed)
            {
                association   = association.Reversed;
                referenceCtor = (o, t, a) => new ReferenceInfo(t, o, a);
            }
            switch (association.Multiplicity)
            {
            case Multiplicity.ZeroToOne:
            case Multiplicity.OneToOne:
            case Multiplicity.ManyToOne:
                var target = (Entity)owner.GetFieldValue(association.OwnerField);
                if (target != null && association.TargetType.UnderlyingType.IsAssignableFrom(target.TypeInfo.UnderlyingType))
                {
                    yield return(referenceCtor(owner, target, association));
                }
                break;

            case Multiplicity.ZeroToMany:
            case Multiplicity.OneToMany:
            case Multiplicity.ManyToMany:
                var targets = (EntitySetBase)owner.GetFieldValue(association.OwnerField);
                foreach (var item in targets.Entities)
                {
                    if (association.TargetType.UnderlyingType.IsAssignableFrom(item.TypeInfo.UnderlyingType))
                    {
                        yield return(referenceCtor(owner, (Entity)item, association));
                    }
                }
                break;
            }
        }
        // Constructors

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <param name="referencingEntity">The referencing entity.</param>
        /// <param name="referencedEntity">The referenced entity.</param>
        /// <param name="association">The association.</param>
        public ReferenceInfo(Entity referencingEntity, Entity referencedEntity, AssociationInfo association)
            : this()
        {
            ReferencingEntity = referencingEntity;
            ReferencedEntity  = referencedEntity;
            Association       = association;
        }
示例#7
0
 public SyncAction(Action <AssociationInfo, IEntity, IEntity, SyncContext, RemovalContext> action,
                   AssociationInfo association, IEntity owner, IEntity target)
     : this()
 {
     Action      = action;
     Association = association;
     Owner       = owner;
     Target      = target;
 }
        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);
        }
        /// <summary>
        /// Finds all the entities that reference <paramref name="target"/> entity
        /// via specified <paramref name="association"/>.
        /// </summary>
        /// <param name="target">The entity to find references to.</param>
        /// <param name="association">The association to process.</param>
        /// <returns>
        /// The sequence of <see cref="ReferenceInfo"/> objects.
        /// </returns>
        /// <exception cref="InvalidOperationException">Type doesn't participate in the specified association.</exception>
        public static IEnumerable <ReferenceInfo> GetReferencesTo(Entity target, AssociationInfo association)
        {
            var handler = target.Session.Handler;

            if (!association.TargetType.UnderlyingType.IsAssignableFrom(target.TypeInfo.UnderlyingType))
            {
                throw new InvalidOperationException(
                          String.Format(Strings.TypeXDoesNotParticipateInTheSpecifiedAssociation, target.TypeInfo.Name));
            }
            return(handler.GetReferencesTo(target, association));
        }
 public override void Process(RemovalContext context, AssociationInfo association, Entity removingObject, Entity target, Entity referencingObject, Entity referencedObject)
 {
     switch (association.Multiplicity)
     {
     case Multiplicity.ZeroToMany:
     case Multiplicity.OneToMany:
     case Multiplicity.ManyToMany:
         ReferentialActions.RemoveReference(association, referencingObject, referencedObject, null, context);
         break;
     }
     target.RemoveLaterInternal(EntityRemoveReason.Association);
 }
示例#11
0
        private void TestSerializationForAssociationInfo()
        {
            const string referentialXml = "<end multiplicity=\"Many\" onOwnerRemove=\"Clear\" onTargetRemove=\"Deny\"><pairTo valueType=\"Custom\"><value>Items</value></pairTo></end>";

            AssociationInfo originalAttribute = CreateAssociationInfo(TestAttributeValuesType.Values1);

            string xml = originalAttribute.SerializeToString();

            Assert.IsTrue(Util.StringEqual(xml, referentialXml, true),
                          "Serialized xml form of 'AssociationInfo' type differs from referential xml.");

            TestSerializationForOrmAssociationEnd(originalAttribute.ToOrmAssociationEnd());
        }
示例#12
0
 public static void Dump(this AssociationInfo target, int indent)
 {
     WriteLine(indent, "Referencing type: " + target.OwnerType.Name);
     WriteLine(indent, "Referencing field: " + target.OwnerField.Name);
     WriteLine(indent, "Referenced type: " + target.TargetType.Name);
     WriteLine(indent, "Multiplicity: " + target.Multiplicity);
     WriteLine(indent, "On Delete: " + target.OnTargetRemove);
     WriteLine(indent, "Master: " + target.IsMaster);
     if (target.Reversed != null)
     {
         WriteLine(indent, "Reversed: " + target.Reversed.Name);
     }
 }
示例#13
0
        // Constructors

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        public ReferentialIntegrityException(AssociationInfo association,
                                             Entity initiator,
                                             Entity referencingObject,
                                             Entity referencedObject)
            : base(
                string.Format(Strings.ReferentialIntegrityViolationOnAttemptToRemoveXKeyY,
                              initiator.GetType().GetFullName(), initiator.Key,
                              association, referencingObject.Key, referencedObject.Key))
        {
            Association       = association;
            Initiator         = initiator.Key;
            ReferencingObject = referencingObject.Key;
            ReferencedObject  = referencedObject.Key;
        }
        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);
            }
        }
        private IEnumerable <ReferenceInfo> GetReferencesToInternal(AssociationInfo association, Entity target, RecordSetHeader header, QueryTask queryTask)
        {
            Session.ExecuteInternalDelayedQueries(true);

            var referenceToTarget       = queryTask.ToEntities(header, Session, 0).Where(e => !e.IsRemoved);
            var removedReferences       = Session.NonPairedReferencesRegistry.GetRemovedReferencesTo(target.State, association).Select(es => es.Entity);
            var addedReferences         = Session.NonPairedReferencesRegistry.GetAddedReferenceTo(target.State, association).Select(es => es.Entity).Where(e => !e.IsRemoved);
            var exceptRemovedReferences = referenceToTarget.Except(removedReferences);
            var withNewReferences       = exceptRemovedReferences.Concat(addedReferences);

            foreach (var entity in withNewReferences)
            {
                yield return(new ReferenceInfo(entity, target, association));
            }
        }
        private static StoredAssociationInfo ConvertAssociation(AssociationInfo source)
        {
            var result = new StoredAssociationInfo {
                Name                 = source.Name,
                MappingName          = source.AuxiliaryType != null ? source.AuxiliaryType.MappingName : null,
                ConnectorTypeName    = source.AuxiliaryType != null ? source.AuxiliaryType.Name : null,
                IsMaster             = source.IsMaster,
                MultiplicityName     = source.Multiplicity.ToString(),
                ReferencedTypeName   = source.TargetType.Name,
                ReferencingFieldName = source.OwnerField.Name,
                ReversedName         = source.Reversed != null ? source.Reversed.Name : null,
            };

            return(result);
        }
示例#17
0
        public static void BuildAssociation(BuildingContext context, FieldDef fieldDef, FieldInfo field)
        {
            var referencedType = field.IsEntity ? context.Model.Types[field.ValueType] : context.Model.Types[field.ItemType];
            var multiplicity   = field.IsEntitySet ? Multiplicity.ZeroToMany : Multiplicity.ZeroToOne;
            var association    = new AssociationInfo(
                field, referencedType, multiplicity,
                fieldDef.OnOwnerRemove, fieldDef.OnTargetRemove);

            association.Name = context.NameBuilder.BuildAssociationName(association);
            context.Model.Associations.Add(association);
            field.Associations.Add(association);

            if (!fieldDef.PairTo.IsNullOrEmpty())
            {
                context.PairedAssociations.Add(new Pair <AssociationInfo, string>(association, fieldDef.PairTo));
            }
        }
示例#18
0
        private Type GenerateAuxiliaryType(AssociationInfo association)
        {
            var masterType = association.OwnerType.UnderlyingType;
            var slaveType  = association.TargetType.UnderlyingType;
            var baseType   = WellKnownOrmTypes.EntitySetItemOfT1T2.MakeGenericType(masterType, slaveType);

            var typeName = string.Format(GeneratedTypeNameFormat,
                                         masterType.Namespace,
                                         context.NameBuilder.BuildAssociationName(association));

            var result = GeneratedTypes.GetValue(typeName,
                                                 (_typeName, _baseType) =>
                                                 TypeHelper.CreateInheritedDummyType(_typeName, _baseType, true),
                                                 baseType);

            return(result);
        }
        private void CheckDirection(AssociationInfo association, Entity owner, Entity item, Direction direction, string message)
        {
            var entry = (stack.Count > 0) ? stack.Peek() : null;

            if (entry == null || isNewChain)
            {
                isNewChain = false;
                entry      = new SyncEntry(association, owner, item);
                stack.Push(entry);
            }
            int depth;

            if (entry.CheckDirection(direction, out depth))
            {
                stack.Pop();
            }
            TestLog.Info("{0}", message.Indent(depth * 2));
        }
        public IEnumerable <EntityState> GetRemovedReferencesTo(EntityState target, AssociationInfo association)
        {
            ArgumentValidator.EnsureArgumentNotNull(target, "target");
            ArgumentValidator.EnsureArgumentNotNull(association, "association");

            if (association.IsPaired)
            {
                return(Enumerable.Empty <EntityState>());
            }

            var key = MakeKey(target, association);
            List <EntityState> removedMap;

            if (removedReferences.TryGetValue(key, out removedMap))
            {
                return(removedMap);
            }
            return(EnumerableUtils <EntityState> .Empty);
        }
示例#21
0
 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);
     }
 }
示例#22
0
        public override void Process(RemovalContext context, AssociationInfo association, Entity removingObject, Entity target, Entity referencingObject, Entity referencedObject)
        {
            switch (association.Multiplicity)
            {
            case Multiplicity.ZeroToOne:
            case Multiplicity.OneToOne:
                ReferentialActions.ClearReference(association, referencingObject, null, null, context);
                break;

            case Multiplicity.ManyToOne:
                ReferentialActions.RemoveReference(association.Reversed, referencedObject, referencingObject, null, context);
                break;

            case Multiplicity.ZeroToMany:
            case Multiplicity.OneToMany:
            case Multiplicity.ManyToMany:
                ReferentialActions.RemoveReference(association, referencingObject, referencedObject, null, context);
                break;
            }
        }
示例#23
0
        public static void Connect(ModelElement sourceElement, ModelElement targetElement)
        {
            PersistentType sourceEntity = (PersistentType)sourceElement;
            PersistentType targetEntity = (PersistentType)targetElement;

            if (sourceEntity is TypedEntitySet && targetEntity is Interface)
            {
                TypedEntitySetHasItemType connection = new TypedEntitySetHasItemType((TypedEntitySet)sourceEntity, (Interface)targetEntity);

                if (DomainClassInfo.HasNameProperty(connection))
                {
                    DomainClassInfo.SetUniqueName(connection);
                }

                return;
            }

            IAssociationInfo sourceInfo = new AssociationInfo
            {
                Multiplicity   = MultiplicityKind.One,
                OnOwnerRemove  = AssociationOnRemoveAction.Default,
                OnTargetRemove = AssociationOnRemoveAction.Default
            };

            sourceInfo.PairTo.SetAsCustom(BuildNavigationPropertyName(sourceEntity, targetEntity, true));

            IAssociationInfo targetInfo = new AssociationInfo
            {
                Multiplicity   = MultiplicityKind.Many,
                OnOwnerRemove  = AssociationOnRemoveAction.Default,
                OnTargetRemove = AssociationOnRemoveAction.Default
            };

            targetInfo.PairTo.SetAsCustom(BuildNavigationPropertyName(sourceEntity, targetEntity, false));

            string associationName = BuildAssociationName(sourceEntity, targetEntity);

            CreatePersistentTypesAssociation(sourceElement, targetElement, sourceInfo, targetInfo, associationName,
                                             true, true);
        }
        /// <summary>
        /// Gets the references to specified entity.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <param name="association">The association.</param>
        /// <returns>References.</returns>
        public virtual IEnumerable <ReferenceInfo> GetReferencesTo(Entity target, AssociationInfo association)
        {
            if (association.IsPaired)
            {
                return(FindReferences(target, association, true));
            }
            object key = new Pair <object, AssociationInfo>(CachingRegion, association);
            Func <object, object> generator = p => BuildReferencingQuery(((Pair <object, AssociationInfo>)p).Second);
            var pair             = (Pair <CompilableProvider, Parameter <Tuple> >)Session.StorageNode.InternalQueryCache.GetOrAdd(key, generator);
            var recordSet        = pair.First;
            var parameter        = pair.Second;
            var parameterContext = new ParameterContext();

            parameterContext.SetValue(parameter, target.Key.Value);
            ExecutableProvider executableProvider = Session.Compile(recordSet);

            var queryTask = new QueryTask(executableProvider, Session.GetLifetimeToken(), parameterContext);

            Session.RegisterInternalDelayedQuery(queryTask);

            return(GetReferencesToInternal(association, target, recordSet.Header, queryTask));
        }
示例#25
0
        public static void BuildAssociation(BuildingContext context, AssociationInfo origin, FieldInfo field)
        {
            var association = new AssociationInfo(field, origin.TargetType, origin.Multiplicity, origin.OnOwnerRemove, origin.OnTargetRemove);

            association.Name = context.NameBuilder.BuildAssociationName(association);
            context.Model.Associations.Add(association);
            var associationsToRemove = field.Associations
                                       .Where(a => a.TargetType == association.TargetType)
                                       .ToList();

            foreach (var toRemove in associationsToRemove)
            {
                field.Associations.Remove(toRemove);
            }
            field.Associations.Add(association);

            var pairTo = context.PairedAssociations.Where(p => p.First == origin).FirstOrDefault();

            if (pairTo.First != null)
            {
                context.PairedAssociations.Add(new Pair <AssociationInfo, string>(association, pairTo.Second));
            }
        }
示例#26
0
        private void TryAddForeignKeyIndex(AssociationInfo association)
        {
            if (!association.Multiplicity.In(Multiplicity.OneToOne, Multiplicity.ZeroToOne))
            {
                return;
            }
            var typeDef = context.ModelDef.Types[association.OwnerType.UnderlyingType];
            var field   = association.OwnerField;

            if ((field.Attributes & FieldAttributes.NotIndexed) != 0 ||
                (field.Attributes.HasFlag(FieldAttributes.PrimaryKey)))
            {
                return;
            }
            bool addIndex = true;

            while (field.Parent != null)
            {
                field    = field.Parent;
                addIndex = addIndex && field.IsStructure;
            }
            if (!addIndex)
            {
                return;
            }
            Func <IndexDef, bool> isIndexForField = i => i.IsSecondary && i.KeyFields.Count == 1 && i.KeyFields[0].Key == association.OwnerField.Name;

            if (typeDef.Indexes.Any(isIndexForField))
            {
                return;
            }
            var attribute = new IndexAttribute(association.OwnerField.Name);
            var indexDef  = modelDefBuilder.DefineIndex(typeDef, attribute);

            typeDef.Indexes.Add(indexDef);
        }
        /// <inheritdoc/>
        protected override IPathNode VisitAssociationInfo(AssociationInfo association)
        {
            // Skip associations that do not impose constraints
            if (association.OnTargetRemove == OnRemoveAction.None)
            {
                return(null);
            }

            if (association.AuxiliaryType == null && !association.OwnerField.IsEntitySet)
            {
                if (!IsValidForeignKeyTarget(association.TargetType))
                {
                    return(null);
                }
                if (association.OwnerType.IsInterface)
                {
                    foreach (var implementorType in association.OwnerType.GetImplementors().SelectMany(GetForeignKeyOwners))
                    {
                        var implementorField = implementorType.FieldMap[association.OwnerField];
                        ProcessDirectAssociation(implementorType, implementorField, association.TargetType);
                    }
                }
                else
                {
                    foreach (var type in GetForeignKeyOwners(association.OwnerField.DeclaringType))
                    {
                        ProcessDirectAssociation(type, association.OwnerField, association.TargetType);
                    }
                }
            }
            else if (association.AuxiliaryType != null && association.IsMaster)
            {
                ProcessIndirectAssociation(association.AuxiliaryType);
            }
            return(null);
        }
示例#28
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;
            }
        }
 /// <summary>
 /// Gets the references from specified entity.
 /// </summary>
 /// <param name="owner">The owner.</param>
 /// <param name="association">The association.</param>
 /// <returns>References.</returns>
 public virtual IEnumerable <ReferenceInfo> GetReferencesFrom(Entity owner, AssociationInfo association)
 {
     return(FindReferences(owner, association, false));
 }
        /// <summary>
        /// Returns the association information for the specified navigation property.
        /// </summary>
        /// <param name="navigationProperty">The navigation property to return association information for</param>
        /// <returns>The association info</returns>
        internal AssociationInfo GetAssociationInfo(NavigationProperty navigationProperty)
        {
            return _associationMap.GetOrAdd(
                navigationProperty.RelationshipType.FullName,
                associationName => {
                    var associationType = (AssociationType) navigationProperty.RelationshipType;

                    if (!associationType.ReferentialConstraints.Any()) {
                        // We only support EF models where FK info is part of the model.
                        throw Error.NotSupported(Resource.LinqToEntitiesProvider_UnableToRetrieveAssociationInfo, associationName);
                    }

                    var toRoleName = associationType.ReferentialConstraints[0].ToRole.Name;
                    var associationInfo = new AssociationInfo {
                        FKRole = toRoleName,
                        Name = GetAssociationName(navigationProperty, toRoleName),
                        ThisKey = associationType.ReferentialConstraints[0].ToProperties.Select(p => p.Name).ToArray(),
                        OtherKey = associationType.ReferentialConstraints[0].FromProperties.Select(p => p.Name).ToArray(),
                        IsRequired = associationType.RelationshipEndMembers[0].RelationshipMultiplicity == RelationshipMultiplicity.One
                    };

                    return associationInfo;
                });
        }
        private void DropStructureWithNestedStructures(Session session)
        {
            var referencedEntity1 = new EntityReferencedFromStructure();
            var referencedEntity2 = new EntityReferencedFromStructure();

            var entity1 = new EntityWithStructure {
                Structure = new ZeroLevelStructure {
                    EntityField         = referencedEntity1,
                    FirstLevelStructure = new FirstLevelStructure {
                        EntityField          = referencedEntity1,
                        SecondLevelStructure = new SecondLevelStructure {
                            EntityField = referencedEntity1
                        }
                    }
                }
            };
            var entity2 = new EntityWithStructure {
                Structure = new ZeroLevelStructure {
                    EntityField         = referencedEntity2,
                    FirstLevelStructure = new FirstLevelStructure {
                        EntityField          = referencedEntity2,
                        SecondLevelStructure = new SecondLevelStructure {
                            EntityField = referencedEntity2
                        }
                    }
                }
            };

            var associations = new AssociationInfo[3];
            var typeInfo     = entity1.TypeInfo;

            associations[0] = typeInfo.Fields["Structure.EntityField"].Associations.First();
            associations[1] = typeInfo.Fields["Structure.FirstLevelStructure.EntityField"].Associations.First();
            associations[2] = typeInfo.Fields["Structure.FirstLevelStructure.SecondLevelStructure.EntityField"].Associations.First();
            Assert.That(associations.All(a => !a.IsPaired && a.TargetType == referencedEntity1.TypeInfo && a.OwnerType == entity1.TypeInfo));

            Assert.That(session.NonPairedReferencesRegistry.AddedReferencesCount, Is.EqualTo(6));
            Assert.That(session.NonPairedReferencesRegistry.RemovedReferencesCount, Is.EqualTo(0));
            var references = ReferenceFinder.GetReferencesTo(referencedEntity1).ToList();

            Assert.That(references.Count, Is.EqualTo(3));
            Assert.That(references.Any(r => r.Association == associations[0] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.True);
            Assert.That(references.Any(r => r.Association == associations[1] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.True);
            Assert.That(references.Any(r => r.Association == associations[2] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.True);

            references = ReferenceFinder.GetReferencesTo(referencedEntity2).ToList();
            Assert.That(references.Count, Is.EqualTo(3));
            Assert.That(references.Any(r => r.Association == associations[0] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);
            Assert.That(references.Any(r => r.Association == associations[1] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);
            Assert.That(references.Any(r => r.Association == associations[2] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);

            entity1.Structure.FirstLevelStructure.SecondLevelStructure.EntityField = null;
            entity1.Structure.FirstLevelStructure.EntityField = null;
            entity1.Structure.EntityField = null;
            Assert.That(session.NonPairedReferencesRegistry.AddedReferencesCount, Is.EqualTo(3));
            Assert.That(session.NonPairedReferencesRegistry.RemovedReferencesCount, Is.EqualTo(0));
            references = ReferenceFinder.GetReferencesTo(referencedEntity1).ToList();
            Assert.That(references.Count, Is.EqualTo(0));
            Assert.That(references.Any(r => r.Association == associations[0] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.False);
            Assert.That(references.Any(r => r.Association == associations[1] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.False);
            Assert.That(references.Any(r => r.Association == associations[2] && r.ReferencingEntity == entity1 && r.ReferencedEntity == referencedEntity1), Is.False);

            references = ReferenceFinder.GetReferencesTo(referencedEntity2).ToList();
            Assert.That(references.Count, Is.EqualTo(3));
            Assert.That(references.Any(r => r.Association == associations[0] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);
            Assert.That(references.Any(r => r.Association == associations[1] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);
            Assert.That(references.Any(r => r.Association == associations[2] && r.ReferencingEntity == entity2 && r.ReferencedEntity == referencedEntity2), Is.True);
        }