📜 ⬆️ ⬇️

WCF RIA Services. Receiving data. Part 2

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

General information


In the first part, we reviewed what kind of WCF RIA Services was for the beast, and created an application that can be briefly called “Hello world”. Everything was very simple, beautiful, as well as drag and drop. However, to create real, large and functional applications, there is a lot more to learn, for example, what features are available in the client side, and how to use them. In this lesson we will dig a little deeper in the field of data acquisition on the client side. You will also learn about conventions that are used for methods that receive data defined in the server side, how to configure them using attributes. I will talk about some software implementations that can be used to obtain data on the client side and how to use data that uses sources other than the Entity Framework.

The starting point for studying the above is a project that was created in the previous, first part.

Step 1: Add a parameterized query to the domain service


After we created the domain service in the previous lesson, it looked like this:
public IQueryable<Task> GetTasks() { return this.ObjectContext.Tasks; } 

This method returns all the entries from the Tasks table as an enum type IQueryable and takes no arguments. If there is a need to get a more specific request on the client side, then you need to add a method to the domain service, which in turn will be automatically available to the client. It is also possible to return data of type IEnumerable, but if the main data provider (Entity Framework or LINQ to SQL) returns IQueryable, then this will need to be implemented separately.
')
Now we are modernizing our application in such a way that we can add the ability to receive events in a given date range. To do this, add the following code to the domain service:
 public IQueryable<Task> GetTasksByStartDate( DateTime lowerDateTimeInclusive, DateTime upperDateTimeInclusive) { return this.ObjectContext.Tasks.Where( t => t.StartDate >= lowerDateTimeInclusive && t.StartDate <= upperDateTimeInclusive); } 

Step 2: Change the UI to get specific data.


For this you need to add a few elements to our page. Open MainPage.xaml. Move the grid so that there is free space above it to add elements. Using “Drag and drop” add two TextBox and one Button from the Toolbar. Name the created elements accordingly: lowerDate, upperDate, searchButton.



Change the Content button to “Search By Date”.


Step 3: Get the result of the query on the client using DomainContext


As you remember, DomainContext is an auto-generated code that reflects the capabilities of the server side of the application. That is, if the domain service is called TasksDomainService, then the client part of the application is called TasksDomainContext. It provides the ability to access the server part in asynchronous mode, as well as save changes that were made on the client side.

So, every time when entities are received from the server by executing queries through the domain context, it also stores references to these objects and some other additional information that helps track changes. And in the case of changes in these objects, the domain context will send to the server only those entities that need to be updated. About this in the next lesson. Now let's focus on getting the data.

Add a “Click” event to the button you just created. And in the event handler add the following code:
 private void searchButton_Click(object sender, RoutedEventArgs e) { DateTime lowerDateVal; DateTime upperDateVal; GetDates(out lowerDateVal, out upperDateVal); TasksDomainContext context = new TasksDomainContext(); taskDataGrid.ItemsSource = context.Tasks; EntityQuery<Task> query = context.GetTasksByStartDateQuery(lowerDateVal, upperDateVal); LoadOperation<Task> loadOp = context.Load(query); } 

Attention should be paid to the last 4 lines of the code, since the first 3 are simply the receipt of the entered date from the corresponding fields, and looks like this:
  private void GetDates(out DateTime lowerDateVal, out DateTime upperDateVal) { lowerDateVal = DateTime.MinValue; upperDateVal = DateTime.MaxValue; if (!string.IsNullOrWhiteSpace(lowerDate.Text)) { lowerDateVal = DateTime.Parse(lowerDate.Text); } if (!string.IsNullOrWhiteSpace(upperDate.Text)) { upperDateVal = DateTime.Parse(upperDate.Text); } } 

In order to be able to call the server part, you need to create an instance of the domain context. Most likely, you will want to create this instance in such a way that it would live as long as necessary, because it is thanks to it that all the necessary information is kept to track changes. For example, when using the MVVM pattern, it is a good idea to create and move an instance of the domain context to the view model property. In more detail we will talk on this subject in the fourth lesson.

The domain context provides collections of entities, which in turn include sets of collections returned by the service domain after the processing of requests. At this stage, the service domain provides only a collection of data from the Tasks table.

Note that this code replaces the ItemsSource in the DataGrid with the Tasks collection before any data request is made. This is necessary because the requests sent by the domain context are asynchronous and will replace the contents of the collection only after the response from the server arrives. This collection implements the INotifyCollectionChanged interface and is able to trigger events directed to the DataGrid (or any associated item) to update its contents.

Then comes the EntityQuery from the context with arguments that are passed to the corresponding method on the server. That is, here we indicate what we would like to receive, but the call is not yet happening. Finally, the LoadOperation is retrieved from the context by calling the Load method. This is where the server directly accesses the shadow stream, and when the request is executed on the server and the answer comes with the necessary data, the UI will automatically update all the information to the current one.

In principle, this is the same thing that happens under the cover of the DomainDataSource from the first lesson.

Step 4: Adding a method that returns a single object


So that the method does not return the collection, it is necessary to declare it as necessary. Further, after compilation, it will also be available on the client side:
 public Task GetTask(int taskId) { return this.ObjectContext.Tasks.FirstOrDefault(t => t.TaskId == taskId); } 

Settings Convention


There is such a tendency in. NET programming, when the amount of code required to explicitly prescribe to provide the necessary settings is reduced, but you have to follow some naming rules. Perhaps you have not noticed this yet by skimming through the domain service code, but there it already applies. This is not very obvious on methods that extract data, since a method with any name will work equally well. However, for the update, delete and create operations, you need to follow certain rules, based on which WCF RIA Services searches for and executes the necessary methods, for example, such as UpdateTask, InsertTask, DeleteTask. It is possible to indicate several options for each operation.

And do not forget about the possibility of customization using the attributes. For example, for the party performing queries, you can decorate methods with the [Query] attribute, which will make your code more readable and easy to understand. It must be remembered that the type of the return value also says a lot about the purpose of the method. For example, the method that returns IQueryable, IEnumberable, is unambiguously responsible for retrieving data. However, the advantage of using the Query attribute is that it also supports some additional parameters, such as the maximum number of returned results, which will limit their number, even if the database returns more than necessary after the query.

Custom Domain Services


And what if you do not want to use the Entity Framework? Alternatively, you can use LINQ to SQL. I would advise using the Entity Framework with new projects. And if possible with the old.

LINQ to SQL is supported in WCF RIA Services via the Toolkit.

However, many use other data access strategies, such as nHibernate and other data sources, such as Oracle, MySQL, and many others. All this can also be used in WCF RIA Services, but you need to define data objects as simple entities and use them further in the code as you see fit. This approach is called POCO (Plain Old CLR Objects) domain services.

To create such a domain service, the domain class of the service must be inherited directly from the DomainService, and not from the LinqToEntitiesDomainService. In the creation wizard, create a service domain, select “Empty Domain Service Class” in the “Available Context Classes” drop-down menu. Then define the entities, return the IEnumerable collections of these entities if your data source does not support IQueryable and do everything you need in methods based on the data source.



Next, you must design your service according to the source data and the objects you work with. These objects must have the properties of other objects or sets of built-in .NET supported types.

The key with which you frame your entities determines that they must have key properties that uniquely identify them. Usually "int" or "Guid". Specify this property as an attribute [Key].

Below is the code that shows how to declare a POCO domain of a service and its entities:
 public class Foo { [Key] public int FooId { get; set; } public string Name { get; set; } } [EnableClientAccess()] public class MyPOCODomainService : DomainService { public IEnumerable<Foo> GetFoos() { return new List<Foo> { new Foo { FooId = 42, Name = "Fred" } }; } } 


In addition to the [Key] attribute, if you have properties that are themselves entity types (related entities or unions), then you need to add properties to this entity, for example, an ID that maps key fields. In addition, directly at the entity property, you must specify the [Association] attribute, which shows the ID of the property that establishes connections (relations) and reports that this is the foreign key. It is still necessary to specify the attribute [Include] for the property of the entity, so that the child entity is also always retrieved when the parent is retrieved using RIA Services.

After launching the project, you will receive such a window where you can select by date:



Video for this lesson




Sources


On github

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


All Articles