次の方法で共有


Creating an Azure Mobile Services .NET backend from scratch

[This post originally appeared in https://blogs.msdn.com/b/azuremobile/archive/2014/04/10/creating-an-azure-mobile-services-net-backend-from-scratch.aspx. I’ll be writing Azure Mobile Service related posts in the Azure Mobile blog primarily, and cross-posting them here for people who have feeds linked to this blog only]

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.         return base.Query();
  13.     }
  14.  
  15.     public SingleResult<TodoItem> GetOne(string id)
  16.     {
  17.         return base.Lookup(id);
  18.     }
  19.  
  20.     [HttpPost]
  21.     public Task<TodoItem> InsertItem(TodoItem item)
  22.     {
  23.         return base.InsertAsync(item);
  24.     }
  25.  
  26.     [HttpPatch]
  27.     public Task<TodoItem> UpdateItem(string id, Delta<TodoItem> patch)
  28.     {
  29.         return base.UpdateAsync(id, patch);
  30.     }
  31.  
  32.     public Task DeleteItem(string id)
  33.     {
  34.         return base.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.