Windows Azure Mobiles Services C# Backend (EN version)

Hi,

I had the opportunity to present during our main event The Techdays, in France, a new feature in Windows Azure Mobile Services : The ability to create a C# backend within Mobile Services, while my colleague Benjamin Talmard presented an other new feature : The Azure Directory authentication integration within Mobile Services.

A few days later, Scott Guthrie made the announcement of those features (and some other)

Today we will focus deeper in the development process of a complete C# backend.

For purpose, here is some useful links :

You will find the complete source code in a zip package here : FabrikamFiberArticle.zip

image image

Backend creation and deployment

This section is largely treated in the MSDN tutorials, so we will focus on the development part.

The C# backend project

Here is my final project :

image5

  • WebApiConfig : Web API configuration of our Backend. You will find the creation and initialization of the database.
  • ServiceTicketController  et ImageController : Table controller. We can compare them to the data components in Mobile Services node.js
  • CustomerController : It is an ApiController : Comparable to the API services in Mobile Services node.js
  • FabrikamFiberContext : Database context, Code First item.
  • Customer, Image, ServiceTicket : Represents our data models.

 

We will go deeper within the two main controllers : TableController and ApiController.

TableController

All the classes inheriting TableController<T> are in charge of interacting with your HTTP requests and manage your tables

If we made a simple comparison, we can say that TableController of C# backend is equivalent to the data’s script in Mobiles Service node.js :

image_thumb4

TableController contains three essentials elements :

  1. FabrikamFiberContext : It’s the DbContext, required to query our datas.
  2. EntityDomainManager : It’s an helper implementing IDomainManager. The domain managers are primarily intended for mapping the table controller CRUD to a backend.
  3. Services : ApiServices class in charge of various operations like Log, Push, accessing configuration ….
 FabrikamFiberContext context = new FabrikamFiberContext(Services.Settings.Name.Replace('-', '_'));
DomainManager = new EntityDomainManager<ServiceTicket>(context, Request, Services);

Every calls in the TableController are nomenclatured to intercept HTTP requests :

 // GET tables/ServiceTicket
public IQueryable<ServiceTicket> GetAllServiceTickets()
{
    return Query();
}

// GET tables/ServiceTicket/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<ServiceTicket> GetServiceTicket(string id)
{
    return Lookup(id);
}

// PATCH tables/ServiceTicket/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<ServiceTicket> PatchServiceTicket(string id, Delta<ServiceTicket> patch)
{
    return UpdateAsync(id, patch);
}

// POST tables/ServiceTicket/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<IHttpActionResult> PostServiceTicket(ServiceTicket item)
{
    ServiceTicket current = await InsertAsync(item);
    return CreatedAtRoute("Tables", new { id = current.Id }, current);
}

// DELETE tables/ServiceTicket/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteServiceTicket(string id)
{
    return DeleteAsync(id);
}

IDomainManager

Each TableController has its own domain manager (IDomainManager)
To make it simple, we can say :

  1. TableController : Maintainer of objects structure (ITableData),  Intercepts HTTP requests, contains services like ApiServices
  2. IDomainManager : Map and implement all the table controller CRUD operations from within your backend database.

 

 public interface IDomainManager<TData> where TData : class, ITableData
{

    Task<bool> DeleteAsync(string id);

    Task<TData> InsertAsync(TData data);

    SingleResult<TData> Lookup(string id);
    Task<SingleResult<TData>> LookupAsync(string id);
    IQueryable<TData> Query();
    Task<IEnumerable<TData>> QueryAsync(ODataQueryOptions query);
    Task<TData> ReplaceAsync(string id, TData data);
  
    Task<TData> UpdateAsync(string id, Delta<TData> patch);
}

We can imagine to create other kind of domain manager like MongoDB domain manager, or maybe Table storage domain manager or even a SQLite domain manager …

For now, you have access to two domain managers implementing IDomainManager :

  1. EntityDomainManager : Standard domain manager to manage SQL Azure tables
  2. MappedEntityDomainManager : In charge to manage SQL tables with entities not directly mapped to the tables structure.

ApiController

All the controllers inheriting from ApiController can be compare to the API part in Mobile Services node.js :

image_thumb3

You have all the flexibility with ApiController to implement your own services. You can create methods (intercepting POST, GET and others operations) and create custom routes.

To work with ApiController, you don’t need any domain manager, just your own DbContext.

You could (but it’s not mandatory) use RoutePrefix and Route to customize your urls :  

 [RoutePrefix("api/Customers")]
public class CustomerController : ApiController
{
    public ApiServices ApiServices { get; set; }
    FabrikamFiberContext context;

    protected override void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);
        context = new FabrikamFiberContext(ApiServices.Settings.Name.Replace('-', '_'));

    }

    [RequiresAuthorization(AuthorizationLevel.Application)]
    public void Get()
    {
        ApiServices.Log.Error("Trying access API with GET ");

        this.Request.CreateBadRequestResponse("Get Customers not allowed. Try /all ");
    }

    [Route("all")]
    [RequiresAuthorization(AuthorizationLevel.Application)]
    public IQueryable<Customer> GetAll()
    {
        return context.Customers;
    }
}

In this sample, I explicitly forbid the use of GET request, and send back a bad request response.

Here is a full example of a merge method :

 [Route("merge")]
[RequiresAuthorization(AuthorizationLevel.Application)]
public Customer MergeCustomer(Delta<Customer> patch)
{
    Customer current;

    // Get partial entity and the Id
    var tmp = patch.GetEntity();

    if (tmp == null)
    {
        ApiServices.Log.Error("Trying Merge customer is in error : Entity not valid ");

        throw new HttpResponseException
                (this.Request.CreateBadRequestResponse("Entity is not valid"));

    }

    var customerId = tmp.Id;

    if (string.IsNullOrEmpty(customerId))
    {
        // get entity
        current = patch.GetEntity();

        // Insert new customer
        if (String.IsNullOrEmpty(current.Id) || current.Id == Guid.Empty.ToString())
            current.Id = Guid.NewGuid().ToString();

        context.Customers.Add(current);

        ApiServices.Log.Info("Customer created with Id : " + current.Id.ToString());
    }
    else
    {
        current = context.Customers.Find(new[] { customerId });
        if (current == null)
        {
            // insert customer
            context.Customers.Add(current);
            ApiServices.Log.Info("Customer created with Id : " + current.Id.ToString());
        }
        else
        {
            // update original
            patch.Patch(current);
            ApiServices.Log.Info("Customer updated with Id : " + current.Id.ToString());

        }
    }

    // Check properties
    if (String.IsNullOrEmpty(current.FirstName) || string.IsNullOrEmpty(current.LastName))
    {
        ApiServices.Log.Warn("FirstName and LastName are mandatory for merging a customer");
        throw new HttpResponseException
                (this.Request.CreateBadRequestResponse("FirstName and LastName are mandatory"));

    }

    // save entity
    context.SaveChanges();

    return current;

}

Note :

  1. Delta<T> : Is in charge to provide a partial entity. Useful when you send a request with only the properties to update.
  2. Delta<T>.GetEntity() : In charge to get the complete entity from within the Delta<T> object (all the properties not provided are set to default)
  3. Delta<T>.Patch(item) : In charge to merge an existing entity and the Delta<T> object. Each modified property are set to Modified state. So usefule for update.
  4. ApiServices.Log() : Logging all the actions.

You will find all the code in the source code provide (file CustomerController.cs)

Quick tips !

Get user informations

Whether from a TableController or from an ApiController, get the user information is pretty straightforward.
The tip is to make a direct cast of the User property to ServiceUser :

 ServiceUser user = (ServiceUser)this.User;

var level = user.Level;
var identities = user.Identities;

Get a personalized entity :

We can imagine to send an entity which we don’t know the structure.
To create and send back such entity, we can rely on the JObject object from the JSON.NET Library.
The JObject can describe the properties and with the help of a JsonSerializerSettings, we can send back a personalized structure.
Here is an example to send back the user details :

 private JObject GetUserDetails()
{
    ServiceUser user = (ServiceUser)this.User;

    JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings()
    {
        DefaultValueHandling = DefaultValueHandling.Ignore,
        NullValueHandling = NullValueHandling.Ignore,
        ContractResolver = new CamelCasePropertyNamesContractResolver()
    };
    string json = JsonConvert.SerializeObject(user.Identities, Formatting.None, jsonSerializerSettings);
    JArray identities = JArray.Parse(json);
            
    return new JObject
        {
            { "id", user.Id },
            { "level", user.Level.ToString() },
            { "identities", identities }
        };
}

Get a personalized collections

This time, it’s the JArray object that will help us to get a full collections of personalized entities :

 [Route("fields")]
[RequiresAuthorization(AuthorizationLevel.Application)]
public JArray GetCustomerFields()
{
    JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings()
    {
        DefaultValueHandling = DefaultValueHandling.Ignore,
        NullValueHandling = NullValueHandling.Ignore,
        ContractResolver = new CamelCasePropertyNamesContractResolver()
    };
    var customers = from c in context.Customers select new { Id = c.Id, LastName = c.LastName, FirstName = c.FirstName };

    string json = JsonConvert.SerializeObject(customers, Formatting.None, jsonSerializerSettings);
    JArray customersArray = JArray.Parse(json);

    return customersArray;

}

Client Windows 8.1 / Windows Phone 8

From the client side, the code is exactly the same code, whether from a node.js backend or from a C# backend

You will find the complete code in the DataService class in the sample provided within this article.

MobileService object initialization :

 // This MobileServiceClient has been configured to communicate with your local
// test project for debugging purposes. Comment out this declaration and use the one
// provided below when you are done developing and deploy your service to the cloud.
MobileService = new MobileServiceClient("https://localhost.fiddler:59217");

// This MobileServiceClient has been configured to communicate with your Mobile Service's url
// and application key. You're all set to start working with your Mobile Service!
// public static MobileServiceClient MobileService = new MobileServiceClient(
//MobileService = new MobileServiceClient(
// "https://fabrikamfiber.azure-mobile.net/",
// "QMizgjEBYjDOW………….eCLLoqMralUv88"
// );

Get information from a table (TableController)

 ticketTable = Context.Current.MobileService.GetTable<ServiceTicket>();
return await ticketTable.LookupAsync(id);

Get informations from a specialized service (ApiController)

  var c = await Context.Current
             .MobileService
             .InvokeApiAsync<List<Customer>>("Customers/all", HttpMethod.Get, null);

Happy coding !

https://www.dotmim.com/SiteFiles/FabrikamFiberArticle.zip