Monday, December 10, 2012

Restoring a LocalDB within an MVC web application

UPDATE: EntityFramework 6 has lots of Resiliency enhancements, but one of the side effects is that this needs a tweak to keep EF from spinning up a transaction around the SQL statement.  Essentially, you have to call ExecuteSqlCommand and request that it does NOT ensure the a transaction exists with the first parameter of TransactionalBehavior.DoNotEnsureTransaction.  If still running EF 5 or below, omit that argument. 

So, as a follow-up to backing up a LocalDB database, I guess I should show the simplest path to restoring one.

So, without further adéu, I give you:

public class RestoreDatabaseModel
    {
        public HttpPostedFileBase File { get; set; }
    }

        //
        // GET: /Admin/RestoreDatabase
        [Authorize(Roles = "Admin")]
        public ActionResult RestoreDatabase()
        {
            return View(new RestoreDatabaseModel());
        }

        //
        // POST: /Admin/RestoreDatabase
        [Authorize(Roles = "Admin")]
        [HttpPost]
        public ActionResult RestoreDatabase(RestoreDatabaseModel model)
        {
            const string YOURAPPNAME = "YourAppName";
            var dbPath = Server.MapPath(String.Format("~/App_Data/Restore_{0}_DB_{1:yyyy-MM-dd-HH-mm-ss}.bak", YOURAPPNAME, DateTime.UtcNow));

            try
            {
                model.File.SaveAs(dbPath);

                using (var db = new DBContext())
                {
                    var cmd = String.Format(@"
USE [Master]; 
ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE {0} FROM DISK='{1}' WITH REPLACE;
ALTER DATABASE {0} SET MULTI_USER;"
                        , YOURAPPNAME, dbPath);
                    db.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, cmd);
                }

                 ModelState.AddModelError("", "Restored!");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", ex);
            }

            return View(model);
        }
This iteration saves the posted file based on the current date-time and the supplies the correct commands to restore the database. I leave the flushing of HttpCache to you...

Also, if you haven't extended the upload limits of a POST, you'll need this (or similar) in your web.config
<system.web>
    <httpRuntime maxRequestLength="40960" targetFramework="4.5">
    </httpRuntime>
</system.web>
This enables larger files to be uploaded (in this case, 40MB... if your database backup is bigger than that, you have no business hacking around with a LocalDB... get a real SQL Server instance to point at.

No comments: