public Action <T, T> CopyAction <T>()

        {
            var key = typeof(T).FullName;

            if (_actions.TryGetValue(key, out var action))
            {
                return(action as Action <T, T>);
            }
            var className = CopyProviderHelper.GetClassName(typeof(T), typeof(T));

            var flags = BindingFlags.Public | BindingFlags.Static |
                        BindingFlags.InvokeMethod;

            if (!_comp.TryGetValue(className, out var comp))
            {
                comp = GenerateCopyClass(typeof(T), typeof(T));
            }
            // comp = GenerateCopyClass(typeof(T), typeof(TU));
            var result = comp.InvokeMember("CopyAction", flags, null,
                                           null, null) as Action <T, T>;

            _actions.Add(key, result);
            return(result);
        }
        /// <summary>
        /// Creates the copy method.
        /// </summary>
        /// <param name="sourceType">Type of the source.</param>
        /// <param name="targetType">Type of the target.</param>
        /// <returns></returns>
        private static CodeMemberMethod CreateCopyMethod(Type sourceType, Type targetType)
        {
            var copyMethod = new CodeMemberMethod
            {
                Attributes = MemberAttributes.Public | MemberAttributes.Static,
                Name       = CopyMethodName
            };

            var source = new CodeParameterDeclarationExpression(sourceType, "source");
            var target = new CodeParameterDeclarationExpression(targetType, "target");

            copyMethod.Parameters.Add(source);
            copyMethod.Parameters.Add(target);

            var map = CopyProviderHelper.GetMatchingProperties(sourceType, targetType);

            foreach (var item in map)

            {
                var targetReference =
                    new CodeFieldReferenceExpression(new CodeVariableReferenceExpression(target.Name),
                                                     item.TargetProperty.Name);
                var sourceReference =
                    new CodeFieldReferenceExpression(new CodeVariableReferenceExpression(source.Name),
                                                     item.SourceProperty.Name);
                copyMethod.Statements.Add(new CodeAssignStatement(targetReference, sourceReference));
            }

            return(copyMethod);
        }
        public void AddPropertyMap <T, TU>()

        {
            var props = CopyProviderHelper.GetMatchingProperties(typeof(T), typeof(TU));

            var className = CopyProviderHelper.GetClassName(typeof(T), typeof(TU));

            _maps.Add(className, props.ToArray());
        }
        public PropertyMap[] AddPropertyMap(object source, object target)

        {
            var props     = CopyProviderHelper.GetMatchingProperties(source.GetType(), target.GetType());
            var className = CopyProviderHelper.GetClassName(source.GetType(), target.GetType());
            var array     = props.ToArray();

            _maps.Add(className, array);
            return(array);
        }
        public void Copy <T, TU>(T source, TU target)
        {
            var className = CopyProviderHelper.GetClassName(source.GetType(), target.GetType());

            if (!(_maps.TryGetValue(className, out var propMap)))
            {
                propMap = AddPropertyMap(source, target);
            }

            foreach (var prop in propMap)
            {
                var sourceValue = prop.SourceProperty.GetValue(source, null);
                prop.TargetProperty.SetValue(target, sourceValue, null);
            }
        }
        public void CopyWithDom <T, TU>(T source, TU target)

        {
            var className = CopyProviderHelper.GetClassName(typeof(T), typeof(TU));

            var flags = BindingFlags.Public | BindingFlags.Static |
                        BindingFlags.InvokeMethod;

            var args = new object[] { source, target };

            if (!_comp.TryGetValue(className, out var comp))
            {
                comp = GenerateCopyClass(source.GetType(), target.GetType());
            }
            // comp = GenerateCopyClass(typeof(T), typeof(TU));

            comp.InvokeMember("CopyProps", flags, null, null, args);
        }
        /// <summary>
        /// Creates the copy action.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TU">The type of the u.</typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        private Action <T, TU> CreateCopyAction <T, TU>(string key)
        {
            var className = CopyProviderHelper.GetClassName(typeof(T), typeof(TU));

            const BindingFlags flags = BindingFlags.Public | BindingFlags.Static |
                                       BindingFlags.InvokeMethod;

            if (!_comp.TryGetValue(className, out var comp))
            {
                comp = GenerateCopyClass(typeof(T), typeof(TU));
            }

            var result = comp.InvokeMember(CopyActionMethodName, flags, null,
                                           null, null) as Action <T, TU>;

            _actions.Add(key, result);
            return(result);
        }
        /// <summary>
        /// Generates the copy class.
        /// </summary>
        /// <param name="sourceType">Type of the source.</param>
        /// <param name="targetType">Type of the target.</param>
        /// <returns></returns>
        public Type GenerateCopyClass(Type sourceType, Type targetType)
        {
            var className = CopyProviderHelper.GetClassName(sourceType, targetType);

            if (_comp.TryGetValue(className, out var type))
            {
                return(type);
            }

            var unit = new CodeCompileUnit();

            var ns = CreateNamespace();

            unit.Namespaces.Add(ns);

            var targetClass = CreateClass(className);

            ns.Types.Add(targetClass);

            var copyMethod = CreateCopyMethod(sourceType, targetType);

            targetClass.Members.Add(copyMethod);

            var copyActionMethod = CreateCopyActionMethod(sourceType, targetType, copyMethod);

            targetClass.Members.Add(copyActionMethod);

            Debug.WriteLine(unit.ToString());

            var results = BuildAssembly(unit, targetType.Assembly);

            // Compiler output

            foreach (var line in results.Output)
            {
                Debug.WriteLine(line);
            }

            var copierType = results.CompiledAssembly.GetType(NamespaceName + "." + className);

            _comp.Add(className, copierType);

            return(copierType);
        }
        public Type GenerateCopyClass(Type sourceType, Type targetType)

        {
            var className = CopyProviderHelper.GetClassName(sourceType, targetType);

            if (_comp.TryGetValue(className, out var type))
            {
                return(type);
            }

            var builder = new StringBuilder();

            builder.Append("namespace CopyHelper {\r\n");

            builder.Append("    using System;\r\n");

            builder.Append("    public class ");

            builder.Append(className);

            builder.Append("   {\r\n");

            builder.Append("        public static Action<" + sourceType.FullName + ", " + targetType.FullName +
                           "> CopyAction()");

            builder.Append("        {\r\n");

            builder.Append("            return CopyProps;");

            builder.Append("        }\r\n");

            builder.Append("        public static void CopyProps(");

            builder.Append(sourceType.FullName);

            builder.Append(" source, ");

            builder.Append(targetType.FullName);

            builder.Append(" target) {\r\n");

            var map = CopyProviderHelper.GetMatchingProperties(sourceType, targetType);

            foreach (var item in map)

            {
                builder.Append("            target.");

                builder.Append(item.TargetProperty.Name);

                builder.Append(" = ");

                builder.Append("source.");

                builder.Append(item.SourceProperty.Name);

                builder.Append(";\r\n");
            }

            builder.Append("        }\r\n   }\r\n}");

            // Write out method body

            Debug.WriteLine(builder.ToString());

            var codeCompiler = CodeDomProvider.CreateProvider("CSharp");

            var compilerParameters = new CompilerParameters();

            compilerParameters.ReferencedAssemblies.Add(typeof(TestObject).Assembly.Location);

            compilerParameters.GenerateInMemory = true;

            var results = codeCompiler.CompileAssemblyFromSource(compilerParameters, builder.ToString());

            // Compiler output

            foreach (var line in results.Output)
            {
                Debug.WriteLine(line);
            }

            var copierType = results.CompiledAssembly.GetType("CopyHelper." + className);

            _comp.Add(className, copierType);

            return(copierType);
        }