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); } }
/// <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))); }
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"); } }
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; }
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); }
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()); }
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); } }
// 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); }
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)); } }
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); }
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); } }
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; } }
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)); }
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)); } }
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); }
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); }