PageRenderTime 63ms CodeModel.GetById 1ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 1ms

/articles/mobile-services-dotnet-backend-use-existing-sql-database.md

https://github.com/deaquino/azure-content
Markdown | 609 lines | 461 code | 148 blank | 0 comment | 0 complexity | 925da1c2da10df92412818cc01319bbd MD5 | raw file
  1<properties linkid="mobile-services-dotnet-backend-use-existing-sql-database" urlDisplayName="Build a service using an existing SQL database with the Mobile Services .NET backend" pageTitle="Build a service using an existing SQL database with the Mobile Services .NET backend - Azure Mobile Services" metaKeywords="" description="Learn how to use an existing cloud or on-premises SQL database with your .NET based mobile service" metaCanonical="" services="mobile-services,biztalk-services" documentationCenter="Mobile" title="Build a service using an existing SQL database with the Mobile Services .NET backend" authors="yavorg" solutions="" manager="" editor="mollybos" />
  2
  3<tags ms.service="mobile-services" ms.workload="mobile" ms.tgt_pltfrm="mobile-multiple" ms.devlang="multiple" ms.topic="article" ms.date="01/01/1900" ms.author="yavorg" />
  4
  5# Build a service using an existing SQL database with the Mobile Services .NET backend
  6
  7The Mobile Services .NET backend makes it easy to take advantage of existing assets in building a mobile service. One particularly interesting scenario is using an existing SQL database (either on-premises or in the cloud), that may already be used by other applications, to make existing data available to mobile clients. In this case it's a requirement that database model (or *schema*) remain unchanged, in order for existing solutions to continue working.
  8
  9This tutorial consists of the following sections:
 10
 111. [Exploring the existing database model](#ExistingModel)
 122. [Creating data transfer objects (DTOs) for your mobile service](#DTOs)
 133. [Establishing a mapping between DTOs and model](#Mapping)
 144. [Implementing domain-specific logic](#DomainManager)
 155. [Implementing a TableController using DTOs](#Controller)
 16
 17<a name="ExistingModel"></a>
 18## Exploring the existing database model
 19
 20For this tutorial we will use the database that was created with your mobile service, but we will not use the default model that is created. Instead, we will manually create an arbitrary model that will represent an existing application that you may have. For full details about how to connect to an on-premises database instead, check out [Connect to an on-premises SQL Server from an Azure mobile service using Hybrid Connections](/en-us/documentation/articles/mobile-services-dotnet-backend-hybrid-connections-get-started/).
 21
 221. Start by creating a Mobile Services server project in **Visual Studio 2013 Update 2** or by using the quickstart project that you can download on the Mobile Services tab for your service in the [Azure Management Portal](http://manage.windowsazure.com). For the purposes of this tutorial, we will assume your server project name is **ShoppingService**.
 23
 242. Create a **Customer.cs** file inside the **Models** folder and use the following implementation. You will need to add an assembly reference to **System.ComponentModel.DataAnnotations** to your project.
 25
 26        using System.Collections.Generic;
 27        using System.ComponentModel.DataAnnotations;
 28
 29        namespace ShoppingService.Models
 30        {
 31            public class Customer
 32            {
 33                [Key]
 34                public int CustomerId { get; set; }
 35                
 36                public string Name { get; set; }
 37
 38                public virtual ICollection<Order> Orders { get; set; }
 39
 40            }
 41        }
 42
 433. Create an **Order.cs** file inside the **Models** folder and use the following implementation:
 44    
 45        using System.ComponentModel.DataAnnotations;
 46
 47        namespace ShoppingService.Models
 48        {
 49            public class Order
 50            {
 51                [Key]
 52                public int OrderId { get; set; }
 53
 54                public string Item { get; set; }
 55
 56                public int Quantity { get; set; }
 57
 58                public bool Completed { get; set; }
 59
 60                public int CustomerId { get; set; }
 61              
 62                public virtual Customer Customer { get; set; }
 63
 64            }
 65        }
 66
 67    You will note that these two classes have a *relationship*: every **Order** is associated with a single **Customer** and a **Customer** can be associated with multiple **Orders**. Having relationships is common in existing data models.
 68
 694. Create an **ExistingContext.cs** file inside the **Models** folder and implement it as so:
 70
 71        using System.Data.Entity;
 72
 73        namespace ShoppingService.Models
 74        {
 75            public class ExistingContext : DbContext
 76            {
 77                public ExistingContext()
 78                    : base("Name=MS_TableConnectionString")
 79                {
 80                }
 81
 82                public DbSet<Customer> Customers { get; set; }
 83
 84                public DbSet<Order> Orders { get; set; }
 85
 86            }
 87        }
 88
 89The structure above approximates an existing Entity Framework model that you may already be using for an existing application. Please note that the model is not aware of Mobile Services in any way at this stage. 
 90
 91<a name="DTOs"></a>
 92## Creating data transfer objects (DTOs) for your mobile service
 93
 94The data model you would like to use with your mobile service may be arbitrarily complex; it could contain hundreds of entities with a variety of relationships between them. When building a mobile app, it is usually desirable to simplify the data model and eliminate relationships (or handle them manually) in order to minimize the payload being sent back and forth between the app and the service. In this section, we will create a set of simplified objects (known as "data transfer objects" or "DTOs"), that are mapped to the data you have in your database, yet contain only the minimal set of properties needed by your mobile app.
 95
 961. Create the **MobileCustomer.cs** file in the **DataObjects** folder of your service project and use the following implementation:
 97
 98        using Microsoft.WindowsAzure.Mobile.Service;
 99
100        namespace ShoppingService.DataObjects
101        {
102            public class MobileCustomer : EntityData
103            {
104                public string Name { get; set; }
105            }
106        }
107
108    Note that this class is similar to the **Customer** class in the model, except the relationship property to **Order** is removed. For an object to work correctly with Mobile Services offline sync, it needs a set of *system properties* for optimistic concurrency, so you will notice that the DTO inherits from [**EntityData**](http://msdn.microsoft.com/library/microsoft.windowsazure.mobile.service.entitydata.aspx), which contains those properties. The int-based **CustomerId** property from the original model is replaced by the string-based **Id** property from **EntityData**, which will be the **Id** that Mobile Services will use.
109
1102. Create the **MobileOrder.cs** file in the **DataObjects** folder of your service project.
111
112        using Microsoft.WindowsAzure.Mobile.Service;
113        using Newtonsoft.Json;
114        using System.ComponentModel.DataAnnotations;
115        using System.ComponentModel.DataAnnotations.Schema;
116
117        namespace ShoppingService.DataObjects
118        {
119            public class MobileOrder : EntityData
120            {
121                public string Item { get; set; }
122
123                public int Quantity { get; set; }
124
125                public bool Completed { get; set; }
126
127                [NotMapped]
128                public int CustomerId { get; set; }
129
130                [Required]
131                public string MobileCustomerId { get; set; }
132
133                public string MobileCustomerName { get; set; }
134            }
135        }
136
137    The **Customer** relationship property has been replaced with the **Customer** name and a **MobileCustomerId** property that can be used to manually model the relationship on the client. For now you can ignore the **CustomerId** property, it is only used later on. 
138
1393. You might notice that with the addition of the system properties on the **EntityData** base class, our DTOs now have more properties than the model types. Clearly we need a place to store these properties, so we will add a few extra columns to the original database. While this does change the database, it will not break existing applications since the changes are purely additive (adding new columns to the schema). To do that, add the following statements to the top of **Customer.cs** and **Order.cs**:
140    
141        using System.ComponentModel.DataAnnotations.Schema;
142        using Microsoft.WindowsAzure.Mobile.Service.Tables;
143        using System.ComponentModel.DataAnnotations;
144        using System;
145
146    Then, add these extra properties to each of the classes:
147
148        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
149        [Index(IsClustered = true)]
150        [TableColumn(TableColumnType.CreatedAt)]
151        public DateTimeOffset? CreatedAt { get; set; }
152
153        [TableColumn(TableColumnType.Deleted)]
154        public bool Deleted { get; set; }
155
156        [Index]
157        [TableColumn(TableColumnType.Id)]
158        [MaxLength(36)]
159        public string Id { get; set; }
160
161        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
162        [TableColumn(TableColumnType.UpdatedAt)]
163        public DateTimeOffset? UpdatedAt { get; set; }
164
165        [TableColumn(TableColumnType.Version)]
166        [Timestamp]
167        public byte[] Version { get; set; }
168
1694. The system properties just added have some built-in behaviors (for example automatic update of created/updated at) that happen transparently with database operations. To enable these behaviors, we need to make a change to **ExistingContext.cs**. At the top of the file, add the following:
170    
171        using System.Data.Entity.ModelConfiguration.Conventions;
172        using Microsoft.WindowsAzure.Mobile.Service.Tables;
173        using System.Linq;
174
175    Then, in the body of **ExistingContext**, override [**OnModelCreating**](http://msdn.microsoft.com/library/system.data.entity.dbcontext.onmodelcreating.aspx):
176
177        protected override void OnModelCreating(DbModelBuilder modelBuilder)
178        {
179            modelBuilder.Conventions.Add(
180                new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
181                    "ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
182
183            base.OnModelCreating(modelBuilder);
184        } 
185
1865. Let's populate the database with some example data. Open the file **WebApiConfig.cs**. Create a new [**IDatabaseInitializer**](http://msdn.microsoft.com/library/gg696323.aspx) and configure it in the **Register** method as shown below.
187
188        using Microsoft.WindowsAzure.Mobile.Service;
189        using ShoppingService.Models;
190        using System;
191        using System.Collections.Generic;
192        using System.Collections.ObjectModel;
193        using System.Data.Entity;
194        using System.Web.Http;
195
196        namespace ShoppingService
197        {
198            public static class WebApiConfig
199            {
200                public static void Register()
201                {
202                    ConfigOptions options = new ConfigOptions();
203
204                    HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
205
206                    Database.SetInitializer(new ExistingInitializer());
207                }
208            }
209
210            public class ExistingInitializer : ClearDatabaseSchemaIfModelChanges<ExistingContext>
211            {
212                protected override void Seed(ExistingContext context)
213                {
214                    List<Order> orders = new List<Order>
215                    {
216                        new Order { OrderId = 10, Item = "Guitars", Quantity = 2, Id = Guid.NewGuid().ToString()},
217                        new Order { OrderId = 20, Item = "Drums", Quantity = 10, Id = Guid.NewGuid().ToString()},
218                        new Order { OrderId = 30, Item = "Tambourines", Quantity = 20, Id = Guid.NewGuid().ToString() }
219                    };
220
221                    List<Customer> customers = new List<Customer>
222                    {
223                        new Customer { CustomerId = 1, Name = "John", Orders = new Collection<Order> { 
224                            orders[0]}, Id = Guid.NewGuid().ToString()},
225                        new Customer { CustomerId = 2, Name = "Paul", Orders = new Collection<Order> { 
226                            orders[1]}, Id = Guid.NewGuid().ToString()},
227                        new Customer { CustomerId = 3, Name = "Ringo", Orders = new Collection<Order> { 
228                            orders[2]}, Id = Guid.NewGuid().ToString()},
229                    };
230
231                    foreach (Customer c in customers)
232                    {
233                        context.Customers.Add(c);
234                    }
235
236                    base.Seed(context);
237                }
238            }
239        }
240
241<a name="Mapping"></a>
242## Establishing a mapping between DTOs and model
243
244We now have the model types **Customer** and **Order** and the DTOs **MobileCustomer** and **MobileOrder**, but we  need to instruct the backend to automatically transform between the two. Here Mobile Services relies on [**AutoMapper**](http://automapper.org/), an object relational mapper, which is already referenced in the project.
245
2461. Add the following to the top of **WebApiConfig.cs**:
247
248        using AutoMapper;
249        using ShoppingService.DataObjects;
250
2512. To define the mapping, add the following to the **Register** method of the **WebApiConfig** class. 
252
253        Mapper.Initialize(cfg =>
254        {
255            cfg.CreateMap<MobileOrder, Order>();
256            cfg.CreateMap<MobileCustomer, Customer>();
257            cfg.CreateMap<Order, MobileOrder>()
258                .ForMember(dst => dst.MobileCustomerId, map => map.MapFrom(x => x.Customer.Id))
259                .ForMember(dst => dst.MobileCustomerName, map => map.MapFrom(x => x.Customer.Name));
260            cfg.CreateMap<Customer, MobileCustomer>();
261
262        });
263
264AutoMapper will now map the objects to one another. All properties with corresponding names will be matched, for example **MobileOrder.CustomerId** will get automatically mapped to **Order.CustomerId**. Custom mappings can be configured as shown above, where we map the **MobileCustomerName** property to the **Name** property of the **Customer** relationship property.
265
266<a name="DomainManager"></a>
267## Implementing domain-specific logic
268
269The next step is to implement a [**MappedEntityDomainManager**](http://msdn.microsoft.com/library/dn643300.aspx), which serves as an abstraction layer between our mapped data store and the controller which will serve HTTP traffic from our clients. We will be able to write our controller in the next section purely in terms of the DTOs and the **MappedEntityDomainManager** we add here will handle the communication with the original data store, while also giving us a place to implement any logic specific to it.
270
2711. Add a **MobileCustomerDomainManager.cs** to the **Models** folder of your project. Paste in the following implementation:
272
273        using AutoMapper;
274        using Microsoft.WindowsAzure.Mobile.Service;
275        using ShoppingService.DataObjects;
276        using System.Data.Entity;
277        using System.Linq;
278        using System.Net.Http;
279        using System.Threading.Tasks;
280        using System.Web.Http;
281        using System.Web.Http.OData;
282
283        namespace ShoppingService.Models
284        {
285            public class MobileCustomerDomainManager : MappedEntityDomainManager<MobileCustomer, Customer>
286            {
287                private ExistingContext context;
288
289                public MobileCustomerDomainManager(ExistingContext context, HttpRequestMessage request, ApiServices services)
290                    : base(context, request, services)
291                {
292                    Request = request;
293                    this.context = context;
294                }
295
296                public static int GetKey(string mobileCustomerId, DbSet<Customer> customers, HttpRequestMessage request)
297                {
298                    int customerId = customers
299                       .Where(c => c.Id == mobileCustomerId)
300                       .Select(c => c.CustomerId)
301                       .FirstOrDefault();
302
303                    if (customerId == 0)
304                    {
305                        throw new HttpResponseException(request.CreateNotFoundResponse());
306                    }
307                    return customerId;
308                }
309
310                protected override T GetKey<T>(string mobileCustomerId)
311                {
312                    return (T)(object)GetKey(mobileCustomerId, this.context.Customers, this.Request);
313                }
314                
315                public override SingleResult<MobileCustomer> Lookup(string mobileCustomerId)
316                {
317                    int customerId = GetKey<int>(mobileCustomerId);
318                    return LookupEntity(c => c.CustomerId == customerId);
319                }
320
321                public override async Task<MobileCustomer> InsertAsync(MobileCustomer mobileCustomer)
322                {
323                    return await base.InsertAsync(mobileCustomer);
324                }
325
326                public override async Task<MobileCustomer> UpdateAsync(string mobileCustomerId, Delta<MobileCustomer> patch)
327                {
328                    int customerId = GetKey<int>(mobileCustomerId);
329
330                    Customer existingCustomer = await this.Context.Set<Customer>().FindAsync(customerId);
331                    if (existingCustomer == null)
332                    {
333                        throw new HttpResponseException(this.Request.CreateNotFoundResponse());
334                    }
335
336                    MobileCustomer existingCustomerDTO = Mapper.Map<Customer, MobileCustomer>(existingCustomer);
337                    patch.Patch(existingCustomerDTO);
338                    Mapper.Map<MobileCustomer, Customer>(existingCustomerDTO, existingCustomer);
339
340                    await this.SubmitChangesAsync();
341
342                    MobileCustomer updatedCustomerDTO = Mapper.Map<Customer, MobileCustomer>(existingCustomer);
343
344                    return updatedCustomerDTO;
345                }
346
347                public override async Task<MobileCustomer> ReplaceAsync(string mobileCustomerId, MobileCustomer mobileCustomer)
348                {
349                    return await base.ReplaceAsync(mobileCustomerId, mobileCustomer);
350                }
351
352                public override async Task<bool> DeleteAsync(string mobileCustomerId)
353                {
354                    int customerId = GetKey<int>(mobileCustomerId);
355                    return await DeleteItemAsync(customerId);
356                }
357            }
358        }
359
360    An important part of this class is the **GetKey** method where we indicate how to locate the ID property of the object in the original data model. 
361
3622. Add a **MobileOrderDomainManager.cs** to the **Models** folder of your project:
363
364        using AutoMapper;
365        using Microsoft.WindowsAzure.Mobile.Service;
366        using ShoppingService.DataObjects;
367        using System.Linq;
368        using System.Net.Http;
369        using System.Threading.Tasks;
370        using System.Web.Http;
371        using System.Web.Http.OData;
372
373        namespace ShoppingService.Models
374        {
375            public class MobileOrderDomainManager : MappedEntityDomainManager<MobileOrder, Order>
376            {
377                private ExistingContext context;
378
379                public MobileOrderDomainManager(ExistingContext context, HttpRequestMessage request, ApiServices services)
380                    : base(context, request, services)
381                {
382                    Request = request;
383                    this.context = context;
384                }
385
386                protected override T GetKey<T>(string mobileOrderId)
387                {
388                    int orderId = this.context.Orders
389                        .Where(o => o.Id == mobileOrderId)
390                        .Select(o => o.OrderId)
391                        .FirstOrDefault();
392
393                    if (orderId == 0)
394                    {
395                        throw new HttpResponseException(this.Request.CreateNotFoundResponse());
396                    }
397                    return (T)(object)orderId;
398                }
399
400                public override SingleResult<MobileOrder> Lookup(string mobileOrderId)
401                {
402                    int orderId = GetKey<int>(mobileOrderId);
403                    return LookupEntity(o => o.OrderId == orderId);
404                }
405
406                private async Task<Customer> VerifyMobileCustomer(string mobileCustomerId, string mobileCustomerName)
407                {
408                    int customerId = MobileCustomerDomainManager.GetKey(mobileCustomerId, this.context.Customers, this.Request);
409                    Customer customer = await this.context.Customers.FindAsync(customerId);
410                    if (customer == null)
411                    {
412                        throw new HttpResponseException(Request.CreateBadRequestResponse("Customer with name '{0}' was not found", mobileCustomerName));
413                    }
414                    return customer;
415                }
416
417                public override async Task<MobileOrder> InsertAsync(MobileOrder mobileOrder)
418                {
419                    Customer customer = await VerifyMobileCustomer(mobileOrder.MobileCustomerId, mobileOrder.MobileCustomerName);
420                    mobileOrder.CustomerId = customer.CustomerId;
421                    return await base.InsertAsync(mobileOrder);
422                }
423
424                public override async Task<MobileOrder> UpdateAsync(string mobileOrderId, Delta<MobileOrder> patch)
425                {
426                    Customer customer = await VerifyMobileCustomer(patch.GetEntity().MobileCustomerId, patch.GetEntity().MobileCustomerName);
427
428                    int orderId = GetKey<int>(mobileOrderId);
429
430                    Order existingOrder = await this.Context.Set<Order>().FindAsync(orderId);
431                    if (existingOrder == null)
432                    {
433                        throw new HttpResponseException(this.Request.CreateNotFoundResponse());
434                    }
435
436                    MobileOrder existingOrderDTO = Mapper.Map<Order, MobileOrder>(existingOrder);
437                    patch.Patch(existingOrderDTO);
438                    Mapper.Map<MobileOrder, Order>(existingOrderDTO, existingOrder);
439
440                    // This is required to map the right Id for the customer
441                    existingOrder.CustomerId = customer.CustomerId;
442
443                    await this.SubmitChangesAsync();
444
445                    MobileOrder updatedOrderDTO = Mapper.Map<Order, MobileOrder>(existingOrder);
446
447                    return updatedOrderDTO;
448                }
449
450                public override async Task<MobileOrder> ReplaceAsync(string mobileOrderId, MobileOrder mobileOrder)
451                {
452                    await VerifyMobileCustomer(mobileOrder.MobileCustomerId, mobileOrder.MobileCustomerName);
453
454                    return await base.ReplaceAsync(mobileOrderId, mobileOrder);
455                }
456
457                public override Task<bool> DeleteAsync(string mobileOrderId)
458                {
459                    int orderId = GetKey<int>(mobileOrderId);
460                    return DeleteItemAsync(orderId);
461                }
462            }
463        }
464
465    In this case the **InsertAsync** and **UpdateAsync** methods are interesting; that's where we enforce the relationship that each **Order** must have a valid associated **Customer**. In **InsertAsync** you'll notice that we populate the **MobileOrder.CustomerId** property, which maps to the **Order.CustomerId** property. We get that value by based looking up the **Customer** with the matching **MobileOrder.MobileCustomerId**. This is because by default the client is only aware of the Mobile Services ID (**MobileOrder.MobileCustomerId**) of the **Customer**, which is different than its actual primary key needed to set the foreign key (**MobileOrder.CustomerId**) from **Order** to **Customer**. This is only used internally within the service to facilitate the insert operation.
466
467We are now ready to create controllers to expose our DTOs to our clients.
468
469<a name="Controller"></a>
470## Implementing a TableController using DTOs
471
4721. In the **Controllers** folder, add the file **MobileCustomerController.cs**:
473
474        using Microsoft.WindowsAzure.Mobile.Service;
475        using Microsoft.WindowsAzure.Mobile.Service.Security;
476        using ShoppingService.DataObjects;
477        using ShoppingService.Models;
478        using System.Linq;
479        using System.Threading.Tasks;
480        using System.Web.Http;
481        using System.Web.Http.Controllers;
482        using System.Web.Http.OData;
483
484        namespace ShoppingService.Controllers
485        {
486            public class MobileCustomerController : TableController<MobileCustomer>
487            {
488                protected override void Initialize(HttpControllerContext controllerContext)
489                {
490                    base.Initialize(controllerContext);
491                    var context = new ExistingContext();
492                    DomainManager = new MobileCustomerDomainManager(context, Request, Services);
493                }
494
495                public IQueryable<MobileCustomer> GetAllMobileCustomers()
496                {
497                    return Query();
498                }
499
500                public SingleResult<MobileCustomer> GetMobileCustomer(string id)
501                {
502                    return Lookup(id);
503                }
504
505                [AuthorizeLevel(AuthorizationLevel.Admin)]
506                protected override Task<MobileCustomer> PatchAsync(string id, Delta<MobileCustomer> patch)
507                {
508                    return base.UpdateAsync(id, patch);
509                }
510
511                [AuthorizeLevel(AuthorizationLevel.Admin)]
512                protected override Task<MobileCustomer> PostAsync(MobileCustomer item)
513                {
514                    return base.InsertAsync(item);
515                }
516
517                [AuthorizeLevel(AuthorizationLevel.Admin)]
518                protected override Task DeleteAsync(string id)
519                {
520                    return base.DeleteAsync(id);
521                }
522            }
523        }
524
525    You will note the use of the AuthorizeLevel attribute to restrict public access to the Insert/Update/Delete operations on the controller. For the purposes of this scenario, the list of Customers will be read-only, but we will allow the creation of new Orders and associating them with existing customers. 
526
5272. In the **Controllers** folder, add the file **MobileOrderController.cs**:
528
529        using Microsoft.WindowsAzure.Mobile.Service;
530        using ShoppingService.DataObjects;
531        using ShoppingService.Models;
532        using System.Linq;
533        using System.Threading.Tasks;
534        using System.Web.Http;
535        using System.Web.Http.Controllers;
536        using System.Web.Http.Description;
537        using System.Web.Http.OData;
538
539        namespace ShoppingService.Controllers
540        {
541            public class MobileOrderController : TableController<MobileOrder>
542            {
543                protected override void Initialize(HttpControllerContext controllerContext)
544                {
545                    base.Initialize(controllerContext);
546                    ExistingContext context = new ExistingContext();
547                    DomainManager = new MobileOrderDomainManager(context, Request, Services);
548                }
549
550                public IQueryable<MobileOrder> GetAllMobileOrders()
551                {
552                    return Query();
553                }
554
555                public SingleResult<MobileOrder> GetMobileOrder(string id)
556                {
557                    return Lookup(id);
558                }
559
560                public Task<MobileOrder> PatchMobileOrder(string id, Delta<MobileOrder> patch)
561                {
562                    return UpdateAsync(id, patch);
563                }
564
565                [ResponseType(typeof(MobileOrder))]
566                public async Task<IHttpActionResult> PostMobileOrder(MobileOrder item)
567                {
568                    MobileOrder current = await InsertAsync(item);
569                    return CreatedAtRoute("Tables", new { id = current.Id }, current);
570                }
571
572                public Task DeleteMobileOrder(string id)
573                {
574                    return DeleteAsync(id);
575                }
576            }
577        }
578
5793. You are now ready to run your service. Press **F5** and use the test client built into the help page to modify the data.
580
581Please note that both controller implementations make exclusive use of the DTOs **MobileCustomer** and **MobileOrder** and are agnostic of the underlying model. These DTOs are readily serialized to JSON and can be used to exchange data with the  Mobile Services client SDK on all platforms. For example, if building a Windows Store app, the corresponding client-side type would look as shown below. The type would be analogous on other client platforms. 
582
583    using Microsoft.WindowsAzure.MobileServices;
584    using System;
585
586    namespace ShoppingClient
587    {
588        public class MobileCustomer
589        {
590            public string Id { get; set; }
591
592            public string Name { get; set; }
593
594            [CreatedAt]
595            public DateTimeOffset? CreatedAt { get; set; }
596
597            [UpdatedAt]
598            public DateTimeOffset? UpdatedAt { get; set; }
599
600            public bool Deleted { get; set; }
601            
602            [Version]
603            public string Version { get; set; }
604
605        }
606
607    }
608
609As a next step, you can now build out the client app to access the service.