In my previous blog post I wrote about my experience with Azure Mobile Service and Database migrations. In this blog post I will explain step by step how to get manual migration in place.
I like to have my migrations close to the domain model which I often create as a separate class library. Then I enable database migration for the Domain project using Package Manager in Visual Studio (VS) (Tools -> NuGet Package Manager -> Package Manager Control):
By default Azure Mobile Services uses automatic migration with code first. It’s also configured to no support data loss during migrations so we need to turn this off (if you don’t care about data loss).
When database migration is enabled a Configuration.cs file will be added to the Migrations folder. To add the following to its ctor (constructor) we will disable automatic migrations and data loss is allowed during a migration:
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = true;
MigrationsAssembly = this.GetType().Assembly;
Note: The MigrationsAssembly is where the assembly where the migrations are added. In my case the Migrations are added to the Domain project.
Next step is to make sure the correct database schema is used. By default Entity Framework (EF) database migration will use “dbo” as the default database schema. When creating an Azure Mobile Service from the Visual Studio project template, mobile service will create a DbContext and use the name of the service as the default database schema for EF (so “dbo” will not be the default schema).
If you locate the class that inherits the DbContext in your Mobile Service project you will see the following code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
var schema = ServiceSettingsDictionary.GetSchemaName();
The code will make sure the EF will use the name of the mobile service as the default schema.
Note: Something to have in mind is that the name of the mobile service will be the name of the project you have created, but in Azure the name will be the name of the azure mobile service you have specified when you created the service in the Azure portal.
This key (MS_MobileServiceName) is added to appSettings in the web.config to set the name of the mobile service:
<add key="MS_MobileServiceName" value="MySampleMobileSerivceProjectName " />
If you created your project in VS with a different name than the name you gave the Azure Mobile Service, you will get some problems when running your migrations. Because of the code in the OnModelCreating method that sets the EF default schema to mobile service name, all migrations you are added with the “Add-Migration” command will use the value of the key MS_MobileServiceName as the schema. That will cause a mismatch when you publish the code to Azure. If you have more than one Mobile Service environment in Azure, like Test and Prod, you can remove the code from the OnModelCreating and let EF use the default schema “dbo”. If you only have one Mobile Service environment, just make sure the MS_MobileServiceName is set to the name you gave the Azure Mobile service in Azure and skip down to where you see the code Database.SetInitializer in this blog post.
When creating a Mobile Service in Azure, it will add a database schema to the associated database. The schema will have the name of the Mobile Service. Permissions will also be granted to this schema. The database schema that is created need to be added as part of EF database migrations, if not migrations will fail.
Note: I will recommend you to have two Azure Mobile Service environment in Azure, one for test and one for production.
If you have two environments, just remove the code that sets the default schema in the OnModelCreating.
var schema = ServiceSettingsDictionary.GetSchemaName();
Next step is to give the Azure Mobile Service database user permission to perform actions against the “dbo” schema. When a Mobile Service is created in the Azure portal, a random database use will be created and added to the associated database. This user will have its default schema set to the name of the Mobile Service. We need to make sure that user will have its default schema change to “dbo” and some other permission against the “dbo” schema, like ALTER, SELECT, UPDATE and DELETE etc.
To make sure the database user created by Azure Mobile service gets the correct permission against the “dbo” schema, connect to the database (for example through the Microsoft SQL Server Management) and go to Users under Security of your database. You will see a user with a lot of random character and end with “LoginUser”. That is the user created by the Mobile Service. Create a new Query and write (replace <UserName> with the created Azure Mobile service user):
ALTER USER <UserName> WITH DEFAULT_SCHEMA = dbo;
Then the following to give the user permissions to perform action against the “dbo” schema:
GRANT INSERT ON SCHEMA :: dbo TO <UserName>
GRANT SELECT ON SCHEMA :: dbo TO <UserName>
GRANT ALTER ON SCHEMA :: dbo TO <UserName>
GRANT DELETE ON SCHEMA :: dbo TO <UserName>
GRANT UPDATE ON SCHEMA :: dbo TO <UserName>
GRANT CONTROL ON SCHEMA :: dbo TO <UserName>
GRANT EXECUTE ON SCHEMA :: dbo TO <UserName>
The last step is to make sure the Database Migration will be added while the Azure Mobile service starts. By doing that, add the following code to the WebApiConfig.cs in the App_Start folder of your Mobile Service project:
var migrator = new DbMigrator(new Configuration());
Note: There is probably already a Database.SetInitializer in you code, just replace it with the code above, just make sure you specify your name of the EntityContext.
The SetInializer method will null as an argument will make sure no migrations will take place while there are some changes to your model the first time your code touches the EF DbContext. The code will disable that. Instead the DbMigrator will be used to update the database while the Azure Mobile Service is started.
If everything is done correctly, you can now use manual database migrations, the key thing to get it working is to give the correct user the right permissions to a database schema.
If you want to know when I will post a new blog post, please follow me on twitter @fredrikn