예제 #1
0
        /// <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);
                }
            }
        }
예제 #2
0
        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)
        }
예제 #3
0
			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);
        }
예제 #5
0
 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;
 }
예제 #6
0
 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>();
 }
예제 #7
0
		/// <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);
			}
		}
예제 #8
0
 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);
 }
예제 #11
0
        /// <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
		}
예제 #13
0
 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
 }
예제 #14
0
        /// <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);
        }
예제 #16
0
        /// <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);
            }
        }
예제 #17
0
        /// <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);
        }
예제 #18
0
        /// <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);
        }
예제 #19
0
        /// <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);
        }
예제 #21
0
 /// <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));
 }
예제 #22
0
		/// <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);
        }
예제 #24
0
		/// <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);
		}
예제 #25
0
		/// <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
						);
				}
			}
		}