📜 ⬆️ ⬇️

Back end based on Microsoft Azure

I want to talk about one more topic on which there is extremely little material so far. It will be about developing a back-end based on Microsoft Azure Mobile Services. Although there are quite a few introductory articles on this topic, it is easy to see that the traditional example of TodoItems (for which the vast majority of introductions are limited) contains potential problems for a large project.

The main disadvantage of the demonstration project lies in the features of the EntityDomainManager, which forces the same classes that are used in ORM to send via JSON (say, using the Entity Framework). First, the class being serialized must inherit from EntityData, it turns out that the necessary and convenient fields are not always in the database (for example, it is identified by a string, but is it good to build indexes always on strings?). Secondly, EF has to class inheritance only for the code first scheme, which does not provide for stored procedures in the current version of the mapping (again, the question of the speed of the database). And in the end, where is the layer of logic? After all, the database structure is not necessarily identical to the external interface.

For these reasons, consider another method. I will also mention the fact that there will be no introduction to the basics here, it is assumed that the reader is already aware of the brief introduction.

So, to overcome the above disadvantages, replace the EntityDomainManager with the MappedEntityDomainManager. The latter is focused on using the AutoMapper module, which is easy to find through NuGet, besides this module is part of the Azure Mobile Services project. You will need to use its functions in the WebApiConfig.cs file.
')
For example, let's convert all the same TodoItems project and assume that we work with the database through EF based on the model first. The corresponding ORM model allows mapping of entity classes to views and stored procedures, making calls to stored procedures. When working with EF, even a simple sampling of data through stored procedures occurs at least twice as fast, all other things being equal. Consideration of the features of the database and EF is beyond the scope of this description of Micrsoft Azure, in this case the context created in the project will be used, but which will be replaceable.

Note that the MappedEntityDomainManager class, unlike the EntityDomainManager, is abstract, so there is no way to just replace the line:

DomainManager = new EntityDomainManager<TodoItem>(context, Request, Services); 

to create an instance of the MappedEntityDomainManager class. Therefore, create a class TodoItemsDomainManager. But before that, we denote the data transformation class (or serialization class) with the name TodoItemDTO in order to avoid confusion of classes of different application layers. Another generic parameter of the inherited class is the database mapping class, in which for identification we will use a numeric value, as the best option for database indices. As a result, the class will look like this:

  public class TodoItemsDomainManager : MappedEntityDomainManager<TodoItemDTO, TodoItemDA> { private readonly AzureBackendDemoContext _context; public TodoItemsDomainManager(AzureBackendDemoContext context, HttpRequestMessage request, ApiServices services) : base(context, request, services) { _context = context; } public override System.Threading.Tasks.Task<bool> DeleteAsync(string id) { return base.DeleteItemAsync(id); } public override System.Web.Http.SingleResult<TodoItemDTO> Lookup(string id) { return base.LookupEntity(c => c.Id.ToString() == id); } public override System.Threading.Tasks.Task<TodoItemDTO> UpdateAsync(string id, System.Web.Http.OData.Delta<TodoItemDTO> patch) { return base.UpdateEntityAsync(patch, id); } } 

Hereinafter, the simplest example of implementation is given, but, unlike the initial version, it is much more adapted to further development. The only thing left is that TodoItemsDomainManager does not yet know how to compare the classes TodoItemDTO and TodoItemDA. Therefore, we find the WebApiConfig class and add the following lines to the Register method:

  Mapper.Initialize(cfg => { cfg.CreateMap<TodoItemDA, TodoItemDTO>(). ForMember(dst => dst.Id, map => map.MapFrom(c => c.Id.ToString())) .ForMember(dst => dst.Text, map => map.MapFrom(c => c.Text)) .ForMember(dst => dst.Complete, map => map.MapFrom(c => c.Complete)); cfg.CreateMap<TodoItemDTO, TodoItemDA>(). ForMember(dst => dst.Id, map => map.MapFrom(c => int.Parse(c.Id))) .ForMember(dst => dst.Text, map => map.MapFrom(c => c.Text)) .ForMember(dst => dst.Complete, map => map.MapFrom(c => c.Complete)); }); 

Note that the mapping of classes is not necessarily one to one. The details of using AutoMapper will not be discussed here, since there are a lot of materials on this topic.

Now let's replace the domain manager creation line in the table controller:

 DomainManager = new TodoItemsDomainManager(context, Request, Services); 

Also, in the context class and all related classes, you need to replace TodoItemDTO with TodoItemDA and register this class in inside the OnModelCreating method:

 modelBuilder.Entity<TodoItemDA>(); 

I will make a small reservation - since in this example the real data model was not created, the initial class AzureBackendDemoInitializer inherited from DropCreateDatabaseIfModelChanges was used. In a real project that connects to the database via a connection string, I recommend implementing the IDatabaseInitializer interface, which in this case would look like this:

  public class DatabaseModelInitializer : IDatabaseInitializer<SomeDatabaseContext> { public void InitializeDatabase(SomeDatabaseContext context) { context.Set<TodoItemDA>().Load(); } } 

Run the project with the original sample test data and add the line / tables / TodoItem to the address. As a result, we will see the result of the query:

[{"id":"1","complete":false,"text":"First item"},{"id":"2","complete":false,"text":"Second item"}]

As a small addition, I will give an example of a front-end to the project. To simplify the example, the WPF-based Windows version will be used. But the implementation is also possible for mobile devices on the operating systems Android, iOS, Windows Phone and Windows RT. At the same time, the WPF project is not considered compatible with Azure Mobile Services, although in reality compatibility is present, so in the NuGet console we introduce:

Install-Package WindowsAzure.MobileServices

Then just add these links to the project. Create a class to get the data. For a query by class name, it will have to be called TodoItem, then the call will be to the appropriate controller. But this name can refer to both the data model and the local serialization class for uploading data, etc., so apply the DataTableAttribute and declare the class like this:

  [DataTable("TodoItem")] public class TodoItemDTO { [CreatedAt] public DateTimeOffset? CreatedAt { get; set; } public string Id { get; set; } [UpdatedAt] public DateTimeOffset? UpdatedAt { get; set; } [Version] public byte[] Version { get; set; } public string Text { get; set; } public bool Complete { get; set; } } 

Then the query strings will be as follows:

  public async Task Load() { MobileServiceClient client = new MobileServiceClient("http://localhost:1146/"); try { IEnumerable<TodoItemDTO> items = await client.GetTable<TodoItemDTO>().ToEnumerableAsync(); _items = items.Select(slo => new TodoItemModel { Complete = slo.Complete, Text = slo.Text }).ToList(); } catch (MobileServiceInvalidOperationException ex) { OnGotError(); } catch (TaskCanceledException ex) { OnGotError(); } } 

The layer of server logic was not considered here, but the use of inheritance from the abstract class itself indicates the possibility of invoking the necessary logical objects in the corresponding methods. For example, it was shown how to replace the string identifier with a numeric one. Let's not forget about ApiController.

Naturally, the name itself speaks of assigning a back-end not for the desktop, but for mobile devices, but the same lines can be performed in PCL for the above-mentioned operating systems. This concludes the example, for the sake of clarity, what I want to provide is the source code file for the example (correct me if it can be provided somehow better), because I myself don’t like tutorials without the full source code for the example.

Source: https://habr.com/ru/post/238529/


All Articles