/// <summary>
        /// Ignores a member to map by its name
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="expression"></param>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public static IMappingExpression <TSource, TTarget> IgnoreMemberByName <TSource, TTarget>(
            this IMappingExpression <TSource, TTarget> expression,
            string memberName)
        {
            var member = TypeFinder.CachedDiscoverableProperties(typeof(TTarget))
                         .Where(x => x.Name == memberName)
                         .SingleOrDefault();

            if (member == null)
            {
                throw new MissingMemberException("Could not find the property " + memberName + " on type " + typeof(TTarget).FullName);
            }

            expression.MappingContext.AddMemberExpression(member.PropertyType, typeof(TTarget), memberName, opt => opt.Ignore());
            return(expression);
        }
        /// <summary>
        /// Copies an object graph.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="targetType">Type of the target.</param>
        /// <param name="factory">The factory.</param>
        /// <param name="recurseCount">The recurse count.</param>
        /// <param name="hideErrorsInPartialTrust">The hide errors in partial trust.</param>
        /// <returns></returns>
        public static CloneOf <object> DeepCopy(this object source, Type targetType, Func <object> factory = null, int recurseCount = 0, bool?hideErrorsInPartialTrust = true)
        {
            if (recurseCount > 50)
            {
                throw new InvalidOperationException("Cannot clone an object graph greater than 50 objects in depth");
            }

            var isPartialClone = false;

            //grab the type and create a new instance of that type
            var sourceType = targetType;
            var target     = factory == null?CreateObject(targetType) : factory.Invoke();

            //grab the properties
            var discoverableProperties = TypeFinder.CachedDiscoverableProperties(sourceType);

            //iterate over the properties and if it has a 'set' method assign it from the source TO the target
            foreach (var info in discoverableProperties)
            {
                // We don't check IsAssembly and IsFamily on the get method to ensure we don't try internal / private get accessors
                // because we rely on catching an exception instead - without comparing the PermissionSets of this code with the target
                // code IsAssembly and IsFamily don't give a clear picture of whether we can read it with reflection anyway

                // The GetMethod might have security parameters on it or might be internal. If security is tight
                // on the get method, it won't be returned, so check for null and double-check it's not internal
                var methodGetter = TypeFinder.DynamicMemberAccess.GetterDelegate(info);

                // We don't check !methodInfo.IsAssembly && !methodInfo.IsFamily here, instead leaving that to the try-catch
                // so that we can report back to the caller. If it was easier to check our PermissionSet with that of the type
                // we're looking at, I'd avoid a try-catch, but clarifying the PermissionSet requires full trust(!) - APN
                if (methodGetter != null)
                {
                    try
                    {
                        var propertyValue = info.GetValue(source, null);
                        var setter        = TypeFinder.DynamicMemberAccess.SetterDelegate(info, true);
                        SetValue(info, info.PropertyType, target, propertyValue, setter.Invoke, recurseCount);
                    }
                    catch (MemberAccessException)
                    {
                        if (hideErrorsInPartialTrust.HasValue && hideErrorsInPartialTrust.Value)
                        {
                            isPartialClone = true;
                            continue;
                        }
                        throw;
                    }
                }
            }

            // grab protected fields
            var discoverableFields = TypeFinder.CachedDiscoverableFields(sourceType);

            // Iterate over the properties to see if it is not readonly
            foreach (var fieldInfo in discoverableFields)
            {
                var localFieldInfoCopy = fieldInfo;

                try
                {
                    var sourceValue = fieldInfo.GetValue(source);
                    SetValue(fieldInfo, fieldInfo.FieldType, target, sourceValue, localFieldInfoCopy.SetValue, recurseCount);
                }
                catch (FieldAccessException)
                {
                    if (hideErrorsInPartialTrust.HasValue && hideErrorsInPartialTrust.Value)
                    {
                        isPartialClone = true;
                        continue;
                    }
                    throw;
                }
            }

            //return the new item
            return(new CloneOf <object>(isPartialClone, target));
        }