/// <summary> /// Make association property metadata for the entity. /// Also populates the ForeignKeyMap which is used for related-entity fixup in NHContext.FixupRelationships /// </summary> /// <param name="containingPersister">Entity Persister containing the property</param> /// <param name="propType">Association property</param> /// <param name="propName">Name of the property</param> /// <param name="dataProperties">Data properties already collected for the containingType. "isPartOfKey" may be added to a property.</param> /// <param name="isKey">Whether the property is part of the key</param> /// <returns></returns> private Dictionary <string, object> MakeAssociationProperty(AbstractEntityPersister containingPersister, IAssociationType propType, string propName, List <Dictionary <string, object> > dataProperties, bool isKey) { var nmap = new Dictionary <string, object>(); nmap.Add("nameOnServer", propName); var relatedEntityType = GetEntityType(propType.ReturnedClass, propType.IsCollectionType); nmap.Add("entityTypeName", relatedEntityType.Name + ":#" + relatedEntityType.Namespace); nmap.Add("isScalar", !propType.IsCollectionType); // the associationName must be the same at both ends of the association. Type containingType = containingPersister.EntityMetamodel.Type; string[] columnNames = GetPropertyColumnNames(containingPersister, propName, propType); nmap.Add("associationName", GetAssociationName(containingType.Name, relatedEntityType.Name, columnNames)); var propertyIndex = containingPersister.PropertyNames.ToList().IndexOf(propName); var cascadeStyle = containingPersister.PropertyCascadeStyles[propertyIndex]; nmap.Add("hasOrphanDelete", cascadeStyle.HasOrphanDelete); string[] fkNames = null; var joinable = propType.GetAssociatedJoinable((ISessionFactoryImplementor)this._sessionFactory); var memberConfiguration = GetMemberConfiguration(containingType, propName); if (!string.IsNullOrEmpty(memberConfiguration.SerializedName)) { nmap.Add("name", memberConfiguration.SerializedName); } if (propType.IsCollectionType) { // inverse foreign key var collectionPersister = joinable as AbstractCollectionPersister; if (collectionPersister != null) { // many-to-many relationships do not have a direct connection on the client or in metadata var elementPersister = collectionPersister.ElementPersister as AbstractEntityPersister; if (elementPersister != null) { fkNames = GetPropertyNamesForColumns(elementPersister, columnNames); if (fkNames != null) { nmap.Add("invForeignKeyNamesOnServer", fkNames); } } } } else { // Not a collection type - a many-to-one or one-to-one association var entityRelationship = containingType.FullName + '.' + propName; // Look up the related foreign key name using the column name fkNames = GetPropertyNamesForColumns(containingPersister, columnNames); // TODO: support FK with multiple columns if (fkNames == null && columnNames.Length == 1) //try to find the column for the relation if is not specified in class { var pType = containingPersister.GetPropertyType(propName); var index = containingPersister.PropertyNames .Select((val, i) => new { Index = i, Value = val }) .Where(o => o.Value == propName) .Select(o => o.Index) .First(); var isNullable = containingPersister.PropertyNullability[index]; var convertedColumns = new List <string>(); foreach (var columnName in columnNames) { // TODO: convert columnName to c# name var synProp = new NHSyntheticProperty { Name = columnName, FkPropertyName = propName, FkType = pType, IsNullable = isNullable }; convertedColumns.Add(columnName); var relatedType = _sessionFactory.GetClassMetadata(synProp.FkType.Name); if (relatedType == null) { throw new ArgumentException("Could not find related entity of type " + synProp.FkType.Name); } synProp.PkType = relatedType.IdentifierType; synProp.PkPropertyName = relatedType.IdentifierPropertyName; if (!_syntheticProperties.ContainsKey(containingType)) { _syntheticProperties.Add(containingType, new List <NHSyntheticProperty>()); } _syntheticProperties[containingType].Add(synProp); //Create synthetic property as unmapped var dmap = MakeDataProperty(memberConfiguration, columnName, relatedType.IdentifierType, synProp.IsNullable, false, false); dmap["isUnmapped"] = true; dataProperties.Add(dmap); } fkNames = convertedColumns.ToArray(); } if (fkNames != null) { if (propType.ForeignKeyDirection == ForeignKeyDirection.ForeignKeyFromParent) { nmap.Add("foreignKeyNamesOnServer", fkNames); } else { nmap.Add("invForeignKeyNamesOnServer", fkNames); } // For many-to-one and one-to-one associations, save the relationship in ForeignKeyMap for re-establishing relationships during save _map.ForeignKeyMap.Add(entityRelationship, string.Join(",", fkNames)); if (isKey) { foreach (var fkName in fkNames) { var relatedDataProperty = FindPropertyByName(dataProperties, fkName); if (!relatedDataProperty.ContainsKey("isPartOfKey")) { relatedDataProperty.Add("isPartOfKey", true); } } } } else if (fkNames == null) { nmap.Add("foreignKeyNamesOnServer", columnNames); nmap.Add("ERROR", "Could not find matching fk for property " + entityRelationship); _map.ForeignKeyMap.Add(entityRelationship, string.Join(",", columnNames)); throw new ArgumentException("Could not find matching fk for property " + entityRelationship); } } return(nmap); }
public NHSyntheticPropertyValueProvider(NHSyntheticProperty syntheticProp) { _syntheticProp = syntheticProp; }