private static object DeepClone(this ISessionImplementor session, object entity, DeepCloneOptions opts, System.Type entityType, IDictionary <object, object> resolvedEntities, DeepCloneParentEntity parentEntity = null) { opts = opts ?? new DeepCloneOptions(); if (entity == null || !NHibernateUtil.IsInitialized(entity)) { return(entityType.GetDefaultValue()); } entityType = entityType ?? entity.GetUnproxiedType(true); if (entityType.IsSimpleType()) { return(entity); } AbstractEntityPersister entityMetadata; try { entityMetadata = (AbstractEntityPersister)session.Factory.GetClassMetadata(entityType); } catch (Exception) { return(entityType.GetDefaultValue()); } if (resolvedEntities.ContainsKey(entity) && parentEntity != null) { return(CopyOnlyForeignKeyProperties(resolvedEntities[entity], entityType, entityMetadata, opts, parentEntity)); } if (resolvedEntities.ContainsKey(entity)) { return(resolvedEntities[entity]); } if (opts.CanCloneAsReferenceFunc != null && opts.CanCloneAsReferenceFunc(entityType)) { return(entity); } var propertyInfos = entityType.GetProperties(); var copiedEntity = ReflectHelper.GetDefaultConstructor(entityType).Invoke(new object[0]); resolvedEntities.Add(entity, copiedEntity); foreach (var propertyInfo in propertyInfos .Where(p => opts.CanCloneIdentifier(entityType) || entityMetadata.IdentifierPropertyName != p.Name) .Where(p => !opts.GetIgnoreMembers(entityType).Contains(p.Name)) .Where(p => p.GetSetMethod(true) != null)) { IType propertyType; try { propertyType = entityMetadata.GetPropertyType(propertyInfo.Name); } catch (Exception) { continue; } var resolveFn = opts.GetResolveFunction(entityType, propertyInfo.Name); if (resolveFn != null) { propertyInfo.SetValue(copiedEntity, resolveFn(entity), null); continue; } if (propertyType.IsEntityType && opts.SkipEntityTypesValue.HasValue && opts.SkipEntityTypesValue.Value) { continue; } //TODO: verify: false only when entity is a proxy or lazy field/property that is not yet initialized if (!NHibernateUtil.IsPropertyInitialized(entity, propertyInfo.Name)) { continue; } var propertyValue = propertyInfo.GetValue(entity, null); if (!NHibernateUtil.IsInitialized(propertyValue)) { //Use session load for proxy, works only for references (collections are not supported) if ( propertyValue != null && propertyValue.IsProxy() && !(propertyValue is IPersistentCollection) && opts.UseSessionLoadFunction ) { var lazyInit = ((INHibernateProxy)propertyValue).HibernateLazyInitializer; propertyInfo.SetValue(copiedEntity, LoadEntity(session, lazyInit.PersistentClass, lazyInit.Identifier), null); } continue; } var filterFn = opts.GetFilterFunction(entityType, propertyInfo.Name); if (filterFn != null) { propertyValue = filterFn(propertyValue); } var colNames = entityMetadata.GetPropertyColumnNames(propertyInfo.Name); var propType = propertyInfo.PropertyType; var copyAsReference = opts.CanCloneAsReference(entityType, propertyInfo.Name); if (propertyType.IsCollectionType) { var propertyList = CreateNewCollection(propertyType); propertyInfo.SetValue(copiedEntity, propertyList, null); AddItemToCollection(propertyList, propertyValue, o => copyAsReference ? o : session.DeepClone(o, opts, null, resolvedEntities, new DeepCloneParentEntity { Entity = copiedEntity, EntityPersister = entityMetadata, ChildType = propertyType, ReferencedColumns = ((CollectionType)propertyType) .GetReferencedColumns(session.Factory) })); } else if (propertyType.IsEntityType) { if (copyAsReference) { propertyInfo.SetValue(copiedEntity, propertyValue, null); } //Check if we have a parent entity and that is bidirectional related to the current property (one-to-many) else if (parentEntity != null && parentEntity.ReferencedColumns.SequenceEqual(colNames)) { propertyInfo.SetValue(copiedEntity, parentEntity.Entity, null); } else { propertyInfo.SetValue(copiedEntity, session.DeepClone(propertyValue, opts, propType, resolvedEntities), null); } } else if (propType.IsSimpleType()) { //Check if we have a parent entity and that is bidirectional related to the current property (one-to-many) //we dont want to set FKs to the parent entity as the parent is cloned if (parentEntity != null && parentEntity.ReferencedColumns.Contains(colNames.First())) { continue; } propertyInfo.SetValue(copiedEntity, propertyValue, null); } } return(copiedEntity); }