Contents tagged with Silverlight

  • WCF RIA Services Silverlight Business Application – Using ASP.NET SiteMap for Navigation

    Note: This blog post examples is based on the WCF RIA Services PDC Beta and WCF RIA Services VS 2010 Preview, changes to the framework may happen before it hits RTM.

    This blog post will be about using the ASP.NET SiteMap together with WCF RIA Services and the Navigation feature added to Silverlight. I assume you are familiar to how the Navigation feature will work when reading this blog post, even if you don’t know you may find this blog post interesting.

    Read more...

  • Silverlight – About validation when binding to custom forms – Part 2 using Silverlight 4

    In my first part about validation and binding to custom forms I wrote about how we could with some small hack enable the Validation Tooltip for a bounded control manually when we press a button. In this post I will instead use Silverlight 4 with Commanding and the INotifyDataErrorInfo interface to “manually” show the validation tooltip for a specific control.

    Read more...

  • Silverlight – About validation when binding to custom forms

    I have notice that some people have problems about how to implement User Input validation when they don’t use controls like the DataForm or DataGrid  etc, and instead bind directly to a TextBox or other user input controls. In this post I will write about different ways to handle validations. In this post I will work with a simple View. I will later add a post where I will use some new Validation features in Siverlight 4 and where I also use the MVVM pattern and Commanding.

    Read more...

  • Silverlight - Navigate to a specific Page using a Hyperlink

    Sometimes I think it’s hard to know what I should write about, my ideas comes from different sources. Some of my posts is based on questions people are asking on forums. If someone ask one specific question, it’s a big chance that someone else have the same kind of question, so I often blog about the solution so others kind find them. So this blog post will be about how we can from a HTML page navigate to a page hosting a Silverlight application and navigate to a specific .XAML page. Probably someone else have blogged about this before but I also use my blog as a reference for my self :P

    If we have our Silverlight control hosted on a page, for example default.aspx, the Silverlight will always show the same .XAML page when the page is loaded. This is because the visual root is set within the App file when the Application is started:

    private void Application_Startup(object sender, StartupEventArgs e)
    {
         this.RootVisual = new MainPage();
    }

    We must specify a RootVisual when the application is started and that will make the Silverlight application always show a specific page when we request the the page hosting the Silverlight application. So how can we from a hyperlink navigate to our Silverlight host page and specify which page we want to show? The RootVisual is of type UIElement, so we can set any kind of UIElement to be the RootVisual, for example a Grid and add a UserControl as a Child to the Grid, like this:

    private void Application_Startup(object sender, StartupEventArgs e)
    {
         var rootGrid = new Grid();
         this.RootVisual = rootGrid;
    
         rootGrid.Children.Add(new MainPage());
    }


    The following code will still show the MainPage UserControl but this time inside of a Grid. Because we can specify any kind of UIElement as the RootVisual, and in this example a Grid, we can now dynamically add UIElements to the RootVisual’s UIElement. By using Reflection we can create an instance of the UserControl from a string, so we can for example get the name of the UserControl we want to display form a QueryString. We can use an URL like this “default.aspx?page=Page2” when we want to request the page hosting the Silverlight application and show the Page2.xaml at startup.

    Here is the code which will get the Page from a QueryString:

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        var rootGrid = new Grid();
                
        this.RootVisual = rootGrid;
                
        var pageToShow = GetPageFromQueryString();
    
        if (string.IsNullOrEmpty(pageToShow))
            pageToShow = "MainPage";
    
        var userControlToShow = LoadPage(pageToShow);
    
        rootGrid.Children.Clear();
        rootGrid.Children.Add(userControlToShow);
    }
    
    
    private static UserControl LoadPage(string pageToShow)
    {
       var appType = App.Current.GetType();
    
       var userControlTypeString = appType.Namespace + "." + pageToShow;
    
       var userControlToShowType = Type.GetType(userControlTypeString);
    
       if (userControlToShowType == null)
          throw new Exception(string.Format("Can't find the Page with the type {0}", userControlTypeString));
    
       var userControlToShow = Activator.CreateInstance(userControlToShowType) as UserControl;
    
       if (userControlToShow == null)
          throw new Exception(string.Format("Can't create an instance of Page with the type {0}", userControlTypeString));
    
       return userControlToShow;
    }
    
    
    private static string GetPageFromQueryString()
    {
        string pageToShow = null;
    
        if (HtmlPage.Document.QueryString.Count > 0 && HtmlPage.Document.QueryString.ContainsKey("page"))
             pageToShow = HtmlPage.Document.QueryString["page"];
                
        return pageToShow;
    }

    Note: This code will not do any security check etc, it's only used to give you the basic ideas and the concept. So type-injection and what kind of pages a user are allowed to see etc is not taken care of in this example, I wanted it to only show the basic concept and not make the code too complex.

    To do something similar as the above code we can instead use the Frame control on our MainPage. The Frame control can show a specific control based on a URL like this “defaul.aspx#/Page2.xaml” (This URL will show the Page2.xaml content inside of the Frame control). To use the Frame Control you only need to add the Frame control to the XAML:

    <UserControl
        xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"  
       ....>
      <Grid x:Name="LayoutRoot">
    
          <navigation:Frame></navigation:Frame>
          
      </Grid>
    </UserControl>

    I hope this blog post have given you some ideas about how you you can navigate to your Silverlight application and specify which Page (UserControl) you want to show when the application is started.

    If you want to know when I publish a new blog post, you can follow me on twitter: http://www.twitter.com/fredrikn

    Read more...

  • WCF RIA Services – Dynamically create a criteria on the client-side

    Note: This post is based on the WCF RIA Services VS 2008 PDC Beta and VS 2010 Beta 2 preview, changes can be done before the framework hits RTM.

    I have seen questions about how to dynamically create search criteria when using WCF RIA Services and LINQ. So this post is about how you can dynamically create a criteria as a string on the client-side and pass it down to the server for execution.

    There is a simple way to create a criteria on the client-side by using WCF RIA Services and LINQ and pass it down to the server for execution, here is an example:

    var dx = new CustomerDomainService();
    
    EntityQuery<Customer> query = from customer in dx.GetCustomersQuery()
                                  where customer.Country == "Sweden"
                                  select customer;
    
    dx.Load<Customer>(dx.GetCustomersQuery());

    The problem with the above code and LINQ is that LINQ is static. So if we need to dynamically create a criteria it will be hard.

    If we for example have an advanced search form with several ComboBox(s) and also TextBox(s) etc. we want to create a query based on the user’s inputs. It will require a lot of code if we use LINQ and probably an ugly condition statement. Wouldn’t it be great if we could do something like this:

    var dx = new CustomerDomainService();
    
    string criteria = string.Format("Country = {0} and CompanyName = {1}", countryComboBox.SelectedItem, companyNameTextBox.Text);
    
    dx.Load<Customer>(dx.GetCustomersQuery(criteria));

    The above code will dynamically create a criteria based on the user’s input and pass it to the GetCustomersQuery.

    The CustomerDomainService GetCustomers Query method will look something like this:

    [EnableClientAccess()]
    public class DomainService1 : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
        public IQueryable<Customer> GetCustomers(string criteria)
        {
              return this.ObjectContext.CustomerSet;
        }
    }

    As you may see the criteria isn’t used in the code, and if we want to use it we need to parse the criteria and that will require a lot of code to create different static LINQ queries based on criteria. But there is a solution, that is the point of this blog post ;)

    We can use the LINQ Dynamic Query Library to solve the problem. To use the LINQ Dynamic Query Library we need to add “using System.Linq.Dynamic” namespace to our DomainService (this after we have the LINQ Dynamic Query Library). To get it you can simply downloaded the example Lib from one of the following links:

  • VB Dynamic Query Library (included in the \Language Samples\LINQ Samples\DynamicQuery directory)
  • C# Dynamic Query Library (included in the \LinqSamples\DynamicQuery directory)

  • What I did to make it work was to add the Dynamic.cs (.vb) class from the example to my Web project. Then added the “using System.Linq.Dynamic” to get the extension method added to the Dynamic.cs (.vb) file to the IEnumerable<T> interface.

    When this is done we will have an extended version of the Where extension method which will take a criteria as a string and parse it for us to a valid query.

    Here is a simple example where I have created a criteria on the client-side as a string and pass it down to the DomainService, where I use the LINQ Dynamic Query Library:


    CustomerDomainService.cs

    [EnableClientAccess()]
    public class CustomerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
        public IQueryable<Customer> GetCustomers(string criteria)
        {
             if (!string.IsNullOrEmpty(criteria))
                 return this.ObjectContext.CustomerSet.Where(criteria, new object[] { });
    
             return this.ObjectContext.CustomerSet;
        }
    }

    MainPage.xaml.cs

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
           InitializeComponent();
    
           var dx = new CustomerDomainService();
    
           string criteria = "Country = \"Sweden\" and City <> \"Luleå\"";
    
           dx.Load<Customer>(dx.GetCustomersQuery(criteria));
    
           customersDataGrid.ItemsSource = dx.Customers;
        }
    }

    When using the LINQ Dynamic Query Library all strings need to be enclosed by “ “, so that is the reason why you see \”Sweden\”. To make the criteria more interesting I also added that the City property shouldn’t be “Luleå” (Luleå is a city far to the north in Sweden).

    The LINQ Dynamic Query Library will also work on Linq To SQL etc.

    Summary

    By using the LINQ Dynamic Query Library we can now in a easier way dynamically create a criteria based on users options, for example on a advanced search form.

    If you want to know when I post a new blog post on my blog etc, you can follow me on twitter: http://www.twitter.com/fredrikn

    Read more...

  • WCF RIA Services DomainService life-cycle and adding Transactions

    Note: This blog post will use the WCF RIA Services PDC Beta and VS2010 Preview, changes to the framework can me made before it hits RTM.

    Some questions that pops up about WCF RIA Services is how to enable transactions. So I decided to write a blog post about it, but to not make this post just a few lines I also decided to write about the DomainService life-cycle so you will know what will happen step-by-step in the DomainService after a DomainContext’s SubmitChanges is called on the client-side.

    The following images will show you the part of the DomainService life-cycle when a SubmitChanges takes place:

    image

    The first thing that will happen is the creation of the DomainService, this is done through a Domain Service Factory, you can read about it here. The Domain Service Factory will make a call to the DomainService Initialize method. Within this method you can for example set the ConnectionString on the ObjectContext’s Connection if you are using the LintToEntitesDomainService, or the DataContext if you are using the LintToSqlDomainService.

    Here is an example of changing the ObjectContext’s Connection’s ConnectionString:

    [EnableClientAccess()]
    public class CustomerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
         public override void Initialize(DomainServiceContext context)
         {
             this.ObjectContext.Connection.ConnectionString = "";            
             base.Initialize(context);
          }
    
         ...
    }

    After the Initialization the Submit method of the DomainService will be executed. This method will execute the following DomainService methods in the following order:

    1) AuthorizationChangeSet

    2) ValidateChangeSet

    3) ExecuteChangeSet

    4) PersistChangeSet

    5) ResolveChangeSet


    Note: I will only focus on the virtual method which you can overwrite.

    You can override the Submit method and do something before the Submit will take place, and also do things after the Submit is executed, for example do some pre- and post conditions like logging etc.

    Note: If you want to log exceptions that will take place during a Submit, it’s recommended that you override the OnError method and log the exception within that method.

    public override bool Submit(ChangeSet changeSet)
    {
       //do something here pre submit
    
       var submitResult = base.Submit(changeSet);
                
        //do something here post submit
    
        return submitResult;
    }


    The base.Submit will make a call to the OnError if there was any error during the process of the Submit method, so it will not take place after the Submit method. There is a try and catch block around all the above mentioned methods within the base.Submit method so if anyone of them will fail the Submit method will make a call to the OnError an then throw the exception up the chain.

    Note: The PersistChangeSet methods of the LinqToEntitiesDomainService and LinqToSqlDomainService can also make a call to the OnError method if a concurrency conflict occurs during the persistent of the entities.

    As I mentioned earlier the Submit method will make a call to some methods in a specific order, the first method is the AuthorizeChangeSet, but before taking a look at the AuthorizeChangeSet method I will focus on the ChangeSet object, which every xxxxChangeSet methods will take as an argument.


    The ChangeSet object

    The ChangeSet object is like a Unit of Work (things that should be done in a specific order). The ChangeSet will contains a list of Domain Operations that should take place and also the entity which is part of that operation, for example if you do the following operation on the client-side:
    updateCustomer.Name = "Jane Doe";
    
    var newCusotmerc = new Customer() { CustomerID = 2, Name = "Fred Doe" };
    
    domainContext.Customers.Add(newCustomer);
    
    domainContext.Customers.Remove(removeCustomer);
    
    domainContext.SubmitChanges();

    The ChangeSet’s ChangeSetEntries will have three ChangeSetEntry:


    Operation = Insert, Entity = newCustomer

    Operation = Update, Entity = updateCustomer

    Operation = Delete, Entity = removeCustomer


    The Operation property of the ChangeSetEntry has the name of the operation that should take place. A operation method is the method which you add to the DomainService to Insert, Update or Delete an entity. The method use a naming convention where the prefix of the method name is the name of the operation, the method can only take an Entity as argument, here is an example of three operation methods added to the DomainService:

    public void InsertCustomer(Customer customer)
            
    public void UpdateCustomer(Customer customer)
           
    public void DeleteCustomer(Customer customer)

    The following name can be used for the Insert operation: Insert, Create and Add, or the InsertAttribute on the method if you don’t want to use the naming convention.

    The following name can be used for the Update operation: Update. Modify and Edit, or the UpdateAttribute on the method.

    The following for the Delete operation: Delete and Remove or the DeleteAttribute.

    There is something called a “custom” method. A Custom method can’t return anything and the first argument must be an entity the rest a valid RIA Services type. The name of the method doesn’t matter. Here is an example of a Rebate custom method:


    public
    void Rebate(Customer customer)


    When a “custom” method is called on the client-side, it will not be added to the ChangeSet as a Operation will. Instead it will be added as an EntityAction to the ChangeSet’s Entity.

    The Entity property holds the Entity that should be passed to the specific Operation and also the “custom” methods that should be executed.

    I will not dig any deeper into the ChangeSet class, it has more properties and method for Entity associations and error etc.

    Now to the different xxxxxChangeSet methods.

    AuthorizeChangeSet

    The AuthorizeChangeSet method is the first method that will be executed when the Submit method is called. This method will validate and see if the current user are allowed to make a call to the DomainService operations. The AuthorizeChangeSet method will iterate through the ChageSet and see if any of the Operations in ChangeSet or the “custom” methods has any of the the AuthorizeAttribute specified (ReuqiredAuthenticationAttribute or RequiredRoleAttrtibute). The AuthorizeAttribute is used to prevent non-authorized user to make a call to an operation method. If the current user isn’t allowed to execute any of the operations in the ChangeSet an exception will be thrown and the OnError method of the DomainService will be called, and the execution of the Submit will ended. Here is an example where the AuthroizateAttribute is used:

    [RequiresAuthentication(), RequiresRole("Admin")]
    public void AddCustomer(CustomerDto customer)


    If the current user isn’t authenticated or the authenticated user don’t belong to the role “Admin”,  the AuthorizeChangeSet will fail and an exception is thrown.


    ValidateChangeSet

    The next method that will be executed if the authorize validation passed during the Submit is the ValdiateChangeSet. This method will perform the server-side validation when the validation annotation is used on entities. If the validation fails on a entity in the ChangeSet the OnError method will be called and the Submit method will break. The following is an example about how to use the validation annotation on a Entity Framework or Linq to SQL generated class:

    [MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
    public partial class Customer
    {
        internal sealed class CustomerMetadata
        {
             private CustomerMetadata()
             {
             }
    
             [Required]
             [RegularExpression("[A-Z][A-Za-z0-9]*")]
             [StringLength(32)]
             public string CompanyName;
    
             ...
            }
        }
    }

    If you use a DTO/”Presentation Model” no meta data types are needed, instead you can just add the validation attributes to the property of your DTO/”Presentation Model”:

    public class Customer
    {
          [Required]
          [RegularExpression("[A-Z][A-Za-z0-9]*")]
          [StringLength(32)]
          public string CompanyName { get; set;}
    
             ...
    }

    Note: The validation will take place on the client-side, so the WCF RIA Services validation annotation will do both a client-side validation and server-side. It’s the ValidateChangeSet method that will do the validation on the server-side.

    ExecuteChangeSet

    The ExecuteChangeSet will take place after the validation is passed. This method will make two internal execution, it will first iterate through the ChangeSet and execute the CUD (Create, Update and Delete) Operations, and then the “custom” methods will be executed if there are any in the ChangeSet. If the ChangeSet has the following ChangeSetEntries:

    Operation = Insert, Entity = newCustomer

    Operation = Update, Entity = updateCustomer

    Operation = Delete, Entity = removeCustomer

    and the following operation methods in the DomainService:

    public void InsertCustomer(Customer customer)
            
    public void UpdateCustomer(Customer customer)
           
    public void DeleteCustomer(Customer customer)

    The first method to be executed during the ExecuteChangeSet will be the InsertCustomer then the UpdateCustomer and last the DeleteCustomer. When the ExecuteChangeSet method is completed the PersitChangeSet method will be executed.


    PersistChangeSet

    The PersistChangeSet method will make sure to save the inserted, updated and deleted entities to a data source. If the LintToEntityDomainService is used the PersistChangeSet will make a call to the ObjectContext’s SaveChanges method, if LinqToSqlDomainService is used the PersistChangeSet will call the DataContext’s SubmitChanges method. If a normal DomainService is used (when you use DTO/”Presentation Model”), the PresistChangeSet will do nothing if you don’t implement it. If you for example uses nHibernate you can call the nHibernate’s Session’s Save method in the PersistChangeSet, if the other operation methods just add entities to or remove entities from the nHibernate’s session. If you simply use the Repository pattern, Business Logic Components or Data Access components, you don’t need to use the PersistChangeSet, instead the operations method will work against the components instead (of course it’s up to you and based on the way you want to persist the model).

    The last method that will be executed by the Submit method is the ResolveChangeSet, it will only be executed if the ChangeSet’s Entities got any conflicts during the process of the Submit method.

    ResolveChangeSet

    The ResolveChangeSet will try to resolve any conflicts appeared during the execution of the Submit method. If the conflicts are solved the method will return true otherwise false. If LinqToEntityDomainService or the LinqToSqlEntityDomainService is used, the ResolveChangeSet will make a call to the ObjectContext’s SaveChanges or the DataContext’s SubmitChanges after it succeeded to resolve the entities.

    Now when you have got the basic understanding about what the Submit method in the DomainService does, it’s time to see where to begin and end a transaction.


    Using Transactions within the DomainService

    I have seen some examples where people starts an transaction within the Submit method. A transaction should be short lived and by starting a Transaction within the Submit method like this:

    public override bool Submit(ChangeSet changeSet)
    {
         using (var trans = new TransactionScope())
         {
            var result = base.Submit(changeSet);
    trans.Complete();


    return result; } }

    can make sure the transaction will be up an running during the time the Submit method will call the AuthorizationChangeSet, ValidateChangeSet, ExecuteCHangeSet and the PersistChangeSet. It can take time for the methods to be executed and there is basically no reason to have a open transaction during the execution of some of the methods. The only methods that may need the Transaction is the ExecuteChangeSet or the PersistChangeSet and maybe the ResolveChangeSet method, but still it's based on how you have implemented your own DomainSerivce. If you use the LinqToEntityDomainService or the LinqToSqlDomainServices the SaveChanges and SubmitChanges uses an implicit transaction or enlist in any ambient transaction and does all the necessary work in that transaction. The LinqToEntityDomainService and the LinqToSqlDomainServices will try to Save- or SubmitChanges after it tries to resolve some of the conflict during the ResolveChangeSet method.

    The reason why you may want to being the transaction within the Submit method is to make sure the ResolveChangeSet will be using the same transaction as the PersistChangeSet and also the other methods, it's depends on the Business Layer and Data Access Layer you are using. Another solution to avoid having an transaction open before the PersistChangeSet is called, is by starting a transaction within the PersistChangeSet and Commit or rollback the exception within the Submit method, then the ResolveChangeSet will also reuse the same transaction if you need to do that:

    private TransactionScope _trans = null;
    
    protected override bool PersistChangeSet(ChangeSet changeSet)
    {
         if (_trans == null )
    _trans = new TransactionScope();



    return base.PersistChangeSet(changeSet); } public override bool Submit(ChangeSet changeSet) { bool submitResult = false; try { var submitResult = base.Submit(changeSet); if (trans != null) trans.Complete(); } catch { if (trans != null) { trans.Dispose(); trans = null; } } return submitResult; }

    EDIT: In most case you don't need to reuse the same Transaction used within the PersistChangeSet and the ResolveChangeSet, it depends on the Business Logic layer and Data Access Layer you are using. If you use a Transaction for the PersistChangeSet it will do a rollback if there was any conflicts or Commit if there wasn't. In the case where the transaction will Commit there will be no conflicts so the ResolveChangeSet will not be called. If there was a conflict, the transaction will do a rollback and the ResolveChangeSet will then be executed. The ResolveChangeSet can then use its own Transaction when it try to resolve the conflicts and submit changes while using the LinqToSqlDomainService or LinqToEntiesDomainService.

    If you are using DTO/”Presentation Model” and your operation methods are working directly against your Business layer from the DomainService, you can start the transaction within the ExecuteChangeSet method, but it will also depends on your DAL.

    Summary

    You have now seen the life-cycle of the DomainService and what methods it will execute during a Submit. You have also seen where you can start a transaction.

    If you want to know when I post a new blog post you can follow me on twitter: http://www.twitter.com/fredrikn

    Read more...

  • Solution to minimize the loading of too much data to a DataGrid– WCF RIA Services and Silverlight

    Note: The WCF RIA Services examples in this post is based on the WCF RIA Services PDC Beta for VS 2008 and VS 2010 Beta 2 Preview.

    I have notice that a lot of developers are passing a lot of data over the wire, like they have forgot or don’t see the network between the client and the server. In this post I’m going to show how some solutions can be used to minimize the loading of data. I’m not going to mention about using techniques like GZIP compression with IIS to reduce the size, instead how to use the DataPager and also do aync. calls to load data on demand etc.

    The following example uses WCF RIA Services and the well known Northwind database. The example also uses the LinqToEntitesDomainService to just passing DAL types directly to the client over the network, but all the examples will work fine even with DTO/”Presentation Model”.

    Note: By using DTO instead or create a Entity Data Model for presentation purpose only,you can reduce the number of values passed to the client and also focus on the data that should be displayed by the current View, so no extra data is passed. Passing too much data that shouldn’t be displayed on the view can affect performance badly if the app has many users.

    Here is the code of the DomainService:

    [EnableClientAccess()]
    public class CustomerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
        public IQueryable<Customer> GetCustomerSet()
        {
            return this.ObjectContext.CustomerSet;
        }
    }

    It simply returns all the Customers located in the Northwind’s Customers table. The View has a DataGrid to display all the Customers and here is the XAML for the View:

    <UserControl
           xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
           xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"  ...>
      <Grid x:Name="LayoutRoot">
    
          <data:DataGrid x:Name="customersDataGrid"/>
          <controlsToolkit:BusyIndicator x:Name="busyIndicator"/>
    
        </Grid>
    </UserControl>


    Note: It’s a good practice to add some kind of ProgressBar while loading data, the reason is because all communications to the Server services are made asynchronous and we can’t predict how long time it will take to load the data.

    In the example above the Silverlight 3 November 09 Toolkit’s BusyIndicator is used as an activity indicator.

    This example don’t use the WCF RIA Services DomainDataSource control, instead code-behind to load the Customers, the DomainDataSource will be mentioned later in this post.

    Here is the code-behind of the View:

    public partial class MainPage : UserControl
    {
         public MainPage()
         {
             InitializeComponent();
             this.Loaded += new RoutedEventHandler(MainPage_Loaded);
          }
    
          void MainPage_Loaded(object sender, RoutedEventArgs e)
          {
    
             var customerDomainContext = new CustomerDomainContext();
    
             busyIndicator.IsBusy = true;
             customerDomainContext.Load<Customer>(
                   customerDomainContext.GetCustomerSetQuery(),
                   lo => busyIndicator.IsBusy = false
                   null);
    
             customersDataGrid.ItemsSource = customerDomainContext.Customers;
    
         }
     }

    The code above will show the BusyIndicator before the Loading of Customers takes place, and will hide the indicator when the Customers is returned from the server by passing a callback to the Load method of the customerDomainContext. The customerDataGrid’s ItemsSource property is set to the customerDomainContext’s Customers property. A very simple example of binding a DataGrid with data, but there are several problems with the above code.

    1) The DataGrid in Silverlight 3 can take some time to add all rows and update the UI. (As far as I know the DataGrid in Silverlight 3 will not asynchronous add the rows. In a future version of Silverlight the rows may be added asynchronous, haven’t yet confirmed if the DataGird in Silverlight 4 will do it).

    2) There are a lot of data passed over the wire because all Customers and the properties of a Customer entity will be passed over the network from the server to the client. If this is a public application we can’t know what kind of bandwidth the different users have etc. Passing too much data can affect performance badly.

    3) The users will probably never walk through all the Customers in the list, so what’s the reason to add them all to the DataGrid from start?

    The following part of the post will take each three issues above and show different solutions to reduce the above problems.

    How to solve the problem where the DataGrid will take some time to display all rows?

    One simple way to make sure that not all rows will be added to the DataGrid is by using paging. With Silverlight 3 there is a DataPager control which can be used for paging. The following shows how a DataPager can be added to XAML and be used with a DataGrid:

    <UserControl
         xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
         xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"  ...>
        <Grid x:Name="LayoutRoot">
    
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    
            <data:DataGrid x:Name="customersDataGrid"/>
            
            <data:DataPager
                Grid.Row="1"
                PageSize="20"
                Source="{Binding ItemsSource, ElementName=customersDataGrid}"
                IsTotalItemCountFixed="True"
                VerticalAlignment="Top"/>
    
            <controlsToolkit:BusyIndicator
                Grid.RowSpan="2"
                x:Name="busyIndicator"/>
    
        </Grid>
    </UserControl>


    The DataPager will only work if its Source property is set to a PagedCollectionView, so the Customers added to the customersDataGrid ItemsSource property needs to be converted to a PagedCollectionView, this can be done by creating an instance of the PagedCollectionView and pass the Customers as an argument to the PageCollectionView’s constructor.

    public partial class MainPage : UserControl
    {
       public MainPage()
       {
          InitializeComponent();
          this.Loaded += new RoutedEventHandler(MainPage_Loaded);
       }
    
       void MainPage_Loaded(object sender, RoutedEventArgs e)
       {
          var customerDomainContext = new CusotmerDomainContext();
    
          busyIndicator.IsBusy = true;
    
          customerDomainContext.Load<Customer>(
               customerDomainContext.GetCustomerSetQuery(),
               lo => busyIndicator.IsBusy = false,
               null);
    
         customersDataGrid.ItemsSource = new PagedCollectionView(customerDomainContext.Customers);
    } }

    The above code will only make sure the DataGrid will show data a lot faster by not adding all rows at once to the DataGrid, but it will still pass all Customers to the client. Some users may not want the paging support, instead they want to have all the Customers listed in the DataGrid. I try to make them understand what kind of problems that can lead to and try to suggest some other solutions, for example by adding a filter to make sure to only list about 500 items, and add a TextBlock to the View with a text like this: “Total number of Customers is 500 out of 25 000 ….”, then make sure they use some kind of filter to load some specific Customers, for example maybe filter on Country and City etc, everything to reduce the number of data passed from the server to the client. With WCF RIA Services there is a DomainDataSource Control which can be used with the DataPager and can asynchronous load the Customers the current page is showing. No code-behind is needed when the DomainDataSource is used. The following is an example where the DomainDataSource is used with a DataPager:

    <UserControl
        xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Ria"
        xmlns:riaData="clr-namespace:System.Windows.Data;assembly=System.Windows.Controls.Ria"
        xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
        xmlns:myData="clr-namespace:SilverlightApplication53.Web"
         ...>
        <Grid x:Name="LayoutRoot">
    
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    
            <data:DataGrid
                x:Name="customersDataGrid"
                ItemsSource="{Binding Data, ElementName=dds}"/>
    
            <data:DataPager
                Grid.Row="1"
                PageSize="20"
                Source="{Binding Data, ElementName=dds}"
                IsTotalItemCountFixed="True"
                VerticalAlignment="Top"/>
    
            <riaControls:DomainDataSource
                x:Name="dds"
                AutoLoad="True"
                PageSize="20"
                LoadSize="30"
                QueryName="GetCustomerSet">
                <riaControls:DomainDataSource.DomainContext>
                    <myData:DomainService1/>
                </riaControls:DomainDataSource.DomainContext>
                <riaControls:DomainDataSource.SortDescriptors>
                    <riaData:SortDescriptor PropertyPath="CompanyName" />
                </riaControls:DomainDataSource.SortDescriptors>
            </riaControls:DomainDataSource>
    
        </Grid>
        
    </UserControl>

    By using the DomainDataSource LoadSize property, we can specify how many Customers that should be loaded at a time. The PageSize is used to specify the size of the Page, in this case the each page should display 20 Customers.

    The solution by using the DomainDataSource control will solve the three problems mentioned earlier in this post. It will reduce the number of data passed to the client, it will also make sure the DataGrid will show the data much faster and if the users don’t need to see all Customers, they don’t need get all Customers loaded and added to the DataGrid. The only limitation is the use of a DataPager which some users may not want to use.

    Another solution to minimize the data passed to the client can be to only get 20 Customers and add them to the DataGird, and when the user moves the DataGrid Scrollbar an asynchronous call is made to get the next 20 Customers and add them dynamically to the DataGrid. By doing so the DataPager isn’t needed and the DataGrid can show the Customers a lot faster, it will also reduce the number of Customers passed from the server to the client.

    How to minimize the number of items passed over the wire?

    The DomainContext’s Load method takes a Query as an argument, the Query will  be passed to the DomainService and will be executed on the server-side. So by using this Query feature we can make sure to filter the data and only make sure the Query method will give us the items we are interested in. The following example will create a Query and pass it down to the server for execution, the query will make sure to Skip a number of Customers and only take 21 Customers:

    EntityQuery<Customer> query = _customerDomainContext.GetCustomerSetQuery()
                                                        .OrderBy(customer => customer.CompanyName)
                                                        .Skip(0)
                                                        .Take(21);
    _customerDomainContext.Load<Customer>(
                                          query,
                                          lo =>
                                          {
                                              busyIndicator.IsBusy = false;
                                          },
                                          null);


    To reduce the number of items passed over the wire and skip the use of a DataPager we can create our own Custom DataGrid where we will only show the 21 first Customers and when the user moves the DataGrid’s  Vertical Scroller we load the next 21 Customers asynchronous and add them to the DataGrid. The reason why we need to create our own custom DataGrid is because we will not have access to the Vertical Scroller from the DataGrid itself. The DataGrid will only add a Scroller if some items will not fit into the height of the DataGrid. If we only get 21 Customers and bind it to the DataGrid and they all will fit into the DataGrid no scroller will be visible, and the users will think they got all Customers. What we need to do is to add a Scroller as if all the Customers are already added to the DataGrid. To make sure to make such as Scroller we need to get the actual height of a DataGrid row and also get the total number of Customers the DataGrid should normally display, and take the height times the number of Customers to fake the Scroller. By using the WCF RIA Services service operation we can do a really fast call to get the total number of Customers, then we can create a Query to load a the 21 first Customers. You may wonder why 21 why not 20, the reason is that we will set the height of the DataGrid so only 20 items will be listed, but to get access to the Scroller and set a its range we need to make it appear. So a simple hack to get access to the Scroller is to just show 20 items but add 21 to the DataGrid. The following code is the new CustomerDomainService with a Service operation to get the total number of Customers:

    [EnableClientAccess()]
    public class CusotmerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
        public IQueryable<Customer> GetCustomerSet()
        {
            return this.ObjectContext.CustomerSet;
        }
    
        [Invoke]
        public int NumberOfCustomers()
        {
            return this.ObjectContext.CustomerSet.Count();
        }
    }

    The following code is the code-behind of the View that will  show the Customers:

    public partial class MainPage : UserControl
    {
        CustomerDomainService _customerDomainContext = new CustomerDomainService();
    
        int _currentPage = 0;
        const int NumberOfItemsToLoad = 20;
            
        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }
    
    
        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            customersDataGrid.ItemsSource = _customerDomainContext.Customers;
            LoadCustomers();
         }
    
         private void LoadCustomers()
         {
             busyIndicator.IsBusy = true;
    
             EntityQuery<Customer> query = _customerDomainContext.GetCustomerSetQuery()
                                           .OrderBy(customer => customer.CompanyName)
                                           .Skip(_currentPage * NumberOfItemsToLoad)
                                           .Take(NumberOfItemsToLoad+1);
    
             _customerDomainContext.NumberOfCustomers(
                         io =>
                         {
                            customersDataGrid.TotalNumberOfRows = io.Value;
    
                            _customerDomainContext.Load<Customer>(
                                           query,
                                           lo =>
                                           {
                                              busyIndicator.IsBusy = false;
                                           },
                                           null);
                         },
                         null);
        }
    
    
        private void customersDataGrid_LoadNewItems(object sender, 
    System.Windows.Controls.Primitives.ScrollEventArgs e)
        {
           _currentPage++;
           LoadCustomers();
         }
    }


    I will not focus so much on this code, I assume you can figure out what it does. The NumberOfCustomers Service operation will be executed before loading the  Customers to make sure the custom DataGrid’s TotalNumberOfRows property is set before the Customers is added to the DataGird’s ItemsSource. You can also see in the code that the Skip and Take is used to create a Query, the _currentPage will be increased by 1 when the custom DataGrid will trigger its LoadNewItems event handler (The LoadNewItems event will be trigged when the Scroller of the DataGrid reach a specific value, in this case when 20 rows are displayed in the DataGrid and the next 20 must be loaded and added to the DataGrid). The LoadNewItems event handler will also make a call to the LoadCustomers method to Load the next 21 Customers. The following code is the XAML of the View where the custom DataGrid is used:

    <UserControl
        xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
        xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"  
        xmlns:myData="clr-namespace:SilverlightApplicationSample"
        ...>
        <Grid x:Name="LayoutRoot">
    
            <myData:AsyncDataGrid
                x:Name="customersDataGrid"
                Height="505"
                LoadNewItems="customersDataGrid_LoadNewItems"
                />
            
            <controlsToolkit:BusyIndicator
                Grid.RowSpan="2"
                x:Name="busyIndicator"/>
    
        </Grid>
    </UserControl>

    The following code is the custom DataGrid:

    public class AsyncDataGrid : DataGrid
    {
        public event ScrollEventHandler LoadNewItems;
    
        private double _rowHeight = 0.0;
        private double _oldScrollValue = 0.0;
        private ScrollBar _verticalScrollBar = null;
    
        public AsyncDataGrid()
        {
           this.LoadingRow += AsyncDataGrid_LoadingRow;
        }
    
        public int TotalNumberOfRows { get; set; }
    
        private double GetNextLoadingPosition()
        {
           var numberOfItems = 0;
    
           foreach (var item in ItemsSource)
              numberOfItems++;
    
           return _rowHeight * numberOfItems;
        }
    
        public override void OnApplyTemplate()
        {
           base.OnApplyTemplate();
    
           _verticalScrollBar = this.GetTemplateChild("VerticalScrollbar") as ScrollBar;
    
           if (_verticalScrollBar != null)
             _verticalScrollBar.Scroll +=scrollBar_Scroll;
        }
    
        private void  AsyncDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
        {
           e.Row.SizeChanged += Row_SizeChanged;
           this.LoadingRow -= AsyncDataGrid_LoadingRow;
        }
    
        private void Row_SizeChanged(object sender, SizeChangedEventArgs e)
        {
           _rowHeight = e.NewSize.Height;
           ((DataGridRow)sender).SizeChanged -= Row_SizeChanged;
    
           if (_verticalScrollBar != null)
           {
              _verticalScrollBar.Maximum = TotalNumberOfRows * _rowHeight;
              _verticalScrollBar.UpdateLayout();
           }
        }
        private void scrollBar_Scroll(object sender, ScrollEventArgs e)
        {
          if (LoadNewItems != null && e.NewValue > _oldScrollValue)
          {
            _verticalScrollBar.Maximum = TotalNumberOfRows * _rowHeight;
    
            if (e.NewValue >= GetNextLoadingPosition())
            {
              _oldScrollValue = e.NewValue;
              LoadNewItems(this, e);
            }
          }
       }
    }

    By override the OnApplyTemplate method, we can get access to the DataGrid’s Default ControlTemplate’s VerticalScrollbar and hook up to the Scroller’s Scoll event. By using the LoadingRow event and then the SizeChanged on a DataGridRow we can get the height of the DataGridRow (I couldn’t figure out a simple way of doing it, because the LoadingRow event can’t be used to get the ActaulHeight of the row loaded, it’s to early and there is no Rows property to get the UIElement of the DataGrid’s Row, the only way to get a DataGridRow is by using the LoadingRow event). Within the SizeChanged event handler of the DataGridRow, the Maximum property of the Scroller is set to the TotalNumberOfRows times the height of the row, this will make sure the Scroller has a range that will make sure it looks like several of rows are added but they aren’t. Withing the Scoller’s Scroll event handler (scrollBar_Scroll) the LoadNewItems event will be trigged only if the Scroller reach a position when new Customers should be loaded.

    Note: I haven’t take care of the key board events, if a user uses the keyboard to walk through all the Customers added and reach the 21 Customer, no Customers will be loaded, but it’s easy to add. I left it out in this example because the hard part is to figure out how to handle the Scroller.

    This solution will solve the three problems mentioned earlier in this post. It will reduce the number of data passed to the client, it will also make sure the DataGrid will show the data much faster and if the users don’t need to see all Customers, they don’t need get all Customers loaded and added to the DataGrid.

    Summary

    This post covered  some solutions to minimize the data loaded form the server to the client, and also speed up the DataGrid. This was done by using the DataPager control, or the DomainDataSoure or to load Customers while the users moves a DataGrid’s Scrollbar.

    If you want to know when I post a new blog post or just follow my me, you can follow me on twitter: http://www.twitter.com/fredrikn

    Read more...

  • Silverlight exception handling using WCF RIA Services and WCF Services

    Note: Some examples in this post uses the WCF RIA Services PDC Beta, so changes can be done until it hit RTM.

    I have already mention about how to handle exceptions with WCF RIA Services here: http://weblogs.asp.net/fredriknormen/archive/2009/12/08/wcf-ria-services-exception-handling.aspx, but this post is about different ways to handle exception occurred on the client-side and how to log those exceptions. 

    Logging Services

    If you want to log exceptions occurred on the client-side to the server-side you need to add a Service which can take the exception message and store it. Silverlight supports different ways of passing data to the server for example, Web Service, WCF Service, Service operation via WCF RIA Services or just passing data to a ASP.NET Web Form etc. In this post I will focus on using WCF RIA Services and also WCF Service, only to give you two different options for using a service to log information.

    The following is an example of a WCF Service and a WCF RIA DomainService to act as a logger:

    Silverlight enabled WCF Service:

    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class LogService
    {
        [OperationContract]
        public void Log(string message)
        {
            //Log4Net, Exception or Instrumetionation Application Block (EntLib), EventLog or what you use ...
         }
    }



    WCF RIA Services DomainService:

    [EnableClientAccess()]
    public class LogDomainService : DomainService
    {
       [Invoke]
        public void Log(string message)
        {
             //...
        }
    }


    Note: I haven’t added code to log the message, I assume you have your own solutions for logging. I also just take one argument of type string which will contain a formatted exception message, if you prefer to use a specific class as an argument with properties to hold more information about the exception, you can do that. I will only keep this example simple for demonstrate the concept.

     

    The InvokeAttribute added to the DomainService will make the method as a Service operation, a method which we can simply call without using the WCF RIA Services DomainContext’s SubmitChanges.

    Handling Exception on the client-side

    If you want to use a generic way to handle exceptions on the client-side and call the logging service, you can use the Application_UnhandledException located in the code-behind of the App.xaml:

    public partial class App : Application
    {
        public App()
        {
            //...
            this.UnhandledException += this.Application_UnhandledException;
    
            InitializeComponent();
        }
    
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (!System.Diagnostics.Debugger.IsAttached)
            {
               //... 
               e.Handled = true;
             }
        }
    }

    If you want to log the exception directly when it occurs, you can call the Logging Service in the catch block of your try and catch block.

    Note: If you applications will throw a lot of exceptions, you will get a lot of service calls, which can affect performance, so in that case it can be advisable to save exceptions for example into a variable or Isolated Storage, so send them all in a “batch”.

    Here is an example how you can use either the DomainService or the WCF Service in the Application_UnhandledException:

    WCF Service:

    private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
        if (!System.Diagnostics.Debugger.IsAttached)
        {
            var logServiceClient = new LogServiceClient();
            logServiceClient.LogCompleted += (s, ae) =>
                                             {
                                                 if (ae.Error != null)
                                                 {
                                                     //...
                                                 }
                                              };
    
            logServiceClient.LogAsync(e.ExceptionObject.ToString());
                  
            e.Handled = true;
        }
    }

    WCF RIA Services:

    private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
        if (!System.Diagnostics.Debugger.IsAttached)
        {
             var logDomainContext = new LogDomainContext();
    
             logDomainContext.Log(e.ExceptionObject.ToString(),
                                 invokeOperation =>
                                 {
                                    if (invokeOperation.HasError)
                                    {
                                         //....
                                     }
                                  },
                                  null);
                    
                    e.Handled = true;
        }
    }

    As you can see the async. call to the Services is only made if no debugger is attached when running the application. There is no reason to log exception during debug mode.

    Because a Service will do a call over the network, the Service can also fail, for example connection problem to the server, or the service it self thrown an exception while trying to log messages etc. If the Service fails, the LogCompleted event of the WCF Service or the InvokeOperation callback of a WCF RIA Services can be used to check if there was any exception while calling the services. As you have probably notice, there is no code added for handling the exception when calling a Service in my examples, it’s because you maybe want to handle exceptions in different ways, so I left it for you to implement it. What you can do is for example add a MessageBox showing a user friendly message that the logging fails, but that is kind of strange message to show the user. Another solution could be to store the original exception to the Isolated Storage, and try to resend the messages stored in the Isolated Storage later and hope it will be sent the next time without any problems. But I should added a solution where the user can decide if the exception and information should be sent for logging and that is what the rest of the blog post is about.

    Using Child Window to show and send exceptions to the service

    When an exception occurs in the Windows OS we will see a dialog with the exception and also detailed information, we can also decide if we want to send the log to Microsoft or not. This will let the users know what kind of information that will be sent and also let the user know that the application will send information. By sending information without letting the user knows about it can be kind of bad thing to do (If the user haven’t already agree on it). So instead of just sending the message to a Service we can show a ChildWindow when an exception occurs, for example a window like this:

    image

    Here is the XAML for the above ChildWindow:

    <controls:ChildWindow
               xmlns:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"  x:Class="SilverlightApplication50.ExceptionWindow"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
               Width="400" Height="300" 
               Title="Exception occured">
        <Grid x:Name="LayoutRoot" Margin="2">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <StackPanel>
                <TextBlock FontSize="12" FontWeight="Bold" Text="An exception occured"/>
                
                <TextBlock
                            x:Name="userFriendlyMessageTextBlock"
                            Margin="0,10,0,0"
                            FontStyle="italic"
                            TextWrapping="Wrap"
                            Text="User friendly message"/>
                
                <controlsToolkit:Expander Height="160" Header="More information" Margin="0,10,0,0">
                    <controlsToolkit:Expander.Content>
                        
                        <TextBox 
                                 x:Name="detailedInforTextBox"
                                 AcceptsReturn="True"
                                 VerticalScrollBarVisibility="Auto"
                                 HorizontalScrollBarVisibility="Auto"
                                 Text="A more detailed information about the exception"/>
    
                    </controlsToolkit:Expander.Content>
                </controlsToolkit:Expander>
    
            </StackPanel>
    
            <Button x:Name="SaveLogButton" Click="SaveLogButton_Click" Content="Save Log".../>
            <Button x:Name="CancelButton" Content="Don't Send" Click="CancelButton_Click" ... />
            <Button x:Name="OKButton" Content="Send" Click="OKButton_Click" ... />
        </Grid>
    </controls:ChildWindow>


    Note: The Silverlight Controls Toolkit is used here to add the Expander control.

    The ChildWindow will have an Error property added in the Code-Behind so we can pass the Exception to the ChildControl when it’s created and also a proeprty for an user friendly message and for a detailed message. When the user press the Ok button the message will be sent to a Service. The following is an example where the WCF RIA Services LogDomainService is used (To use the WCF Service, just replace the code in the OKButton_Click to call the WCF Service and add the LogCompleted event to check for an exception):

    public partial class ExceptionWindow : ChildWindow
    {
        private Exception _exception;
    
        public ExceptionWindow()
        {
          InitializeComponent();
        }
    
        public string UserFriendlyException
        {
           get { return userFriendlyMessageTextBlock.Text; }
           set { userFriendlyMessageTextBlock.Text = value; }
        }
    
        public string DetailedException
        {
            get { return detailedInforTextBox.Text; }
            set { detailedInforTextBox.Text = value; }
         }
    
         public Exception Error
         {
            set { _exception = value; }
         }
    
         private void OKButton_Click(object sender, RoutedEventArgs e)
         {
            var errorMsg = FormatMessage();
                
            var logDomainContext = new LogDomainContext();
    
            logDomainContext.Log(errorMsg,
                                  invokeOperation =>
                                  {
                                      if (invokeOperation.HasError)
                                      {
                                          if (MessageBox.Show("Error while trying to send the log, 
    do you want to save it temporary for a later try?", "Error sending log",
    MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                                             IsolatedStorageHelper.SaveToIsolatedStorage("retry_sending_exception.log", errorMsg);
                                             
                                          invokeOperation.MarkErrorAsHandled();
                                      }
                                  },
                                  null);
    
             this.DialogResult = true;
         }
    
        
         private string FormatMessage()
         {
             string originalException = string.Empty;
    
             if (_exception != null)
                 originalException = _exception.ToString();
    
              return string.Format("{0} - {1}\n\nDetailed Message:\n{2}\n\nOriginal Exception:\n{3}",
                                              DateTime.Now.ToString(),
                                              UserFriendlyException,
                                              DetailedException,
                                              originalException);
         }
    
         private void CancelButton_Click(object sender, RoutedEventArgs e)
         {
             this.DialogResult = false;
         }
    
        //...
    }

    The OKButton_Click event handler will call the Logging Service Log method, if the call fails a MessageBox will appear and the use can decide if they want to save the original exception temporary. The IsolatedStorateHelper is a simple class I have added which will save the errorMsg to the Isolated Storage:

    public void SaveToIsolatedStorage(filenName, string errorMsg)
    {
          using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
          {
              using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(
                                                              fileName,
                                                              FileMode.Create,
                                                              isf))
              {
                  using (StreamWriter sw = new StreamWriter(isfs))
                  {
                      sw.Write(errorMsg);
                      sw.Close();
                  }
              }
         }
    }


    Note: The Isolated Storage only allow use to use the FileMode Create, so we can’t use the Append mode, so only the latest failed logging will be saved.

    The Application_Startup event handler in the App.xaml.cs can be used to try to resend the latest exception, here is an example for doing that:

    public partial class App : Application
    {
         public App()
         {
                this.Startup += this.Application_Startup;
                //...
         }
    
         private void Application_Startup(object sender, StartupEventArgs e)
         {
             TryToResendLatestException();
    
             this.RootVisual = new MainPage();
          }
    
          private void TryToResendLatestException()
          {
              var logServiceContext = new LogDomainContext();
    
              var errorMsg = IsolatedStorageHelper.LoadStringFromIsolatedStorage("retry_sending_exception.log");
                
              if (!string.IsNullOrEmpty(errorMsg))
              {
                  logServiceContext.Log(errorMsg,
                                    invokeOperation =>
                                    {
                                      if (!invokeOperation.HasError)
                                         IsolatedStorageHelper.DeleteFileFromIsolatedStorage("retry_sending_exception.log");
    
                                      invokeOperation.MarkErrorAsHandled();
                                    },
                                     null);
             }
         }
    
       //...
    }

    Here is the IsolatedStorageHelper’s Load and Delete methods:

    public string LoadStringFromIsolatedStorage(string fileName)
    {
        using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
        {
           if (isf.FileExists("retry_sending_exception.log"))
           {
               using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(
                                               fileName,
                                               FileMode.Open,
                                               isf))
               {
                    using (StreamReader sr = new StreamReader(isfs))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }
    
        return null;
    }
    
    public void DeleteFileFromIsolatedStorage(string fileName)
    {
       using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
       {
           if (isf.FileExists(fileName))
               isf.DeleteFile(fileName);
        }
    }


    The ChildWindow also has a “Save Log” button, here is the code to save the exception message to the client's local disc (I cut it out from the above ChildWindow example to not making the code too big, I could of course used some Refactoring here to make the file smaller):

    private void SaveLogOnClientDisc(string errorMessage)
    {
        var saveLogDialog = new SaveFileDialog();
    
        saveLogDialog.DefaultExt = ".log";
        saveLogDialog.Filter = "Text Files|*.txt|Log Files|*.log|All Files|*.*";
        saveLogDialog.FilterIndex = 2;
    
        bool? dialogResult = saveLogDialog.ShowDialog();   
      
        if ( dialogResult == true )   
        {   
             try
             {
                 var contents = Encoding.Unicode.GetBytes(errorMessage);
    
                 using (var fileStream = saveLogDialog.OpenFile())
                 {
                      fileStream.Write(contents, 0, contents.Length);
                      fileStream.Close();
                      MessageBox.Show("File successfully saved!");
                  } 
              }   
              catch ( Exception ex )   
              {
                  MessageBox.Show("Can't save file: " + ex.Message);
               }   
         }   
    }
    
    private void SaveLogButton_Click(object sender, RoutedEventArgs e)
    {
       SaveLogOnClientDisc(FormatMessage());
    }


    The following is the code added to the Application_UnhandledException to show the Exception dialog:

    private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
       if (!System.Diagnostics.Debugger.IsAttached)
       {
           var exceptionDialog = new ExceptionWindow();
           exceptionDialog.UserFriendlyException = e.ExceptionObject.Message;
           exceptionDialog.DetailedException = e.ExceptionObject.ToString();
           exceptionDialog.Error = e.ExceptionObject;
           exceptionDialog.Show();
    
           e.Handled = true;
       }
    }


    Note: I could have skipped adding the UserFriendlyException and DetailedException property and only use the Error property. But I decided to use them in this example so I can reuse the dialog for try and catch blocks, where I don’t want to show messages out from the exception thrown, for example:

    try
    {
        //.. Do something
    }
    catch (Exception e)
    {
       var exceptionDialog = new ExceptionWindow();
       exceptionDialog.UserFriendlyException = "Can't create user 'John Doe'";
       exceptionDialog.DetailedException = "The call to the CreateUser method failed because...;
       exceptionDialog.Error = e;
       exceptionDialog.Show();
    }


    Another kind of logging solutions

    With Silverligth 4.0 we can use COM, so we could for example open up Outlook and let the user send the error log via e-mail. We can also in Silverlight 3 do something similar by adding a client-side script and call it from the Silverlight app to open up the user’s e-mail client. In Silverlight 4 in a OOB and trusted mode, we can store log files to the users local disc.

    Summary

    In this post I wanted to show you different ways of logging client-side exceptions by calling a Service, either by using WCF or WCF RIA Services. Something to have in mind is that the call to the log service can fail, so have a backup plan to save the original exception on the client-side, for example by using the Isolated Storage to temporary store the log and try to resend it later, for example next time the Silverlight application is started.

    If you want to know when I publish new blog posts, you can follow me on twitter: http://www.twitter.com/fredrikn

    Read more...

  • WCF RIA Service – How to configure the WCF Service maximum items returned by the Query

    Note: This is post is based on the WCF RIA Services Beta for VS 2008 and the Preview bits for VS 2010 and changes can be done before WCF RIA Services hits RTM.

    There are several post on the forums about changing the default number of items passed passed from a DomainService to the client, so I decided to write a short blog post about how to configure the WCF Service of the WCF RIA Services. If you want to know how the WCF RIA Services adds default endpoints etc, you can take a look at my Deep Dive post.

    Have the following in mind when creating your DomainService:

    There is a wire between the server and the client, so be careful and not pass too much data over the wire. In a enterprise application, an app with a lot of users or low bandwidth it’s advisable to create a light weight class (Data Transfer Object) instead of instead of just passing a DAL type generated from Linq to SQL, Entity Framework or a domain entity. Only pass the data the View needs, this will reduce the number on data passed over the wire. I have helped a lot of customers lately which has major performance issue because they exposes DAL types or domain entities over the wire. There is a reason why for example WCF uses a 65kb message limit by default. It’s to make sure we open up our eyes and notice the network which bounds our client with the server.

    If you really need to pass a lot of data over the wire, you may notice that you will get an exception when you have exceeded the default number of item which can be serialized. You can change the maxItemsInObjectGraph size by configuring the WFC RIA Services behavior in web.config, by default this value is 65536 items. By changing the size of how many items which can be serialized and deserialized, you can “register” your service and add a behavior configuration for your service:

    <system.serviceModel>
    

      <!— ... -->  


    <services> <service name="SilverlightApplication.Web.MyDomainService" behaviorConfiguration="MyDomainService_BehaviorConfig"> </service> </services> <behaviors> <serviceBehaviors> <behavior name="MyDomainService_BehaviorConfig"> <dataContractSerializer maxItemsInObjectGraph="xxxxxx"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>


    The Service element name is the “Namespace.DomainServiceName”. You don’t need to change the MaxReceivedMessageSize of the binding, because by default it’s set to: 2 147 483 647 bytes.

    Read more...