/// <summary> /// Generates code (AST) for single association in both directions using properties and/or extension methods. /// </summary> /// <param name="association">Association model.</param> /// <param name="extensionAssociations">Association extensions region provider.</param> /// <param name="entityAssociations">Assocation property groups for each entity.</param> /// <param name="extensionEntityAssociations">Association methods groupsfor each entity.</param> private void BuildAssociation( AssociationModel association, Func <RegionGroup> extensionAssociations, Dictionary <EntityModel, PropertyGroup> entityAssociations, Dictionary <EntityModel, MethodGroup> extensionEntityAssociations) { // get source and target entities for association // source is foreign key source table // target is foreign key target table if (!_entityBuilders.TryGetValue(association.Source, out var sourceBuilder) || !_entityBuilders.TryGetValue(association.Target, out var targetBuilder)) { // data model misconfiguration (e.g. entity was removed from model, but not its associations) throw new InvalidOperationException("Discovered association that connects tables, missing from current model."); } // build metadata keys for association // (add fields used to define assocation to metadata) BuildAssociationMetadataKey(association, false); BuildAssociationMetadataKey(association, true); // Type of assocation on source side. // Nullable for nullable foreign key. var sourceType = targetBuilder.Type.Type.WithNullability(association.SourceMetadata.CanBeNull); // Type of association on target side. var tagetType = sourceBuilder.Type.Type; if (association.ManyToOne) { // for many-to-one assocations has collection type, defined by user preferences if (_options.DataModel.AssociationCollectionAsArray) { tagetType = AST.ArrayType(tagetType, false); } else if (_dataModel.AssociationCollectionType != null) { tagetType = _dataModel.AssociationCollectionType.WithTypeArguments(tagetType); } else { // default type is IEnumerable tagetType = WellKnownTypes.System.Collections.Generic.IEnumerable(tagetType); } } else // if one-to-one association targets nullable columns, association is nullable { tagetType = tagetType.WithNullability(true); } // (if enabled) generate property-based association from source to target if (association.Property != null) { BuildAssociationProperty( entityAssociations, association.Source, sourceType, association.Property, association.SourceMetadata); } // (if enabled) generate property-based back-reference association from target to source if (association.BackreferenceProperty != null) { BuildAssociationProperty( entityAssociations, association.Target, tagetType, association.BackreferenceProperty, association.TargetMetadata); } // (if enabled) generate extension-based association from source to target if (association.Extension != null) { BuildAssociationExtension( extensionEntityAssociations, extensionAssociations, sourceBuilder, targetBuilder, sourceType, association.Extension, association.SourceMetadata, association, false); } // (if enabled) generate extension-based back-reference association from target to source if (association.BackreferenceExtension != null) { BuildAssociationExtension( extensionEntityAssociations, extensionAssociations, targetBuilder, sourceBuilder, tagetType, association.BackreferenceExtension, association.TargetMetadata, association, true); } }