Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#
Tip/Trick

Easing Non-standard EntityFramework Migrations

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
5 Jan 2018CPOL2 min read 9.2K   5  
When you have a customized DbContext constructor.

Introduction

Like most of you probably do, I have a local database server for development, and a production server that's used when the (MVC) app goes live. This means that you probably have two at least two connection strings in the Web.config file.  This is how I resolve it so that I don't have to remember to use the correct connection string. Personally, I have THREE possible connection strings - DefaultConnect, DevConnection, and WorkConnection.

The Code

In my DbConext-derived class, I have the following static properties:

C#
// Helper property to make the code a little shorter in the connectionName property
public static bool IsLocalHost
{
    get
    {
        return System.Web.HttpContext.Current.Request.Url.Host.IsLike("%localhost%");
    }
}

public static string connectionName 
{
    get
    {
        // we have to perform this check so we can use migrations with sending the 
        // system into a fit of rage.
        string name = "name=DefaultConnection";
        if (HttpContext.Current != null)
        {
            name = string.Concat("name=",(IsLocalHost) 
                    ? "DevConnection" 
                    : "DefaultConnection"; 
            // if you have other connection strings, you can add additional code to 
            // select the appropriate one. The code below is how I get a connection string
            // when I'm doing development at work.
            
            //if (name.IsLike("%DevConnection") && !Environment.MachineName.IsLike("homemachinename"))
            //{
            //    name = name.Replace("DevConnection", "WorkConnection");
            //}
        }
        else
        {
            name =  "name=DevConnection";
            // again, here's additional code for my own situation
            //if (!Environment.MachineName.IsLike("homemachinename"))
            //{
            //    name = name.Replace("DevConnection", "WorkConnection");
            //}
        }
        return name;
    }
}

And my context object's constructor looks like this:

C#
public ICSContext() : base(connectionName)
{
    if (HttpContext.Current != null)
    {
        ...
    }
}

What I found was that when you run update-database in the Nuget console, an exception would be thrown if the HttpContext.Current object was null, which it would be if you're running update_database. Because of this, I had to check to make sure it wasn't null when setting the static property.

Another point of concern is that if you use authentication in your web app (and have mutiple possible connection strings, you'll also need to include similar code in the ApplicationDbContext. At the end of the connectionName property, remember to remove "name=" from the name variable.

You may also have noticed the same check inside the constructor. In my case, I'm setting mapped paths for default images, and that requires use of the HttpContext.Current object (which might be null). It was therefore necessary for me to put the same check in the constructor.

The IsLike method in the property is an extension method I've been using for years. It performs the same functionality as the T-SQL Like function, and uses the same wildcard notations as well. I've included it below if you're interested in using it.

C#
/// <summary>
/// Determines whether this string is "like" the specified string. Performs 
/// a SQL "LIKE" comparison. 
/// </summary>
/// <param name="str">This string.</param>
/// <param name="like">The string to compare it against.</param>
/// <returns></returns>
public static bool IsLike(this string str, string pattern)
{
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(pattern))
    {
        return false;
    }

    // this code is much faster than a regular expression that performs the same comparison.
    bool isMatch          = true;
    bool isWildCardOn     = false;
    bool isCharWildCardOn = false;
    bool isCharSetOn      = false;
    bool isNotCharSetOn   = false;
    bool endOfPattern     = false;
    int  lastWildCard     = -1;
    int  patternIndex     = 0;
    char p                = '\0';
    List<char> set        = new List<char>();

    for (int i = 0; i < str.Length; i++)
    {
        char c = str[i];
        endOfPattern = (patternIndex >= pattern.Length);
        if (!endOfPattern)
        {
            p = pattern[patternIndex];
            if (!isWildCardOn && p == '%')
            {
                lastWildCard = patternIndex;
                isWildCardOn = true;
                while (patternIndex < pattern.Length && pattern[patternIndex] == '%')
                {
                    patternIndex++;
                }
                p = (patternIndex >= pattern.Length)  ? '\0' : p = pattern[patternIndex];
            }
            else if (p == '_')
            {
                isCharWildCardOn = true;
                patternIndex++;
            }
            else if (p == '[')
            {
                if (pattern[++patternIndex] == '^')
                {
                    isNotCharSetOn = true;
                    patternIndex++;
                }
                else 
                {
                    isCharSetOn = true;
                }

                set.Clear();
                if (pattern[patternIndex + 1] == '-' && pattern[patternIndex + 3] == ']')
                {
                    char start    = char.ToUpper(pattern[patternIndex]);
                    patternIndex += 2;
                    char end      = char.ToUpper(pattern[patternIndex]);
                    if (start <= end)
                    {
                        for (char ci = start; ci <= end; ci++)
                        {
                            set.Add(ci);
                        }
                    }
                    patternIndex++;
                }

                while (patternIndex < pattern.Length && pattern[patternIndex] != ']')
                {
                    set.Add(pattern[patternIndex]);
                    patternIndex++;
                }
                patternIndex++;
            }
        }

        if (isWildCardOn)
        {
            if (char.ToUpper(c) == char.ToUpper(p))
            {
                isWildCardOn = false;
                patternIndex++;
            }
        }
        else if (isCharWildCardOn)
        {
            isCharWildCardOn = false;
        }
        else if (isCharSetOn || isNotCharSetOn)
        {
            bool charMatch = (set.Contains(char.ToUpper(c)));
            if ((isNotCharSetOn && charMatch) || (isCharSetOn && !charMatch))
            {
                if (lastWildCard >= 0) 
                {
                    patternIndex = lastWildCard;
                }
                else
                {
                    isMatch = false;
                    break;
                }
            }
            isNotCharSetOn = isCharSetOn = false;
        }
        else
        {
            if (char.ToUpper(c) == char.ToUpper(p))
            {
                patternIndex++;
            }
            else
            {
                if (lastWildCard >= 0) 
                {
                    patternIndex = lastWildCard;
                }
                else
                {
                    isMatch = false;
                    break;
                }
            }
        }
    }
    endOfPattern = (patternIndex >= pattern.Length);

    if (isMatch && !endOfPattern)
    {
        bool isOnlyWildCards = true;
        for (int i = patternIndex; i < pattern.Length; i++)
        {
            if (pattern[i] != '%')
            {
                isOnlyWildCards = false;
                break;
            }
        }
        if (isOnlyWildCards) 
        {
            endOfPattern = true;
        }
    }
    return (isMatch && endOfPattern);
}

History

05 Jan 2018 - Updated static properties to reflect weirdness encountered with the "update-database" command. It woud throw "can't connect to the database" and/or "database already exists" exceptions.

11 Nov 2017 - Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
-- There are no messages in this forum --