Exemple #1
0
        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);
        }