This article is Part 2 of the series Silverlight Applications with Telerik OpenAccess ORM
In Part 1, we introduced the basics of WCF RIA Services with Telerik OpenAccess ORM and walked you through the “Hello World” equivalent. Let’s go back to the point when you create a new Domain Service:
This is the Telerik OpenAccess Domain Service template. It helps you to create Telerik OpenAccess Domain Service class for WCF RIA Services applications. Once you select the template and click add, you will be presented with a dialog that lets you select a number of options for your service, including the most important option - what domain model it will expose.
First, you should specify a Domain service class name. This is the name of your service. The Enable client access option must be checked. It will add the EnableClientAccessAttribute attribute to your domain service class to indicate that it is visible to the client tier. In the Available DataContext classes drop-down, you should choose among the Telerik OpenAccess Domain Models in your project. Finally Enable all entities that you want to be exposed by the domain service. The Enable Edit option specifies whether the entity will be read-only or not. Once you click OK, your domain service will be created and added to the server project.
Basically, in order to perform CUD operations, your domain service class should expose methods for insert, update, and delete. If you use the Telerik OpenAccess Domain Service template, then those methods will be added automatically. However, if you create a domain service by hand, like it is described in Part 1 of the series, then you have to add those methods manually.
[EnableClientAccess()]public class NorthwindDomainService : OpenAccessDomainService<NorthwindDbContext>{ public NorthwindDomainService() : base() { } public IQueryable<Customer> GetCustomers() { return this.DataContext.Customers; } public void DeleteCustomers(Customer customer) { // This is a callback method. // The actual Delete is performed internally. } public void UpdateCustomers(Customer customer) { // This is a callback method. // The actual Update is performed internally. } public void InsertCustomers(Customer customer) { // This is a callback method. // The actual Insert is performed internally. }}In order to create a user interface for create, update and delete operations:
<UserControl x:Class="NorthwindWcfRia.MainPage" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" Margin="0,3"> <Button x:Name="btnSave" Content="Save" Click="btnSave_Click" /> <Button x:Name="btnAdd" Content="Add" Margin="7,0" Click="btnAdd_Click" /> <Button x:Name="btnDelete" Content="Delete" Click="btnDelete_Click" /> </StackPanel> <data:DataGrid Name="CustomerGrid" Grid.Row="1" /> </Grid> </UserControl>
In the code-behind page for MainPage.xaml, add event handlers for the click event of the buttons.
using System.ServiceModel.DomainServices.Client; using System.Windows; using System.Windows.Controls; using NorthwindWcfRia.Web; namespace NorthwindWcfRia { public partial class MainPage : UserControl { private NorthwindDomainContext dbContext = new NorthwindDomainContext(); public MainPage() { InitializeComponent(); LoadOperation<Customer> loadOperation = dbContext.Load( dbContext.GetCustomersQuery()); loadOperation.Completed += (s, a) => { CustomerGrid.ItemsSource = dbContext.Customers; }; } private void btnSave_Click(object sender, RoutedEventArgs e) { } private void btnAdd_Click(object sender, RoutedEventArgs e) { } private void btnDelete_Click(object sender, RoutedEventArgs e) { } } } private void btnSave_Click(object sender, RoutedEventArgs e) { if (this.dbContext.HasChanges == false) return; dbContext.SubmitChanges(); }
private void btnDelete_Click(object sender, RoutedEventArgs e){ if (this.CustomerGrid.SelectedItem == null) return; Customer customerToDelete = this.CustomerGrid.SelectedItem as Customer; this.dbContext.Customers.Remove(customerToDelete);}
private void btnAdd_Click(object sender, RoutedEventArgs e) { Customer newCustomer = new Customer(); newCustomer.CustomerID = "ABCDE"; newCustomer.CompanyName = "MyCompany"; newCustomer.City = "MyCity"; newCustomer.ContactName = "MyContactName"; dbContext.Customers.Add(newCustomer); }
Now you are ready to create, update and delete customers, and use the DomainContext class to submit changes back to the server.
You can add validation attributes to properties and persistent classes to enforce validation. WCF RIA Services provide several built-in validation attributes that perform common validation checks, and provide the CustomValidationAttribute attribute so you can specify custom validation checks. The default validation attributes in WCF RIA Services are:
You add the validation attributes to persistent classes in the server project and those validation attributes are propagated to their generated client entity representations. At run time, the validation rules are applied to data from the user. You must add metadata classes to add validation attributes. Note that, when developing a Silverlight application with WCF RIA Services and Telerik OpenAccess ORM, Domain Services automatically generate validation rules by using database attributes such as required fields or maximum string length.
To add a validation attribute, you need to perform the following steps:
On the properties or the persistent class that you want to validate, add the validation attributes that perform validation. The following example shows the RequiredAttribute, RegularExpressionAttribute and StringLengthAttribute attributes applied to the Customer properties.
However, sometimes the standard validation attributes don’t offer enough flexibility. In these scenarios you need to use the CustomValidation attribute as it is shown in the instructions below: The following example shows a class called CustomerValidator with a method named IsCustomerValid that validates a Customer object. When the data is not valid you should return the error message and the name of the property that failed the validation. On the persistent class or property that you want to validate, add the CustomValidationAttribute attribute, passing the type of the validation object and the name of the method that performs the validation. [MetadataTypeAttribute(typeof(Customer.CustomerMetadata))] public partial class Customer { internal sealed class CustomerMetadata { public CustomerMetadata() { } public int CustomerID { get; set; } [Required] [StringLength(10)] public string DrvLicNumber { get; set; } [StringLength(50)] public string FullName { get; set; } [RegularExpression("abc")] public string Address { get; set; } // .... } }
using System.ComponentModel.DataAnnotations; namespace OA.SL4.RIA.Demo.Web { public class CustomerValidator { public static ValidationResult IsCustomerValid( Customer customerToValidate, ValidationContext context) { if (customerToValidate.ZIPCode.EndsWith("Net") == false) return new ValidationResult( "ZipCode must end with 'Net'", new string[] { "ZipCode" } ); else return ValidationResult.Success; } } } [CustomValidation(typeof(CustomerValidator), "IsCustomerValid")] [MetadataTypeAttribute(typeof(Customer.CustomerMetadata))] public partial class Customer { internal sealed class CustomerMetadata { public CustomerMetadata() { } } } Final Words
In Part1 and Part2 of the series, we introduced the basics of WCF RIA Services. In the next article, the Telerik Data Service Wizard will be introduced, so stay tuned.