Tuesday, December 03, 2013

EntityFramework 6 breaks Backup/Restore of LocalDB.

EntityFramework 6 has lots of Resiliency enhancements, but one of the side effects is that doing a backup or restore of a LocalDB database will need a tweak to keep EF from spinning up a transaction around the SQL statement.

Essentially, when you call ExecuteSqlCommand, you have to request that it does NOT ensure the a transaction exists with the first parameter of TransactionalBehavior.DoNotEnsureTransaction.

I have updated the appropriate posts:

How to backup a LocalDB database under an MVC 4 sytem
Restoring a LocalDB within an MVC web application


Friday, May 24, 2013

Getting the requestor's IP address.

Getting the client's IP

While it seems it should be straightforward to get the IP address of the client making a web request in the ASP.Net pipeline, there are a few surprises lurking.  In my link-click tracking application, I ended up with this snippet based on Grant Burton's great post.
namespace Phydeaux.Helpers
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Web;

    public static class ClientIP
    {
        public static string ClientIPFromRequest(this HttpRequestBase request, bool skipPrivate)
        {
            foreach (var item in s_HeaderItems)
            {
                var ipString = request.Headers[item.Key];

                if (String.IsNullOrEmpty(ipString))
                    continue;

                if (item.Split)
                {
                    foreach (var ip in ipString.Split(','))
                        if (ValidIP(ip, skipPrivate))
                            return ip;
                }
                else
                {
                    if (ValidIP(ipString, skipPrivate))
                        return ipString;
                }
            }

            return request.UserHostAddress;
        }

        private static bool ValidIP(string ip, bool skipPrivate)
        {
            IPAddress ipAddr;

            ip = ip == null ? String.Empty : ip.Trim();

            if (0 == ip.Length
                || false == IPAddress.TryParse(ip, out ipAddr)
                || (ipAddr.AddressFamily != AddressFamily.InterNetwork
                    && ipAddr.AddressFamily != AddressFamily.InterNetworkV6))
                return false;

            if (skipPrivate && ipAddr.AddressFamily == AddressFamily.InterNetwork)
            {
                var addr = IpRange.AddrToUInt64(ipAddr);
                foreach (var range in s_PrivateRanges)
                {
                    if (range.Encompasses(addr))
                        return false;
                }
            }

            return true;
        }

        /// 
        /// Provides a simple class that understands how to parse and
        /// compare IP addresses (IPV4 and IPV6) ranges.
        /// 
        private sealed class IpRange
        {
            private readonly UInt64 _start;
            private readonly UInt64 _end;

            public IpRange(string startStr, string endStr)
            {
                _start = ParseToUInt64(startStr);
                _end = ParseToUInt64(endStr);
            }

            public static UInt64 AddrToUInt64(IPAddress ip)
            {
                var ipBytes = ip.GetAddressBytes();
                UInt64 value = 0;

                foreach (var abyte in ipBytes)
                {
                    value <<= 8;    // shift
                    value += abyte;
                }

                return value;
            }

            public static UInt64 ParseToUInt64(string ipStr)
            {
                var ip = IPAddress.Parse(ipStr);
                return AddrToUInt64(ip);
            }

            public bool Encompasses(UInt64 addrValue)
            {
                return _start <= addrValue && addrValue <= _end;
            }

            public bool Encompasses(IPAddress addr)
            {
                var value = AddrToUInt64(addr);
                return Encompasses(value);
            }
        };

        private static readonly IpRange[] s_PrivateRanges =
            new IpRange[] { 
                    new IpRange("0.0.0.0","2.255.255.255"),
                    new IpRange("10.0.0.0","10.255.255.255"),
                    new IpRange("127.0.0.0","127.255.255.255"),
                    new IpRange("169.254.0.0","169.254.255.255"),
                    new IpRange("172.16.0.0","172.31.255.255"),
                    new IpRange("192.0.2.0","192.0.2.255"),
                    new IpRange("192.168.0.0","192.168.255.255"),
                    new IpRange("255.255.255.0","255.255.255.255")
            };


        /// 
        /// Describes a header item (key) and if it is expected to be 
        /// a comma-delimited string
        /// 
        private sealed class HeaderItem
        {
            public readonly string Key;
            public readonly bool Split;

            public HeaderItem(string key, bool split)
            {
                Key = key;
                Split = split;
            }
        }

        // order is in trust/use order top to bottom
        private static readonly HeaderItem[] s_HeaderItems =
            new HeaderItem[] { 
                    new HeaderItem("HTTP_CLIENT_IP",false),
                    new HeaderItem("HTTP_X_FORWARDED_FOR",true),
                    new HeaderItem("HTTP_X_FORWARDED",false),
                    new HeaderItem("HTTP_X_CLUSTER_CLIENT_IP",false),
                    new HeaderItem("HTTP_FORWARDED_FOR",false),
                    new HeaderItem("HTTP_FORWARDED",false),
                    new HeaderItem("HTTP_VIA",false),
                    new HeaderItem("REMOTE_ADDR",false)
            };
    }
}