📜 ⬆️ ⬇️

WCF RIA Services. Update data. Part 3

WCF RIA Services. Start. Part 1
WCF RIA Services. Receiving data. Part 2
WCF RIA Services. Update data. Part 3
WCF RIA Services. Implement the Model-View-ViewModel (MVVM) pattern. Part 4

In the previous lesson we learned in more detail about the possibilities of receiving data in WCF RIA Services. Today we talk about the process of updating data, which is more complex.

The introductory part is the project created in the second lesson.
')

IQueryable <T> and the expression tree magic


In the first lesson, we created a domain service that looked something like this:

public IQueryable<Task> GetTasks() { return this.ObjectContext.Tasks; } 

If you think a little about how it works, it becomes clear that the entire table is retrieved from the database with each method call.

However, this is only at first glance. Let's see what are expression trees and deferred execution?

So. When a call to the GetTasks method occurs, this does not mean that a query is being made to the database and the data is being retrieved. In fact, all that happens is the construction of an expression tree and its return as IQueryable, which simply describes what can be returned by this method. In this case, theoretically, it is possible to get the entire Tasks table. The expression tree also describes what can be returned to the client side. The actual execution of the query and the process of extracting data from the database occurs only at the moment when something is trying to change the collection that the expression tree provides / describes. However, the possibility remains to change the expression tree by the recipient after the request is sent, and this in turn can also change the results with which the collection will eventually be filled.

The ability to change the expression tree is also present immediately before the data extraction process. For example:

 TasksDomainContext context = new TasksDomainContext(); taskDataGrid.ItemsSource = context.Tasks; EntityQuery<Task> query = context.GetTasksQuery(); LoadOperation<Task> loadOp = context.Load(query.Where(t=>t.TaskId == 1)); 

In the second line, the Tasks collection is linked through the domain context, which is actually still empty, as the domain context is being formed. Then the EntityQuery is retrieved from the context. There is still no direct execution of the query and extraction of data from the database. However, EntityQuery allows you to create a tree of expressions, on the basis of which a database query will be generated on the server, after the method is called, and data will be retrieved. In this case, it is possible to extract the entire table. And only when the “Load” method is called, the changed expression tree is passed, which includes the “Where” filter, and after the query is processed, only one row will be returned, which has the value “1” in the “ID” column. An asynchronous call is made to the server side, the expression tree is transmitted, and the extraction takes place. However, even on the server side, the request will already be modified and only one row will be returned from the database. You can see for yourself by simply viewing which SQL queries will be executed when calling this method. That is, the creation of a separate method for extracting multiple lines and one line from the database is eliminated, which makes life easier for the programmer.

DomainContext caching and change tracking


The logic of the work dismantled. But before we proceed to the analysis of the code, it is necessary to sort through even some concepts of the work of WCF RIA Services. The foregoing is not all that happens behind the scenes of a domain context. For example, there is a place to be a proxy call. Any entities or their collections that you get are cached by the context of the domain on the client side. It is for this reason that it is possible, as in the example above, to bind an ItemsSource to the Tasks collection before directly executing the query. This collection will be changed to the current one, and the data in the UI will automatically be updated at the moment when the answer comes after an asynchronous call to the server. In addition to caching, the domain context stores information about any changes to the cached entity, and therefore always knows if a change, deletion, or addition occurs.

Based on everything that we have already learned, it can be concluded that it is not necessary to make server-side calls every time there is any change to the object. You can, for example, save changes to objects and then call the server part once and all changes will be correctly made and processed.

Step 1: Add a data update method to the domain service.


In the first lesson, when creating a domain service, we used the creation wizard. And if you put a tick in front of each entity in the column “Add Editing”, then we will get automatically generated methods for each entity that implement the CRUD functionality.



The code will look like this:

 public void InsertTask(Task task) { if ((task.EntityState != EntityState.Detached)) { this.ObjectContext.ObjectStateManager.ChangeObjectState(task, EntityState.Added); } else { this.ObjectContext.Tasks.AddObject(task); } } public void UpdateTask(Task currentTask) { this.ObjectContext.Tasks.AttachAsModified(currentTask, this.ChangeSet.GetOriginal(currentTask)); } public void DeleteTask(Task task) { if ((task.EntityState == EntityState.Detached)) { this.ObjectContext.Tasks.Attach(task); } this.ObjectContext.Tasks.DeleteObject(task); } 

These methods are simple wrappers for the relevant entity framework operations.

Step 2: Adding items to the UI for new actions


Add two buttons that will add a new task and save the modified collection in the database.

 <Button Name="addTaskButton" Content="Add Task" Click="addTaskButton_Click" .../> <Button Name="saveChangesButton" Content="Save Changes" Click="saveChangesButton_Click" .../> 




Step 3: Create a new task, add it to the domain context and save the changes.


Add the following code to the “Click” event handlers for the corresponding buttons:

 TasksDomainContext context = new TasksDomainContext(); private void addTaskButton_Click(object sender, RoutedEventArgs e) { taskDataGrid.ItemsSource = context.Tasks; context.Load(context.GetTasksQuery()); Task newTask = new Task { TaskName = "Deploy app", Description = "Deploy app to all servers in data center", StartDate = DateTime.Today, EndDate = DateTime.Today + TimeSpan.FromDays(7) }; context.Tasks.Add(newTask); } private void saveChangesButton_Click(object sender, RoutedEventArgs e) { context.SubmitChanges(); } 

First, a variable is added to which the domain context is bound. As mentioned earlier, the context of the domain should live so much that he could track the changes and apply them by calling the appropriate method on the server side of the application. Therefore, we have separated the calls of the methods for adding changes to our object and saving these changes.

After clicking on the “Add Task” button, its handler replaces the ItemsSource of our DataGrid, in order to replace the DomainDataSource, which we connected in the first lesson. Then the “Load” method is called to fill the domain context with the desired entity.
Next, create and populate a new Tasks object and add it to the collection of the Tasks domain context. These changes will immediately appear in the UI, since this collection implements the INotifyCollectionChanged interface. But keep in mind that all these changes have been applied, displayed and saved in the domain context cache. But they were not changed on the server part and in the database. To apply the changes, you must call the SubmitChanges method, which is called when you click on the appropriate button of our application.

When you click on the “Add Task” button, you will see that a new task has been added, however, in the “TaskId” field, the values ​​will always be “0”. However, if you click on the “SubmitChanges” button, then after a while, namely after the asynchronous call occurs, the request will be executed and the data will be refreshed and become relevant.

Asynchronous Domain Context API


I already mentioned it, but I will repeat once more. Methods such as the Load and SubmitChanges domain context APIs are invoked asynchronously. This means that they do not inhibit the work of their caller, which usually has a UI. They take the stream from the thread pool behind the scenes, make the server side call in the background, and when the call runs, they return to the calling UI and update the collection of entities and the UI itself.

All this is easy and beautiful. When it works. But in reality there is always its own fly in the ointment. Sometimes there are problems with the connection, or someone accidentally tampered with the connection string, or there are conflicts of parallelization in the background. But in spite of all possible scenarios, the need to know when the calls have been fulfilled and when it is necessary to provide the opportunity to move on does not disappear anywhere. To do this, you can use a couple of ways: use the return type or callback, which will be called when the operation is completed.

The first option is to work with the return value from the asynchronously called method. The Load method returns the LoadOperation, and the SubmitChanges method returns the SubmitOperation. They both inherit OperationBase and therefore provide a sufficient amount of information about the operation, which you can use in the process or after the operation is completed. They also trigger the “Completed” event at the end of the operation, and naturally you have the opportunity to subscribe to this event. Of course, there are various errors available, different flags triggering and much more that can be used to create an application.

As an alternative to the “Completed” event subscription, you can use the call to the overloaded “Load” or “SubmitChanges” method, which return Action and Action respectively. Pass the link to the callback function and when the operation is completed, it is automatically called.

Video for this lesson




Sources


On github

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


All Articles