Search code examples
asp.net-mvcef-code-firstsimplemembership

How do I make WebSecurity use the specified connection string in update-database?


I have added SimpleMembership to my MVC4 application which uses EF code first.

I wanted to add a couple of users to my database as seed data. When I ran Update-Database the first time everything went fine: my users and passwords were set correctly.

However when I try running it on my alpha server (from visual studio) it does not add my users.

To do this I issue the command

update-database -ConnectionString "<my alpha connection string>"

The schema will be updated, but the seed method runs without adding any data.

After some digging I modified my seed method from:

if (!WebSecurity.Initialized)
    WebSecurity.InitializeDatabaseConnection("TestData", "UserProfile", "UserId", "UserName", true);

Where TestData is the name of my connection string in the web.config file

to:

if (!WebSecurity.Initialized)
    WebSecurity.InitializeDatabaseConnection("<actualConnectionString>","System.Data.SqlClient", "UserProfile", "UserId", "UserName", true);

That added the data to my alpha database.

So it would appear that when I issue an Update-Database command, WebSecurity is ignoring that connection string and just trying to do it locally.

Is there a setting or option that I'm missing?

Edit This seeding is done in the Configuration.cs Seed method.


Solution

  • Disclaimer. There could be a few things tripping you up, so rolling the dice ...

    Update-Database sets the connection string to use in the migrations (the Up and Down methods), but it doesn't impact any connection string you have "compiled into" either your data context or your calls to initialise SimpleMembership.

    Therefore you can use the config file to specify both the connection strings, and an appSettings entry with the name of the connection string to use at any one time (hard to explain in words, skip to the solution below to see it in action).

    You then configure the ctor for the data context, and the call to initialise SimpleMembership (for which I recommend a single helper class) to read the connection string name from WebConfigurationManager.AppSettings["connStringName"].

    Once you have done this, before calling Update-Database change the appSettings entry for the connection string name you require. You can then rely on the Update-Database switches -StartupProjectName (to pick up the config file) and -ProjectName (to pick up the migrations and seeding) to do the rest for you.

    (This solution also ties in nicely with xml transforms that use the build configuration name to change the config file for deployment to remote servers, where you can alter the appsettings entry, or the connection string values. You don't have to use this, as Publish Profiles for publishing from the solution explorer view can also change the connection string value for you.)

    The details

    Hopefully the following describes your setup:

    • Your seeding is done in configuration.cs, which is internal sealed class Configuration : DbMigrationsConfiguration<SomeDataContext>
    • Your SomeDataContext is a public class SomeDataContext: DbContext
    • Your SomeDataContext has

      • a constructor that specifies a connection string name, or
      • the default constructor, which looks for a connection string named the same as the context class name

    Configuration.Seed uses the connection string of your DbContext, not the connection string you specify in Update-Database. While the latter is used for the commands in the migrations, SomeDataContext has it's connection string name "compiled into it", so Update-Database can't change that when it runs.

    Also, it is possible that:

    • Your users are being added through the WebSecurity and Membership class methods (as you state you are using SimpleMembership)
    • SimpleMembership is initialised from your Configuration.Seed method using WebSecurity.InitializeDatabaseConnection (hopefully, while you have multiple places to initialise this, you kept it DRY using a single class to initialise so your solution to this issue gets easier)

    Your WebSecurity.InitializeDatabaseConnection call specifies a connection string name, which again is compiled into the call, so Update-Database can't change it.

    The solutions.

    Use app/web.config to specify the connection string

    One possible solution is to make your connection string name configurable through your web.config file. You can then change the connection string before calling update-database. This has the advantage that is ties in with config transforms for deployment, so you can change the connection string and/or name of the connection string when you deploy to different servers.

    In your config file:

    <connectionStrings>
      <add name="DefaultConnection" connectionString="..." providerName="System.Data.SqlClient" />
      <add name="AlphaServer" connectionString="..." providerName="System.Data.SqlClient" />
    </connectionStrings>
    <appSettings>
      <add key="connStringName" value="DefaultConnection" />
    </appSettings>
    

    Example of a config transform for this:

    <connectionStrings>
      <add name="DefaultConnection" xdt:Locator="Match(name)" xdt:Transform="Remove"/>
    </connectionStrings>
    <appSettings>
      <add key="connStringName" value="AlphaServer" xdt:Locator="Match(key)" xdt:Transform="Replace"/>
    </appSettings>
    

    in your data context constructor:

    public DefaultDataContext()
      : base(WebConfigurationManager.AppSettings["connStringName"]) { }
    

    in your (single) call to WebSecurity.InitializeDatabaseConnection

    WebSecurity.InitializeDatabaseConnection(
      WebConfigurationManager.AppSettings["connStringName"]), ...
    

    Use compilation symbols

    You could also use compilation symbols (e.g. define ALPHA in the build configuration) to change the connection string names at compile time, but it is less preferable than the web.config appSettings entry for a number of reasons; primarily the config entry lets you keep all the coupled information in one place so is a better solution.