/// <summary> /// Finish the process of collection-loading for this bound result set. Mainly this /// involves cleaning up resources and notifying the collections that loading is /// complete. /// </summary> /// <param name="persister">The persister for which to complete loading. </param> public void EndLoadingCollections(ICollectionPersister persister) { if (!loadContexts.HasLoadingCollectionEntries || (localLoadingCollectionKeys.Count == 0)) { return; } // in an effort to avoid concurrent-modification-exceptions (from // potential recursive calls back through here as a result of the // eventual call to PersistentCollection#endRead), we scan the // internal loadingCollections map for matches and store those matches // in a temp collection. the temp collection is then used to "drive" // the #endRead processing. List<CollectionKey> toRemove = new List<CollectionKey>(); List<LoadingCollectionEntry> matches =new List<LoadingCollectionEntry>(); foreach (CollectionKey collectionKey in localLoadingCollectionKeys) { ISessionImplementor session = LoadContext.PersistenceContext.Session; LoadingCollectionEntry lce = loadContexts.LocateLoadingCollectionEntry(collectionKey); if (lce == null) { log.Warn("In CollectionLoadContext#endLoadingCollections, localLoadingCollectionKeys contained [" + collectionKey + "], but no LoadingCollectionEntry was found in loadContexts"); } else if (lce.ResultSet == resultSet && lce.Persister == persister) { matches.Add(lce); if (lce.Collection.Owner == null) { session.PersistenceContext.AddUnownedCollection(new CollectionKey(persister, lce.Key, session.EntityMode), lce.Collection); } if (log.IsDebugEnabled) { log.Debug("removing collection load entry [" + lce + "]"); } // todo : i'd much rather have this done from #endLoadingCollection(CollectionPersister,LoadingCollectionEntry)... loadContexts.UnregisterLoadingCollectionXRef(collectionKey); toRemove.Add(collectionKey); } } localLoadingCollectionKeys.RemoveAll(toRemove); EndLoadingCollections(persister, matches); if ((localLoadingCollectionKeys.Count == 0)) { // todo : hack!!! // NOTE : here we cleanup the load context when we have no more local // LCE entries. This "works" for the time being because really // only the collection load contexts are implemented. Long term, // this cleanup should become part of the "close result set" // processing from the (sandbox/jdbc) jdbc-container code. loadContexts.Cleanup(resultSet); } }
public override ICollection GetSnapshot(ICollectionPersister persister) { EntityMode entityMode = Session.EntityMode; List<object> clonedList = new List<object>(list.Count); foreach (object current in list) { object deepCopy = persister.ElementType.DeepCopy(current, entityMode, persister.Factory); clonedList.Add(deepCopy); } return clonedList; }
public override IAuditWorkUnit Dispatch(IWorkUnitMergeVisitor first) { if (first is PersistentCollectionChangeWorkUnit) { PersistentCollectionChangeWorkUnit original = (PersistentCollectionChangeWorkUnit) first; // Merging the collection changes in both work units. // First building a map from the ids of the collection-entry-entities from the "second" collection changes, // to the PCCD objects. That way, we will be later able to check if an "original" collection change // should be added, or if it is overshadowed by a new one. IDictionary<Object, PersistentCollectionChangeData> newChangesIdMap = new Dictionary<Object, PersistentCollectionChangeData>(); foreach (PersistentCollectionChangeData persistentCollectionChangeData in getCollectionChanges()) { newChangesIdMap.Add( getOriginalId(persistentCollectionChangeData), persistentCollectionChangeData); } // This will be the list with the resulting (merged) changes. List<PersistentCollectionChangeData> mergedChanges = new List<PersistentCollectionChangeData>(); // Including only those original changes, which are not overshadowed by new ones. foreach (PersistentCollectionChangeData originalCollectionChangeData in original.getCollectionChanges()) { if (!newChangesIdMap.ContainsKey(getOriginalId(originalCollectionChangeData))) { mergedChanges.Add(originalCollectionChangeData); } } // Finally adding all of the new changes to the end of the list mergedChanges = (List<PersistentCollectionChangeData>)mergedChanges.Concat(getCollectionChanges()); return new PersistentCollectionChangeWorkUnit(sessionImplementor, EntityName, verCfg, EntityId, mergedChanges, ReferencingPropertyName); } else { throw new Exception("Trying to merge a " + first + " with a PersitentCollectionChangeWorkUnit. " + "This is not really possible."); } }
public override string ToLoggableString(object value, ISessionFactoryImplementor factory) { if (value == null) { return "null"; } Array array = (Array) value; int length = array.Length; IList list = new List<object>(length); IType elemType = GetElementType(factory); for (int i = 0; i < length; i++) { list.Add(elemType.ToLoggableString(array.GetValue(i), factory)); } return CollectionPrinter.ToString(list); }
/// <summary> /// Adds an association and extracts the aliases the association's 'with clause' is dependent on /// </summary> private void AddAssociation(string subalias, OuterJoinableAssociation association) { subalias = subalias.ToLower(); var dependencies = new List<string>(); var on = association.On.ToString(); if (!String.IsNullOrEmpty(on)) { foreach (Match match in aliasRegex.Matches(on)) { string alias = match.Groups[1].Value; if (alias == subalias) continue; dependencies.Add(alias.ToLower()); } } _dependentAliases.Add(new DependentAlias { Alias = subalias, DependsOn = dependencies.ToArray() }); associations.Add(association); }
private IList DoQuery(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies) { RowSelection selection = queryParameters.RowSelection; int maxRows = HasMaxRows(selection) ? selection.MaxRows : int.MaxValue; int entitySpan = EntityPersisters.Length; List<object> hydratedObjects = entitySpan == 0 ? null : new List<object>(entitySpan * 10); IDbCommand st = PrepareQueryCommand(queryParameters, false, session); IDataReader rs = GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection, session); // would be great to move all this below here into another method that could also be used // from the new scrolling stuff. // // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so // that I could do the control breaking at the means to know when to stop LockMode[] lockModeArray = GetLockModes(queryParameters.LockModes); EntityKey optionalObjectKey = GetOptionalObjectKey(queryParameters, session); bool createSubselects = IsSubselectLoadingEnabled; List<EntityKey[]> subselectResultKeys = createSubselects ? new List<EntityKey[]>() : null; IList results = new List<object>(); try { HandleEmptyCollections(queryParameters.CollectionKeys, rs, session); EntityKey[] keys = new EntityKey[entitySpan]; // we can reuse it each time if (log.IsDebugEnabled) { log.Debug("processing result set"); } int count; for (count = 0; count < maxRows && rs.Read(); count++) { if (log.IsDebugEnabled) { log.Debug("result set row: " + count); } object result = GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, keys, returnProxies); results.Add(result); if (createSubselects) { subselectResultKeys.Add(keys); keys = new EntityKey[entitySpan]; //can't reuse in this case } } if (log.IsDebugEnabled) { log.Debug(string.Format("done processing result set ({0} rows)", count)); } } catch (Exception e) { e.Data["actual-sql-query"] = st.CommandText; throw; } finally { session.Batcher.CloseCommand(st, rs); } InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.ReadOnly); if (createSubselects) { CreateSubselects(subselectResultKeys, queryParameters, session); } return results; }
public override IEnumerable GetDeletes(ICollectionPersister persister, bool indexIsFormula) { IList deletes = new List<object>(); Array sn = (Array) GetSnapshot(); int snSize = sn.Length; int arraySize = array.Length; int end; if (snSize > arraySize) { for (int i = arraySize; i < snSize; i++) { deletes.Add(i); } end = arraySize; } else { end = snSize; } for (int i = 0; i < end; i++) { if (array.GetValue(i) == null && sn.GetValue(i) != null) { deletes.Add(i); } } return deletes; }
/// <summary> /// Before <see cref="ReadFrom" /> is called the PersistentArrayHolder needs to setup /// a temporary list to hold the objects. /// </summary> public override void BeginRead() { base.BeginRead(); tempList = new List<object>(); }
private void InitTransientState() { loadContexts = null; nullAssociations = new HashedSet<AssociationKey>(); nonlazyCollections = new List<IPersistentCollection>(InitCollectionSize); }
public override bool AfterInitialize(ICollectionPersister persister) { // NH Different behavior : NH-739 // would be nice to prevent this overhead but the operation is managed where the ICollectionPersister is not available bool result; if (persister.IsOneToMany && HasQueuedOperations) { int additionStartFrom = bag.Count; IList additionQueue = new List<object>(additionStartFrom); foreach (object o in QueuedAdditionIterator) { if (o != null) { for (int i = 0; i < bag.Count; i++) { // we are using ReferenceEquals to be sure that is exactly the same queued instance if (ReferenceEquals(o, bag[i])) { additionQueue.Add(o); break; } } } } result = base.AfterInitialize(persister); if(!result) { // removing duplicated additions foreach (object o in additionQueue) { for (int i = additionStartFrom; i < bag.Count; i++) { if (ReferenceEquals(o, bag[i])) { bag.RemoveAt(i); break; } } } } } else { result = base.AfterInitialize(persister); } return result; }
/// <summary> /// 1. Recreate the collection key -> collection map /// 2. rebuild the collection entries /// 3. call Interceptor.postFlush() /// </summary> protected virtual void PostFlush(ISessionImplementor session) { if (log.IsDebugEnabled) { log.Debug("post flush"); } IPersistenceContext persistenceContext = session.PersistenceContext; persistenceContext.CollectionsByKey.Clear(); persistenceContext.BatchFetchQueue.ClearSubselects(); //the database has changed now, so the subselect results need to be invalidated // NH Different implementation: In NET an iterator is immutable; // we need something to hold the persistent collection to remove, and it must be less intrusive as possible IDictionary cEntries = persistenceContext.CollectionEntries; List<IPersistentCollection> keysToRemove = new List<IPersistentCollection>(cEntries.Count); foreach (DictionaryEntry me in cEntries) { CollectionEntry collectionEntry = (CollectionEntry) me.Value; IPersistentCollection persistentCollection = (IPersistentCollection) me.Key; collectionEntry.PostFlush(persistentCollection); if (collectionEntry.LoadedPersister == null) { keysToRemove.Add(persistentCollection); } else { //otherwise recreate the mapping between the collection and its key CollectionKey collectionKey = new CollectionKey(collectionEntry.LoadedPersister, collectionEntry.LoadedKey, session.EntityMode); persistenceContext.CollectionsByKey[collectionKey] = persistentCollection; } } foreach (IPersistentCollection key in keysToRemove) { persistenceContext.CollectionEntries.Remove(key); } session.Interceptor.PostFlush((ICollection) persistenceContext.EntitiesByKey.Values); }
public override IEnumerable GetDeletes(ICollectionPersister persister, bool indexIsFormula) { IList deletes = new List<object>(); IList sn = (IList) GetSnapshot(); int end; if (sn.Count > list.Count) { for (int i = list.Count; i < sn.Count; i++) { deletes.Add(indexIsFormula ? sn[i] : i); } end = list.Count; } else { end = sn.Count; } for (int i = 0; i < end; i++) { if (list[i] == null && sn[i] != null) { deletes.Add(indexIsFormula ? sn[i] : i); } } return deletes; }
public override IEnumerable GetDeletes(ICollectionPersister persister, bool indexIsFormula) { IList deletes = new List<object>(); IDictionary sn = (IDictionary) GetSnapshot(); foreach (DictionaryEntry e in sn) { object key = e.Key; if (!map.Contains(key)) { deletes.Add(indexIsFormula ? e.Value : key); } } return deletes; }
/// <returns><see cref="IList" /> of <see cref="IType" /></returns> protected SqlType[] GetParameterTypes(QueryParameters parameters, bool addLimit, bool addOffset) { List<IType> paramTypeList = new List<IType>(); int span = 0; for (int index = 0; index < parameters.PositionalParameterTypes.Length; index++) { int location = parameters.PositionalParameterLocations[index]; location = parameters.FindAdjustedParameterLocation(location); IType type = parameters.PositionalParameterTypes[index]; ArrayHelper.SafeSetValue(paramTypeList, location, type); span += type.GetColumnSpan(Factory); } for (int index = 0; index < parameters.FilteredParameterTypes.Count; index++) { int location = parameters.FilteredParameterLocations[index]; IType type = parameters.FilteredParameterTypes[index]; ArrayHelper.SafeSetValue(paramTypeList, location, type); span += type.GetColumnSpan(Factory); } if (parameters.NamedParameters != null && parameters.NamedParameters.Count > 0) { // convert the named parameters to an array of types foreach (KeyValuePair<string, TypedValue> namedParameter in parameters.NamedParameters) { string name = namedParameter.Key; TypedValue typedval = namedParameter.Value; int[] locs = GetNamedParameterLocs(name); span += typedval.Type.GetColumnSpan(Factory) * locs.Length; for (int i = 0; i < locs.Length; i++) { int location = locs[i]; location = parameters.FindAdjustedParameterLocation(location); // can still clash with positional parameters // could consider throwing an exception to locate problem (NH-1098) while ((location < paramTypeList.Count) && (paramTypeList[location] != null)) location++; ArrayHelper.SafeSetValue(paramTypeList, location, typedval.Type); } } } if (addLimit && Factory.Dialect.SupportsVariableLimit) { if (Factory.Dialect.BindLimitParametersFirst) { paramTypeList.Insert(0, NHibernateUtil.Int32); if (addOffset) { paramTypeList.Insert(0, NHibernateUtil.Int32); } } else { paramTypeList.Add(NHibernateUtil.Int32); if (addOffset) { paramTypeList.Add(NHibernateUtil.Int32); } } span += addOffset ? 2 : 1; } return ConvertITypesToSqlTypes(paramTypeList, span); }
protected SqlType[] ConvertITypesToSqlTypes(List<IType> nhTypes, int totalSpan) { SqlType[] result = new SqlType[totalSpan]; int index = 0; foreach (IType type in nhTypes) { int span = type.SqlTypes(Factory).Length; Array.Copy(type.SqlTypes(Factory), 0, result, index, span); index += span; } return result; }
protected internal virtual string RenderLoggableString(object value, ISessionFactoryImplementor factory) { IList list = new List<object>(); IType elemType = GetElementType(factory); IEnumerable iter = GetElementsIterator(value); foreach (object o in iter) list.Add(elemType.ToLoggableString(o, factory)); return CollectionPrinter.ToString(list); }
// For a one-to-many, a <bag> is not really a bag; // it is *really* a set, since it can't contain the // same element twice. It could be considered a bug // in the mapping dtd that <bag> allows <one-to-many>. // Anyway, here we implement <set> semantics for a // <one-to-many> <bag>! public override IEnumerable GetDeletes(ICollectionPersister persister, bool indexIsFormula) { IType elementType = persister.ElementType; EntityMode entityMode = Session.EntityMode; List<object> deletes = new List<object>(); IList sn = (IList) GetSnapshot(); int i = 0; foreach (object old in sn) { bool found = false; if (bag.Count > i && elementType.IsSame(old, bag[i++], entityMode)) { //a shortcut if its location didn't change! found = true; } else { foreach (object newObject in bag) { if (elementType.IsSame(old, newObject, entityMode)) { found = true; break; } } } if (!found) { deletes.Add(old); } } return deletes; }
void IDeserializationCallback.OnDeserialization(object sender) { log.Debug("Deserialization callback persistent-context"); // during deserialization, we need to reconnect all proxies and // collections to this session, as well as the EntityEntry and // CollectionEntry instances; these associations are transient // because serialization is used for different things. parentsByChild = IdentityMap.Instantiate(InitCollectionSize); // OnDeserialization() must be called manually on all Dictionaries and Hashtables, // otherwise they are still empty at this point (the .NET deserialization code calls // OnDeserialization() on them AFTER it calls the current method). entitiesByKey.OnDeserialization(sender); entitiesByUniqueKey.OnDeserialization(sender); ((IDeserializationCallback)entityEntries).OnDeserialization(sender); proxiesByKey.OnDeserialization(sender); entitySnapshotsByKey.OnDeserialization(sender); ((IDeserializationCallback)arrayHolders).OnDeserialization(sender); ((IDeserializationCallback)collectionEntries).OnDeserialization(sender); collectionsByKey.OnDeserialization(sender); // If nullifiableEntityKeys is once used in the current method, HashedSets will need // an OnDeserialization() method. //nullifiableEntityKeys.OnDeserialization(sender); if (unownedCollections != null) { unownedCollections.OnDeserialization(sender); } // TODO NH: "reconnect" EntityKey with session.factory and create a test for serialization of StatefulPersistenceContext foreach (DictionaryEntry collectionEntry in collectionEntries) { try { ((IPersistentCollection)collectionEntry.Key).SetCurrentSession(session); CollectionEntry ce = (CollectionEntry)collectionEntry.Value; if (ce.Role != null) { ce.AfterDeserialize(Session.Factory); } } catch (HibernateException he) { throw new InvalidOperationException(he.Message); } } List<EntityKey> keysToRemove = new List<EntityKey>(); foreach (KeyValuePair<EntityKey, INHibernateProxy> p in proxiesByKey) { if (p.Value != null) { (p.Value).HibernateLazyInitializer.Session = session; } else { // the proxy was pruned during the serialization process because the target had been instantiated. keysToRemove.Add(p.Key); } } for (int i = 0; i < keysToRemove.Count; i++) proxiesByKey.Remove(keysToRemove[i]); foreach (EntityEntry e in entityEntries.Values) { try { e.Persister = session.Factory.GetEntityPersister(e.EntityName); } catch (MappingException me) { throw new InvalidOperationException(me.Message); } } }
void IDeserializationCallback.OnDeserialization(object sender) { log.Debug("Deserialization callback persistent-context"); // during deserialization, we need to reconnect all proxies and // collections to this session, as well as the EntityEntry and // CollectionEntry instances; these associations are transient // because serialization is used for different things. // TODO NH: "reconnect" EntityKey with session.factory and create a test for serialization of StatefulPersistenceContext foreach (DictionaryEntry collectionEntry in collectionEntries) { try { ((IPersistentCollection)collectionEntry.Key).SetCurrentSession(session); CollectionEntry ce = (CollectionEntry)collectionEntry.Value; if (ce.Role != null) { ce.AfterDeserialize(Session.Factory); } } catch (HibernateException he) { throw new InvalidOperationException(he.Message); } } List<EntityKey> keysToRemove = new List<EntityKey>(); foreach (KeyValuePair<EntityKey, INHibernateProxy> p in proxiesByKey) { if (p.Value != null) { (p.Value).HibernateLazyInitializer.Session = session; } else { // the proxy was pruned during the serialization process because the target had been instantiated. keysToRemove.Add(p.Key); } } for (int i = 0; i < keysToRemove.Count; i++) proxiesByKey.Remove(keysToRemove[i]); foreach (EntityEntry e in entityEntries.Values) { try { e.Persister = session.Factory.GetEntityPersister(e.EntityName); } catch (MappingException me) { throw new InvalidOperationException(me.Message); } } }
/// <summary> /// Queue an addition, delete etc. if the persistent collection supports it /// </summary> protected void QueueOperation(IDelayedOperation element) { if (operationQueue == null) { operationQueue = new List<IDelayedOperation>(10); } operationQueue.Add(element); dirty = true; //needed so that we remove this collection from the second-level cache }
/// <summary> /// Remove an entity from the session cache, also clear /// up other state associated with the entity, all except /// for the <tt>EntityEntry</tt> /// </summary> public object RemoveEntity(EntityKey key) { object tempObject = entitiesByKey[key]; entitiesByKey.Remove(key); object entity = tempObject; List<EntityUniqueKey> toRemove = new List<EntityUniqueKey>(); foreach (KeyValuePair<EntityUniqueKey, object> pair in entitiesByUniqueKey) { if (pair.Value == entity) toRemove.Add(pair.Key); } foreach (EntityUniqueKey uniqueKey in toRemove) { entitiesByUniqueKey.Remove(uniqueKey); } entitySnapshotsByKey.Remove(key); nullifiableEntityKeys.Remove(key); BatchFetchQueue.RemoveBatchLoadableEntityKey(key); BatchFetchQueue.RemoveSubselect(key); return entity; }
/// <summary> /// Clears out any Queued operation. /// </summary> /// <remarks> /// After flushing, clear any "queued" additions, since the /// database state is now synchronized with the memory state. /// </remarks> public virtual void PostAction() { operationQueue = null; cachedSize = -1; ClearDirty(); }
/// <summary> /// Takes the contents stored in the temporary list created during <see cref="BeginRead" /> /// that was populated during <see cref="ReadFrom" /> and write it to the underlying /// array. /// </summary> public override bool EndRead(ICollectionPersister persister) { SetInitialized(); array = System.Array.CreateInstance(elementClass, tempList.Count); for (int i = 0; i < tempList.Count; i++) { array.SetValue(tempList[i], i); } tempList = null; return true; }
public virtual bool AfterInitialize(ICollectionPersister persister) { SetInitialized(); //do this bit after setting initialized to true or it will recurse if (operationQueue != null) { PerformQueuedOperations(); operationQueue = null; cachedSize = -1; return false; } else { return true; } }
public override ICollection GetOrphans(object snapshot, string entityName) { object[] sn = (object[]) snapshot; object[] arr = (object[]) array; List<object> result = new List<object>(sn); for (int i = 0; i < sn.Length; i++) { IdentityRemove(result, arr[i], entityName, Session); } return result; }
public ICollection GetQueuedOrphans(string entityName) { if (HasQueuedOperations) { List<object> additions = new List<object>(operationQueue.Count); List<object> removals = new List<object>(operationQueue.Count); for (int i = 0; i < operationQueue.Count; i++) { IDelayedOperation op = operationQueue[i]; if (op.AddedInstance != null) { additions.Add(op.AddedInstance); } if (op.Orphan != null) { removals.Add(op.Orphan); } } return GetOrphans(removals, additions, entityName, session); } return CollectionHelper.EmptyCollection; }
private static int[] GetTopologicalSortOrder(List<DependentAlias> fields) { TopologicalSorter g = new TopologicalSorter(fields.Count); Dictionary<string, int> _indexes = new Dictionary<string, int>(); // add vertices for (int i = 0; i < fields.Count; i++) { _indexes[fields[i].Alias.ToLower()] = g.AddVertex(i); } // add edges for (int i = 0; i < fields.Count; i++) { if (fields[i].DependsOn != null) { for (int j = 0; j < fields[i].DependsOn.Length; j++) { var dependentField = fields[i].DependsOn[j].ToLower(); if (_indexes.ContainsKey(dependentField)) { g.AddEdge(i, _indexes[dependentField]); } } } } return g.Sort(); }
/// <summary> /// Given a collection of entity instances that used to /// belong to the collection, and a collection of instances /// that currently belong, return a collection of orphans /// </summary> protected static ICollection GetOrphans(ICollection oldElements, ICollection currentElements, string entityName, ISessionImplementor session) { // short-circuit(s) if (currentElements.Count == 0) { // no new elements, the old list contains only Orphans return oldElements; } if (oldElements.Count == 0) { // no old elements, so no Orphans neither return oldElements; } IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType; // create the collection holding the orphans List<object> res = new List<object>(); // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access HashedSet<TypedValue> currentIds = new HashedSet<TypedValue>(); foreach (object current in currentElements) { if (current != null && ForeignKeys.IsNotTransient(entityName, current, null, session)) { object currentId = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, current, session); currentIds.Add(new TypedValue(idType, currentId, session.EntityMode)); } } // iterate over the *old* list foreach (object old in oldElements) { object oldId = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, old, session); if (!currentIds.Contains(new TypedValue(idType, oldId, session.EntityMode))) { res.Add(old); } } return res; }
/// <summary> /// Generate a sequence of <c>LEFT OUTER JOIN</c> clauses for the given associations. /// </summary> protected JoinFragment MergeOuterJoins(IList<OuterJoinableAssociation> associations) { IList<OuterJoinableAssociation> sortedAssociations = new List<OuterJoinableAssociation>(); var indices = GetTopologicalSortOrder(_dependentAliases); for (int index = indices.Length - 1; index >= 0; index--) { sortedAssociations.Add(associations[indices[index]]); } JoinFragment outerjoin = Dialect.CreateOuterJoinFragment(); OuterJoinableAssociation last = null; foreach (OuterJoinableAssociation oj in sortedAssociations) { if (last != null && last.IsManyToManyWith(oj)) { oj.AddManyToManyJoin(outerjoin, (IQueryableCollection) last.Joinable); } else { oj.AddJoins(outerjoin); // NH Different behavior : NH1179 and NH1293 // Apply filters in Many-To-One association if (enabledFiltersForManyToOne.Count > 0) { string manyToOneFilterFragment = oj.Joinable.FilterFragment(oj.RHSAlias, enabledFiltersForManyToOne); bool joinClauseDoesNotContainsFilterAlready = outerjoin.ToFromFragmentString.IndexOfCaseInsensitive(manyToOneFilterFragment) == -1; if (joinClauseDoesNotContainsFilterAlready) { outerjoin.AddCondition(manyToOneFilterFragment); } } } last = oj; } return outerjoin; }
public static void IdentityRemove(IList list, object obj, string entityName, ISessionImplementor session) { if (obj != null && ForeignKeys.IsNotTransient(entityName, obj, null, session)) { IType idType = session.Factory.GetEntityPersister(entityName).IdentifierType; object idOfCurrent = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, obj, session); List<object> toRemove = new List<object>(list.Count); foreach (object current in list) { if (current == null) { continue; } object idOfOld = ForeignKeys.GetEntityIdentifierIfNotUnsaved(entityName, current, session); if (idType.IsEqual(idOfCurrent, idOfOld, session.EntityMode, session.Factory)) { toRemove.Add(current); } } foreach (object ro in toRemove) { list.Remove(ro); } } }