sexta-feira, 24 de julho de 2015

NHibernate - EntityCloner

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;

namespace Lapuinka.Data
{
    ///
    /// Create a dettached clone of a entity.
    ///
    public class EntityCloner
    {
        public static T MakeClone(T entity)
            where T : IEntity
        {
            var cloner = new EntityClonerInternal(entity, entity.GetEntityType());

            return (T)cloner.Clone();
        }

        private class EntityClonerInternal
        {
            private readonly IEntity _original;
            private readonly Type _type;
            private readonly Dictionary<object, object> _knowObjects;

            public EntityClonerInternal(IEntity original, Type type)
            {
                _original = original;
                _type = type;
                _knowObjects = new Dictionary<object, object>();
            }

            public IEntity Clone()
            {
                var clone = (IEntity)Activator.CreateInstance(_type);

                _knowObjects.Add(_original, clone);

                CloneProperties(_original, clone, _type);

                return clone;
            }

            private object CloneObject(object from, Type type)
            {
                if (from == null) return null;

                if (_knowObjects.ContainsKey(from))
                {
                    return _knowObjects[from];
                }

                var tmp = Activator.CreateInstance(type);

                _knowObjects.Add(from, tmp);
                CloneProperties(from, tmp, type);

                return tmp;
            }

            private void CloneProperties(object from, object to, Type type)
            {
                foreach (var propertyInfo in type.GetProperties())
                {
                    if (propertyInfo.GetCustomAttributes(typeof(XmlIgnoreAttribute), true).Length > 0) continue;

                    var fromValue = propertyInfo.GetValue(from, null);

                    switch (Type.GetTypeCode(propertyInfo.PropertyType))
                    {
                        case TypeCode.Byte:
                        case TypeCode.UInt16:
                        case TypeCode.UInt32:
                        case TypeCode.UInt64:
                        case TypeCode.SByte:
                        case TypeCode.Int16:
                        case TypeCode.Int32:
                        case TypeCode.Int64:
                        case TypeCode.Single:
                        case TypeCode.Double:
                        case TypeCode.Decimal:
                        case TypeCode.Boolean:
                        case TypeCode.Char:
                        case TypeCode.String:
                        case TypeCode.DateTime:
                            propertyInfo.SetValue(to, fromValue, null);
                            break;

                        case TypeCode.Object:
                        case TypeCode.Empty:
                        case TypeCode.DBNull:
                            if (propertyInfo.PropertyType == typeof(Guid))
                            {
                                propertyInfo.SetValue(to, fromValue, null);
                                break;
                            }

                            if (propertyInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEntity)))
                            {
                                propertyInfo.SetValue(to, CloneObject(fromValue, propertyInfo.PropertyType), null);
                                break;
                            }

                            if (propertyInfo.PropertyType.IsGenericType &&
                                propertyInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)))
                            {
                                var elementType = propertyInfo.PropertyType.GetGenericArguments()[0];

                                if (elementType.GetInterfaces().All(x => x != typeof(IEntity)))
                                {
                                    continue;
                                }

                                var listType = typeof(List<>);
                                var constructedListType = listType.MakeGenericType(elementType);

                                var list = (IList)Activator.CreateInstance(constructedListType);
                                //TODO: check if from Value is null.
                                if (fromValue != null)
                                {
                                    foreach (var item in (IEnumerable)fromValue)
                                    {
                                        list.Add(CloneObject(item, elementType));
                                    }

                                    propertyInfo.SetValue(to, list, null);
                                }

                                continue;
                            }

                            if (propertyInfo.PropertyType.IsGenericType &&
                                propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                            {
                                propertyInfo.SetValue(to, propertyInfo.GetValue(from, null), null);
                                break;
                            }

                            throw new NotSupportedException(String.Format("PropertyType {0} is not supported",
                                                                          propertyInfo.PropertyType.FullName));
                    }
                }
            }
        }
    }
}


Postar um comentário