/// <summary> /// Add on association (one-to-one, many-to-one, or a collection) to a list /// of associations to be fetched by outerjoin /// </summary> private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhsColumns, string alias, string path, int currentDepth, JoinType joinType) { IJoinable joinable = type.GetAssociatedJoinable(Factory); string subalias = GenerateTableAlias(associations.Count + 1, path, joinable); OuterJoinableAssociation assoc = new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters); assoc.ValidateJoin(path); AddAssociation(subalias, assoc); int nextDepth = currentDepth + 1; if (!joinable.IsCollection) { IOuterJoinLoadable pjl = joinable as IOuterJoinLoadable; if (pjl != null) { WalkEntityTree(pjl, subalias, path, nextDepth); } } else { IQueryableCollection qc = joinable as IQueryableCollection; if (qc != null) { WalkCollectionTree(qc, subalias, path, nextDepth); } } }
private static void ProcessAssociationType( IAssociationType associationType, ISessionFactoryImplementor sessionFactory, MemberMetadata member, System.Type convertType, out IType memberType, out IEntityPersister memberPersister, out IAbstractComponentType memberComponent) { if (associationType.IsCollectionType) { // Check manually for entity association as GetAssociatedEntityName throws when there is none. var queryableCollection = (IQueryableCollection)associationType.GetAssociatedJoinable(sessionFactory); if (!queryableCollection.ElementType.IsEntityType) // q.OneToManyCompositeElement[0].Member, q.OneToManyElement[0].Member { memberPersister = null; // Can be <composite-element> or <element> switch (queryableCollection.ElementType) { case IAbstractComponentType componentType: // q.OneToManyCompositeElement[0].Member memberComponent = componentType; memberType = TryGetComponentPropertyType(componentType, member.Path); return; default: // q.OneToManyElement[0].Member memberType = null; memberComponent = null; return; } } // q.OneToMany[0].Member TryGetEntityPersister( associationType.GetAssociatedEntityName(sessionFactory), convertType, sessionFactory, out memberPersister); } else if (associationType.IsAnyType) { // ((Address)q.AnyType).Member, q.AnyType.Member // Unfortunately we cannot detect the exact entity name as cast does not provide it, // so the only option is to guess it. TryGetEntityPersister(convertType, sessionFactory, out memberPersister); } else // q.ManyToOne.Member { TryGetEntityPersister( associationType.GetAssociatedEntityName(sessionFactory), convertType, sessionFactory, out memberPersister); } memberComponent = null; memberType = memberPersister != null ? memberPersister.EntityMetamodel.GetPropertyType(member.Path) : null; // q.AnyType.Member, ((NotMappedClass)q.ManyToOne) }
public Join(ISessionFactoryImplementor factory, IAssociationType associationType, string alias, JoinType joinType, string[] lhsColumns) { this.associationType = associationType; this.joinable = associationType.GetAssociatedJoinable(factory); this.alias = alias; this.joinType = joinType; this.lhsColumns = lhsColumns; }
private Persister.Entity.IJoinable GetPathJoinable(string path) { // start with the root IJoinable last = rootPersister; var tokens = path.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { return(last); } IPropertyMapping lastEntity = rootPersister; int i = 0; if (entityJoins.TryGetValue(tokens[0], out var entityJoinInfo)) { last = entityJoinInfo.Persister; lastEntity = (IPropertyMapping)last; i++; } string componentPath = string.Empty; for (; i < tokens.Length; i++) { componentPath += tokens[i]; IType type = lastEntity.ToType(componentPath); if (type.IsAssociationType) { if (type.IsCollectionType) { // ignore joinables for composite collections var collectionType = (CollectionType)type; var persister = Factory.GetCollectionPersister(collectionType.Role); if (persister.ElementType.IsEntityType == false) { return(null); } } IAssociationType atype = (IAssociationType)type; last = atype.GetAssociatedJoinable(Factory); lastEntity = (NHibernate_Persister_Entity.IPropertyMapping)Factory.GetEntityPersister(atype.GetAssociatedEntityName(Factory)); componentPath = ""; } else if (type.IsComponentType) { componentPath += '.'; } else { throw new QueryException("not an association: " + componentPath); } } return(last); }
public Join(ISessionFactoryImplementor factory, IAssociationType associationType, string alias, JoinType joinType, string[] lhsColumns) { this.associationType = associationType; this.joinable = associationType.GetAssociatedJoinable(factory); this.alias = alias; this.joinType = joinType; this.lhsColumns = lhsColumns; this.rhsColumns = lhsColumns.Length > 0 ? JoinHelper.GetRHSColumnNames(joinable, associationType) : Array.Empty <string>(); }
/// <summary> /// Get the columns of the associated table which are to /// be used in the join /// </summary> public static string[] GetRHSColumnNames(IAssociationType type, ISessionFactoryImplementor factory) { string uniqueKeyPropertyName = type.RHSUniqueKeyPropertyName; IJoinable joinable = type.GetAssociatedJoinable(factory); if (uniqueKeyPropertyName == null) { return joinable.JoinColumnNames; } else { return ((IOuterJoinLoadable)joinable).GetPropertyColumnNames(uniqueKeyPropertyName); } }
public OuterJoinableAssociation(IAssociationType joinableType, String lhsAlias, String[] lhsColumns, String rhsAlias, JoinType joinType, ISessionFactoryImplementor factory, IDictionary <string, IFilter> enabledFilters) { this.joinableType = joinableType; this.lhsAlias = lhsAlias; this.lhsColumns = lhsColumns; this.rhsAlias = rhsAlias; this.joinType = joinType; joinable = joinableType.GetAssociatedJoinable(factory); rhsColumns = JoinHelper.GetRHSColumnNames(joinableType, factory); on = joinableType.GetOnCondition(rhsAlias, factory, enabledFilters); this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application }
public OuterJoinableAssociation(IAssociationType joinableType, String lhsAlias, String[] lhsColumns, String rhsAlias, JoinType joinType, ISessionFactoryImplementor factory, IDictionary<string, IFilter> enabledFilters) { this.joinableType = joinableType; this.lhsAlias = lhsAlias; this.lhsColumns = lhsColumns; this.rhsAlias = rhsAlias; this.joinType = joinType; joinable = joinableType.GetAssociatedJoinable(factory); rhsColumns = JoinHelper.GetRHSColumnNames(joinableType, factory); on = joinableType.GetOnCondition(rhsAlias, factory, enabledFilters); this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application }
public override string SelectFragment(IJoinable rhs, string rhsAlias, string lhsAlias, string collectionSuffix, bool includeCollectionColumns, EntityLoadInfo entityInfo) { // we need to determine the best way to know that two joinables // represent a single many-to-many... if (rhs != null && IsManyToMany && !rhs.IsCollection) { IAssociationType elementType = (IAssociationType)ElementType; if (rhs.Equals(elementType.GetAssociatedJoinable(Factory))) { return(ManyToManySelectFragment(rhs, rhsAlias, lhsAlias, collectionSuffix, elementType)); } } return(includeCollectionColumns ? SelectFragment(lhsAlias, collectionSuffix) : string.Empty); }
/// <summary> /// Get the columns of the associated table which are to /// be used in the join /// </summary> public static string[] GetRHSColumnNames(IAssociationType type, ISessionFactoryImplementor factory) { string uniqueKeyPropertyName = type.RHSUniqueKeyPropertyName; IJoinable joinable = type.GetAssociatedJoinable(factory); if (uniqueKeyPropertyName == null) { return(joinable.JoinColumnNames); } else { return(((IOuterJoinLoadable)joinable).GetPropertyColumnNames(uniqueKeyPropertyName)); } }
public OuterJoinableAssociation(IAssociationType joinableType, String lhsAlias, String[] lhsColumns, String rhsAlias, JoinType joinType, SqlString withClause, ISessionFactoryImplementor factory, IDictionary<string, IFilter> enabledFilters) { this.joinableType = joinableType; this.lhsAlias = lhsAlias; this.lhsColumns = lhsColumns; this.rhsAlias = rhsAlias; this.joinType = joinType; joinable = joinableType.GetAssociatedJoinable(factory); rhsColumns = JoinHelper.GetRHSColumnNames(joinableType, factory); on = new SqlString(joinableType.GetOnCondition(rhsAlias, factory, enabledFilters)); if (StringHelper.IsNotEmpty(withClause)) on = on.Append(" and ( ").Append(withClause).Append(" )"); this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application }
public OuterJoinableAssociation(IAssociationType joinableType, String lhsAlias, String[] lhsColumns, String rhsAlias, JoinType joinType, SqlString withClause, ISessionFactoryImplementor factory, IDictionary <string, IFilter> enabledFilters) { this.joinableType = joinableType; this.lhsAlias = lhsAlias; this.lhsColumns = lhsColumns; this.rhsAlias = rhsAlias; this.joinType = joinType; joinable = joinableType.GetAssociatedJoinable(factory); rhsColumns = JoinHelper.GetRHSColumnNames(joinableType, factory); on = new SqlString(joinableType.GetOnCondition(rhsAlias, factory, enabledFilters)); if (SqlStringHelper.IsNotEmpty(withClause)) { on = on.Append(" and ( ").Append(withClause).Append(" )"); } this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application }
/// <summary> /// Used to detect circularities in the joined graph, note that /// this method is side-effecty /// </summary> protected virtual bool IsDuplicateAssociation(string lhsTable, string[] lhsColumnNames, IAssociationType type) { string foreignKeyTable; string[] foreignKeyColumns; if (type.ForeignKeyDirection.Equals(ForeignKeyDirection.ForeignKeyFromParent)) { foreignKeyTable = lhsTable; foreignKeyColumns = lhsColumnNames; } else { foreignKeyTable = type.GetAssociatedJoinable(Factory).TableName; foreignKeyColumns = JoinHelper.GetRHSColumnNames(type, Factory); } return(IsDuplicateAssociation(foreignKeyTable, foreignKeyColumns)); }
private Persister.Entity.IJoinable GetPathJoinable(string path) { NHibernate_Persister_Entity.IJoinable last = (NHibernate_Persister_Entity.IJoinable)Factory.GetEntityPersister(rootEntityName); NHibernate_Persister_Entity.IPropertyMapping lastEntity = (NHibernate_Persister_Entity.IPropertyMapping)last; string componentPath = ""; StringTokenizer tokens = new StringTokenizer(path, ".", false); foreach (string token in tokens) { componentPath += token; IType type = lastEntity.ToType(componentPath); if (type.IsAssociationType) { if (type.IsCollectionType) { // ignore joinables for composite collections var collectionType = (CollectionType)type; var persister = Factory.GetCollectionPersister(collectionType.Role); if (persister.ElementType.IsEntityType == false) { return(null); } } IAssociationType atype = (IAssociationType)type; last = atype.GetAssociatedJoinable(Factory); lastEntity = (NHibernate_Persister_Entity.IPropertyMapping)Factory.GetEntityPersister(atype.GetAssociatedEntityName(Factory)); componentPath = ""; } else if (type.IsComponentType) { componentPath += '.'; } else { throw new QueryException("not an association: " + componentPath); } } return(last); }
/// <summary> /// Add on association (one-to-one, many-to-one, or a collection) to a list /// of associations to be fetched by outerjoin /// </summary> private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhsColumns, string alias, string path, string pathAlias, int currentDepth, JoinType joinType) { IJoinable joinable = type.GetAssociatedJoinable(Factory); string subalias = GenerateTableAlias(associations.Count + 1, path, pathAlias, joinable); var qc = joinable.IsCollection ? (IQueryableCollection)joinable : null; var assoc = InitAssociation( new OuterJoinableAssociation( type, alias, aliasedLhsColumns, subalias, joinType, //for many-to-many with clause is applied with OuterJoinableAssociation created for entity persister so simply skip it here qc?.IsManyToMany == true ? null :GetWithClause(path, pathAlias), Factory, enabledFilters, GetSelectMode(path)), path); assoc.ValidateJoin(path); AddAssociation(assoc); int nextDepth = currentDepth + 1; if (qc == null) { IOuterJoinLoadable pjl = joinable as IOuterJoinLoadable; if (pjl != null) { WalkEntityTree(pjl, subalias, path, nextDepth); } } else { WalkCollectionTree(qc, subalias, path, pathAlias, nextDepth); } }
/// <summary> /// Make association property metadata for the entity. /// Also populates the _fkMap which is used for related-entity fixup in NHContext.FixupRelationships /// </summary> /// <param name="propType"></param> /// <param name="propName"></param> /// <param name="pClass"></param> /// <param name="relatedDataPropertyMap"></param> /// <returns></returns> private Dictionary <string, object> MakeAssociationProperty(Type containingType, IAssociationType propType, string propName, string columnNames, PersistentClass pClass, Dictionary <string, Dictionary <string, object> > relatedDataPropertyMap, 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. nmap.Add("associationName", GetAssociationName(containingType.Name, relatedEntityType.Name, (propType is OneToOneType))); // look up the related foreign key name using the column name Dictionary <string, object> relatedDataProperty = null; string fkName = null; if (relatedDataPropertyMap.TryGetValue(columnNames, out relatedDataProperty)) { fkName = (string)relatedDataProperty["nameOnServer"]; if (propType.ForeignKeyDirection == ForeignKeyDirection.ForeignKeyFromParent) { nmap.Add("foreignKeyNamesOnServer", new string[] { fkName }); } else { // inverse foreign key // many-to-many relationships do not have a direct connection on the client or in metadata var joinable = propType.GetAssociatedJoinable((ISessionFactoryImplementor)this._sessionFactory) as AbstractCollectionPersister; if (joinable == null || !joinable.IsManyToMany) { nmap.Add("invForeignKeyNamesOnServer", new string[] { fkName }); } } } // For many-to-one and one-to-one associations, save the relationship in _fkMap for re-establishing relationships during save if (!propType.IsCollectionType) { var entityRelationship = pClass.EntityName + '.' + propName; if (relatedDataProperty != null) { _fkMap.Add(entityRelationship, fkName); if (isKey) { if (!relatedDataProperty.ContainsKey("isPartOfKey")) { relatedDataProperty.Add("isPartOfKey", true); } } } else { nmap.Add("foreignKeyNamesOnServer", columnNames); nmap.Add("ERROR", "Could not find matching fk for property " + entityRelationship); _fkMap.Add(entityRelationship, columnNames); throw new ArgumentException("Could not find matching fk for property " + entityRelationship); } } return(nmap); }
/// <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); }
/// <summary> /// Make association property metadata for the entity. /// Also populates the _fkMap which is used for related-entity fixup in NHContext.FixupRelationships /// </summary> /// <param name="propType"></param> /// <param name="propName"></param> /// <param name="pClass"></param> /// <param name="relatedDataPropertyMap"></param> /// <returns></returns> private Dictionary<string, object> MakeAssociationProperty(Type containingType, IAssociationType propType, string propName, string columnNames, PersistentClass pClass, Dictionary<string, Dictionary<string, object>> relatedDataPropertyMap, 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. nmap.Add("associationName", GetAssociationName(containingType.Name, relatedEntityType.Name, (propType is OneToOneType))); // look up the related foreign key name using the column name Dictionary<string, object> relatedDataProperty = null; string fkName = null; if (relatedDataPropertyMap.TryGetValue(columnNames, out relatedDataProperty)) { fkName = (string)relatedDataProperty["nameOnServer"]; if (propType.ForeignKeyDirection == ForeignKeyDirection.ForeignKeyFromParent) { nmap.Add("foreignKeyNamesOnServer", new string[] { fkName }); } else { // inverse foreign key // many-to-many relationships do not have a direct connection on the client or in metadata var joinable = propType.GetAssociatedJoinable((ISessionFactoryImplementor)this._sessionFactory) as AbstractCollectionPersister; if (joinable == null || !joinable.IsManyToMany) nmap.Add("invForeignKeyNamesOnServer", new string[] { fkName }); } } // For many-to-one and one-to-one associations, save the relationship in _fkMap for re-establishing relationships during save if (!propType.IsCollectionType) { var entityRelationship = pClass.EntityName + '.' + propName; if (relatedDataProperty != null) { _fkMap.Add(entityRelationship, fkName); if (isKey) { if (!relatedDataProperty.ContainsKey("isPartOfKey")) { relatedDataProperty.Add("isPartOfKey", true); } } } else { nmap.Add("foreignKeyNamesOnServer", columnNames); nmap.Add("ERROR", "Could not find matching fk for property " + entityRelationship); _fkMap.Add(entityRelationship, columnNames); throw new ArgumentException("Could not find matching fk for property " + entityRelationship); } } return nmap; }
/// <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)); string[] fkNames = null; var joinable = propType.GetAssociatedJoinable((ISessionFactoryImplementor)this._sessionFactory); if (propType.IsCollectionType) { // inverse foreign key var collectionPersister = joinable as AbstractCollectionPersister; if (collectionPersister != null) { if (collectionPersister.IsManyToMany || collectionPersister.IsOneToMany) { // 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); 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); }
/// <summary> /// Get the columns of the associated table which are to /// be used in the join /// </summary> public static string[] GetRHSColumnNames(IAssociationType type, ISessionFactoryImplementor factory) { return(GetRHSColumnNames(type.GetAssociatedJoinable(factory), type)); }
/// <summary> /// Add on association (one-to-one, many-to-one, or a collection) to a list /// of associations to be fetched by outerjoin /// </summary> private void AddAssociationToJoinTree(IAssociationType type, string[] aliasedLhsColumns, string alias, string path, int currentDepth, JoinType joinType) { IJoinable joinable = type.GetAssociatedJoinable(Factory); string subalias = GenerateTableAlias(associations.Count + 1, path, joinable); OuterJoinableAssociation assoc = new OuterJoinableAssociation(type, alias, aliasedLhsColumns, subalias, joinType, GetWithClause(path), Factory, enabledFilters); assoc.ValidateJoin(path); AddAssociation(subalias, assoc); int nextDepth = currentDepth + 1; if (!joinable.IsCollection) { IOuterJoinLoadable pjl = joinable as IOuterJoinLoadable; if (pjl != null) WalkEntityTree(pjl, subalias, path, nextDepth); } else { IQueryableCollection qc = joinable as IQueryableCollection; if (qc != null) WalkCollectionTree(qc, subalias, path, nextDepth); } }
private NavigationProperty CreateNavigationProperty( IAssociationType associationType, string propertyName, int propertyIndex, bool partOfKey, Func <int, CascadeStyle> getCascadeStyle, StructuralType structuralType, EntityMetadata entityMetadata) { var navigationProperty = new NavigationProperty { NameOnServer = propertyName, IsScalar = !associationType.IsCollectionType }; var joinable = associationType.GetAssociatedJoinable(entityMetadata.EntityPersister.Factory); if (associationType.IsCollectionType) { var collectionPersister = (IQueryableCollection)joinable; var elementPersister = collectionPersister.ElementPersister; if (!_entityMetadata.ContainsKey(elementPersister.MappedClass) || !entityMetadata.Associations.TryGetValue(propertyName, out var association)) { // Element is excluded from metadata, exclude also the relation return(null); } navigationProperty.AssociationName = GetAssociationName(structuralType.ShortName, elementPersister.MappedClass.Name, association.ForeignKeyPropertyNames); navigationProperty.EntityTypeName = GetBreezeTypeFullName(elementPersister.MappedClass); navigationProperty.InvForeignKeyNamesOnServer = association.ForeignKeyPropertyNames; } else { // Many to one var relatedPersister = (IEntityPersister)joinable; if (!_entityMetadata.ContainsKey(relatedPersister.MappedClass) || !entityMetadata.Associations.TryGetValue(propertyName, out var association)) { // Element is excluded from metadata, exclude also the relation return(null); } navigationProperty.AssociationName = GetAssociationName(structuralType.ShortName, relatedPersister.MappedClass.Name, association.ForeignKeyPropertyNames); navigationProperty.EntityTypeName = GetBreezeTypeFullName(relatedPersister.MappedClass); if (association.ForeignKeyDirection == ForeignKeyDirection.ForeignKeyFromParent) { navigationProperty.ForeignKeyNamesOnServer = association.ForeignKeyPropertyNames; } else { navigationProperty.InvForeignKeyNamesOnServer = association.ForeignKeyPropertyNames; } if (partOfKey) { // NH does not set FK properties as keys when the entity relation is set as key, we have to do it manually var keyProperties = new HashSet <string>(association.ForeignKeyPropertyNames); foreach (var dataProperty in structuralType.DataProperties) { if (keyProperties.Contains(dataProperty.NameOnServer)) { dataProperty.IsPartOfKey = true; } } } } if (_orphanDeleteEnabled) { navigationProperty.HasOrphanDelete = getCascadeStyle(propertyIndex).HasOrphanDelete; } return(navigationProperty); }
/// <summary> /// Used to detect circularities in the joined graph, note that /// this method is side-effecty /// </summary> protected virtual bool IsDuplicateAssociation(string lhsTable, string[] lhsColumnNames, IAssociationType type) { string foreignKeyTable; string[] foreignKeyColumns; if (type.ForeignKeyDirection.Equals(ForeignKeyDirection.ForeignKeyFromParent)) { foreignKeyTable = lhsTable; foreignKeyColumns = lhsColumnNames; } else { foreignKeyTable = type.GetAssociatedJoinable(Factory).TableName; foreignKeyColumns = JoinHelper.GetRHSColumnNames(type, Factory); } return IsDuplicateAssociation(foreignKeyTable, foreignKeyColumns); }
/// <summary> /// Add on association (one-to-one, many-to-one, or a collection) to a list /// of associations to be fetched by outerjoin /// </summary> private void AddAssociationToJoinTree( IAssociationType type, string[] aliasedLhsColumns, string alias, string path, int currentDepth, JoinType joinType) { IJoinable joinable = type.GetAssociatedJoinable(Factory); string subalias = GenerateTableAlias( associations.Count + 1, //before adding to collection! path, joinable ); OuterJoinableAssociation assoc = new OuterJoinableAssociation( type, alias, aliasedLhsColumns, subalias, joinType, Factory, enabledFilters ); assoc.ValidateJoin(path); associations.Add(assoc); int nextDepth = currentDepth + 1; if (!joinable.IsCollection) { if (joinable is IOuterJoinLoadable) { WalkEntityTree( (IOuterJoinLoadable) joinable, subalias, path, nextDepth ); } } else { if (joinable is IQueryableCollection) { WalkCollectionTree( (IQueryableCollection) joinable, subalias, path, nextDepth ); } } }