次の方法で共有


Creating an Azure Mobile Services .NET backend from scratch

When learning a new feature I often like to understand the minimum pieces necessary to get it running. In the case of the .NET backend of azure mobile services, that would mean the smallest number steps required to go from an empty project to a running mobile service – no additional tooling required. Currently the item and project templates required for the mobile services backend require the VS 2013 Update 2 Release Candidate, and many people (myself included) are wary of installing non-RTM versions of VS updates, so here’s a guide on how to create a .NET backend project without those.

Set up

The minimum project which we can create for a service backend is an empty ASP.NET application (on VS 2013, select the New Project –> Web –> ASP.NET Web Application, then select Empty). At that point we’ll won’t have anything at all (except for an empty web.config file).

Now it’s time to add the reference to the .NET backend binaries. Right-click the project, select “Manage NuGet Packages…”, and search for “MobileServices”. There will be (as of today) 5 packages related to the backend, with three of them (Entity Framework Extension, Azure Storage Extension, Mongo Extension) defining extensions for specific storage providers. Let’s choose the entity framework one (Windows Azure Mobile Services .NET Backend Entity Framework Extension), and click “Install”. After being prompted to accept licenses, NuGet will install the necessary packages (there is currently a bug that a “conflict file” dialog is shown; you can safely ignore that dialog). And now we still have an empty project, with the difference that the web.config has a few more entries which we can ignore.

Defining the model

Now let’s add a class to represent the model (entity) we’ll want to expose. For simplicity sake, I’ll use the old TodoItem class. To expose a class as a table in the .NET backend, it needs to implement the ITableData interface, but the entity framework extension NuGet package defines the EntityData class which we can use as a base type.

  1. public class TodoItem : EntityData
  2. {
  3.     public string Text { get; set; }
  4.  
  5.     public bool Complete { get; set; }
  6. }

Now we need a context to hook up Entity Framework with our model types (or single type in this case). When you create a new mobile service, a new schema is defined in the associated database, and the service is granted access to that schema. In our implementation of the context we need to tell it that we use that schema (you can also use databases on different schemas, or even a completely different database, and that is a topic for another post). Normally we’d override the OnModelCreating method on the DbContext class:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3.     if (modelBuilder == null)
  4.     {
  5.         throw new ArgumentNullException("modelBuilder");
  6.     }
  7.  
  8.     if (!string.IsNullOrEmpty(this.Schema))
  9.     {
  10.         modelBuilder.HasDefaultSchema(this.Schema);
  11.     }
  12. }

But since this would be done many times, we provide one class derived from DbContext – EntityContext – which does that automatically, so we can implement our context fairly concisely:

  1. public class MyContext : EntityContext
  2. {
  3.     public MyContext()
  4.         : base()
  5.     {
  6.     }
  7.  
  8.     public MyContext(string schema)
  9.         : base(schema)
  10.     {
  11.     }
  12.  
  13.     public DbSet<TodoItem> TodoItems { get; set; }
  14. }

The EntityContext class also “teaches” EF how to use the TableColumnAttribute attribute, which you can use to customize the type of the columns created in your tables. And now that the context is created, we can define the table controller. This code is pretty much boilerplate for exposing entities as tables, and if you have the latest tooling (currently with the VS 2013 Update 2 RC) you can do that by using the item template.

  1. public class TodoItemController : TableController<TodoItem>
  2. {
  3.     protected override void Initialize(HttpControllerContext controllerContext)
  4.     {
  5.         base.Initialize(controllerContext);
  6.         var context = new MyContext(this.Services.Settings.Schema);
  7.         this.DomainManager = new EntityDomainManager<TodoItem>(context, this.Request, this.Services);
  8.     }
  9.  
  10.     public IQueryable<TodoItem> GetAll()
  11.     {
  12.         returnbase.Query();
  13.     }
  14.  
  15.     public SingleResult<TodoItem> GetOne(string id)
  16.     {
  17.         returnbase.Lookup(id);
  18.     }
  19.  
  20.     [HttpPost]
  21.     public Task<TodoItem> InsertItem(TodoItem item)
  22.     {
  23.         returnbase.InsertAsync(item);
  24.     }
  25.  
  26.     [HttpPatch]
  27.     public Task<TodoItem> UpdateItem(string id, Delta<TodoItem> patch)
  28.     {
  29.         returnbase.UpdateAsync(id, patch);
  30.     }
  31.  
  32.     public Task DeleteItem(string id)
  33.     {
  34.         returnbase.DeleteAsync(id);
  35.     }
  36. }

That’s it, the service is ready to be built and deployed. If you publish it to the mobile service (after downloading the publishing profile from the portal), you should be able to send requests to the table.

Configuring the service

The mobile service is now running, but we haven’t configured anything on how we want it to run – so the mobile service used a default bootstrapper, which initializes the service configuration with default options. If you want to change any configuration settings, define additional routes, access other extensibility points, define mapping between entity objects and DTOs, among other initialization tasks, you need to define a method to be called on startup. My colleague Henrik described the configuration in details on this blog post so I won’t cover it here, and let’s just define a WebApiConfig class to change something in our project, which calls the Initialize method on the ServiceConfig class, and changes a setting in the JSON.NET serializer.

  1. public static class WebApiConfig
  2. {
  3.     public static void Register()
  4.     {
  5.         var config = ServiceConfig.Initialize(new ConfigBuilder());
  6.         config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
  7.     }
  8. }

If you now publish the project to the server again and send a request to the server, you’ll see that the JSON responses are now indented.

Running locally

One of the great features of the .NET backend for Azure Mobile Services is the ability to test the service locally prior to deploying. But at this point, while your service can be run in the cloud, if you try to run it locally it won’t do anything (it will show an error page). There are two things which we still need to do: hook up the .NET backend to the ASP.NET pipeline running in IIS Express, and call the bootstrapper code needs to be called.

For the first part can be done by installing another NuGet package, Microsoft.Owin.Host.SystemWeb, to the project. The .NET backend is implemented using the OWIN framework, and that NuGet adds a bridge between the OWIN adapter and System.Web, which is used in IIS. No code is needed in this step.

For the second part, we need to tell the application to invoke the WebApiConfig.Register method. To do that, add a new Global Application Class (global.asax). You can remove all methods except Application_Start from the generated class. In the app start, call that method, as shown below.

  1. public class Global : System.Web.HttpApplication
  2. {
  3.     protected void Application_Start(object sender, EventArgs e)
  4.     {
  5.         WebApiConfig.Register();
  6.     }
  7. }

If you now press F5, the project will run (i.e., the “This mobile service is up and running” page will appear on your browser). But when you try to invoke any operation in the table controller, you’ll get an internal server error. Looking at the response body it will show the following error:

 System.ArgumentNullException: Value cannot be null. Parameter name: schema
   at Microsoft.WindowsAzure.Mobile.Service.EntityContext..ctor(String schema)
   at WebApplication9.MyContext..ctor(String schema) in c:\…\WebApplication9\WebApplication9\MyContext.cs:line 17
   at WebApplication9.TodoItemController.Initialize(HttpControllerContext controllerContext) in c:\…\WebApplication9\WebApplication9\TodoItemController.cs:line 18
   at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__0.MoveNext()"  

The parameter which we passed to the context constructor (this.Services.Settings.Schema) is null, which is causing this error. What is missing are configuration settings which the service adds when the backend is running on the cloud, but for local running they need to be defined in web.config. The schema can be defined with the MS_TableSchema app setting, but if that value is not defined it will default to the mobile service name (defined by the MS_MobileServiceName app setting). Another thing which we need to define is the connection string for the database used by the Entity Framework context. The EntityContext class which we used to define our DB context uses the connection setting named MS_TableConnectionString by default, so we need to define it as well.

  1. <appSettings>
  2.   <add key="MS_MobileServiceName" value="myMobileServiceName" />
  3. </appSettings>
  4. <connectionStrings>
  5.   <add name="MS_TableConnectionString"
  6.        connectionString="Data Source=(localdb)\v11.0;Initial Catalog=mylocaldatabasename;Integrated Security=True;MultipleActiveResultSets=True"
  7.        providerName="System.Data.SqlClient" />
  8. </connectionStrings>

There are many other values which you can add to the app settings – you can check them out in the ServiceSettingsKeys class.

Wrapping up

I hope this post will give you not only a recipe for starting from scratch to create a mobile service backend (but if it does, great) but also explain a little more about how the .NET backend is hosted in the Azure Mobile Service. If you have any comments or questions, feel free to leave as comments in this post, ask a new question in our MSDN forums or contact us via twitter @AzureMobile.

Comments

  • Anonymous
    April 18, 2014
    Hello, In my quest to find the answer for my query on stackoverflow, I was still searching the web when I found this article. So far I was able to follow it & created a local service from scratch. I've not yet tried hooking this with a WP project & then trying to access it in my wp8 emulator, but that's the next thing I am gonna do. Meanwhile I just wanted to say that the article was just awesome :)Supreet
  • Anonymous
    April 24, 2014
    Thanks for this info! Can we already use this .NET back-end with apps (Android/iOS) developed in Xamarin?Frank
  • Anonymous
    April 25, 2014
    Yes, it should work with Xamarin as well. As long as the server "talks" in the same language as a comparable node.js service (i.e., the REST interface), then for the client it's just another HTTP server.
  • Anonymous
    May 12, 2014
    I wish the EntityData and DocumentData classes were in a portable class library so we could keep our model in a PCL that can be used on the server and client.....
  • Anonymous
    May 21, 2014
    @MiddleTommy, as Jason mentioned in his comment at blogs.msdn.com/.../mapping-between-database-types-and-client-type-in-the-net-backend-using-automapper.aspx, we're looking at how we can enable this scenario. But what you can do for now is to share the source code files between your projects (using linked files in VS), possibly using partial classes, to use the same model definition between the client and server.
  • Anonymous
    September 15, 2014
    The service doesn't create a database.  its not clear what the database should look like.  In the JavaScript version there are __createAd, __updatedAt, __version and id.  Are those needed still?  Do I just create a database table manually?