Feeds:
Posts
Comments

Posts Tagged ‘Isolated Storage’

In the last two parts of this session I have shown how to develop Silverlight application that shows information from a database in occasion connected environment. In this last part I will show you how to manage updates (including inserting and deleting records) and synchronizing them back into the database.

The first step is to make sure we are working with the relevant DomainContext object and to apply all the updates to the offline/online DomainContext, depending on the application’s status. To accomplish this I have created a factory method that checks the application’s status and returns the relevant DomainContext. This method should be used whenever we need a DomainContext object in Silverlight projects.

public static NorthwindDomainContext GenerateNewNorthwindDomainContext()
{
    NorthwindDomainContext northwindDomainContext;
    if (OfflineHelper.IsApplicationOnline)
    {
        northwindDomainContext = new NorthwindDomainContext();
    }
    else
    {
        northwindDomainContext = 
          (NorthwindDomainContext)DomainContextExtension.
                    GetOfflineDomainContext(typeof(NorthwindDomainContext));
    }

    return northwindDomainContext;
}

The second step is submitting the changes. To accomplish that, we will need to check the application status; if it is online we should use the out of the box SubmitChanges method and if it is offline, we just call the OCSaveAsOfflineDomainContext that we implemented in the previous parts. Since the Entity object is tracing all the changes, we do not need to do anything except serialize the updated DomainContext to the offline storage. To simplify the operation I created this extension method:


public static Task OCSubmitChangesAsync(this DomainContext source)
{
    if (OfflineHelper.IsApplicationOnline)
    {
        return source.SubmitChangesAsync();
    }
    else
    {
        return TaskEx.Run(() =>
        {
            source.OCSaveAsOfflineDomainContext();
        });
    }
}

The third and last step is synchronizing with the database when the application becomes online.  All we need is to call the SubmitChanges on the offline DomainContext. Although the MSDN sample that enables serializing RIA DomainContext objects fit the job, it still required fixing some bugs to make it work properly (I attached the final code at the button of the post). Again, I warp it up with an extension method:

public static Task OCSyncWithOnlineServiceAsync(this DomainContext source)
{
    Task result = null;
    if (OfflineHelper.IsApplicationOnline)
    {
        DomainContext offlineDomainContext = 
             GetOfflineDomainContext(source.GetType());
        try
        {
            result = offlineDomainContext.SubmitChangesAsync();
        }
        catch (Exception ex)
        {
            HandleSyncErrors(ex);
        }
    }

    return result;
}

An example of using the above methods for doing offline updating, would look something like this:

NorthwindDomainContext domainContext = 
          DomainContextFactory.GenerateNewNorthwindDomainContext();

await this.domainContext.LoadAsync(this.domainContext.GetCustomersQuery());
this.domainContext.OCSaveAsOfflineDomainContext();

OfflineHelper.IsApplicationOnline = false;

domainContext = DomainContextFactory.GenerateNewNorthwindDomainContext();
// Do some update on domainContext objects...

OfflineHelper.IsApplicationOnline = true;
await this.domainContext.OCSyncWithOnlineServiceAsync();

In addition, I have also attached a simple application that demonstrates the use of this framework. Notice that you can take the application offline, make changes, close and open the application and the changes will still be there. The application is also able to run out of the browser so you will be able to run this application even after the web server and the SQL server are unavailable.

Summary: To convert your application to support offline scenarios you will need to follow these steps:

  1. Download and install the Microsoft Visual Studio Asynchronous Framework.
  2. Add reference to the SilverlightOccasionallyConnected DLL.(attached to this post)
  3.  Create a factory method as demonstrated, to create DomainContext and use only this method throughout the Silverlight projects.
  4. Use the OCLoadAsync and OCSubmitChangesAsync to query and submit your changes respectively.
  5. Make sure that the filter and order-by logic are located in the Silverlight projects and not on the Server-side project.
  6. Add to your application this functionalities :
    1. An offline/online indication
    2. An option to generate offline copy of the database using the OCSaveAsOfflineDomainContext function. Load all the data that your application will need access to in offline mode before calling this method.
    3. An option to synchronize using the OCSyncWithOnlineServiceAsync function.

Source Code for this article

Read Full Post »

In the previous part I showed how to easily write a unified code that automatically checks the application connectivity status and executes a query against the database, or an offline storage, whether the application is online/offline respectively. In this part I will describe in more detail the implementation of the DomainContext extension methods that enables this.
The DomainContextExtension class includes the method OCLoadAsync that receives as a parameter an RIA services query that can be customized on the fly and returns a Task of type IEnumerable of the requested entity type.

The method contains 3 main steps:

  1. If the application is online, the method just loads the data from the RIA service.
  2. Else, will create an IQueryable of the requested entity from the offline DomainContext.
  3. Transfers the query from the current DomainContext to the offline DomainContext.

I will explain in details each step:

  1. The class SilverlightOccasionallyConnected.OfflineHelper has a static Boolean property IsApplicationOnline. The property is initiated with the built-in NetworkInterface.GetIsNetworkAvailable() value. The idea is to set the status at the application loading and to enable control of this property to the application. If the application is online, the method use the Async framework to load the query from the server as I described in detail in this article.
  2. If the application is offline we need to execute the query on the offlineDomainContext that is stored on the application Isolated Storage. I used the example from the MSDN- Code Gallery to load the DomainContext from the Isolated Storage. Next we need to create IQueryable for the requested Entity. If the entity framework’s model used in the domain service does not have any inherit relation we can just write:
    OfflineDomainContext.EntityContainer.
                           GetEntitySet(requestEntityType)
    

    If however we have inherit relation we need to do an extra step and find the base type of the Entity (the parent entity that is inherit directly from the System.ServiceModel.DomainServices.Client.Entity object) and then use the following syntax:

    OfflineDomainContext.EntityContainer.
                           GetEntitySet(baseEntityType).OfType<T>()
    

    Where T is the requestEntityType. We also need to convert the IEnumerable to IQueryable so the next step of changing the query source will be available.

  3. This step is the trickiest. We need to transfer the query from the current DomainContext to the offline DomainContext. The queries on the DomainContext are of type EntityQuery<T>. This type has a Query property of type IQueryable that contains the specific requested query (for example “where-clauses” that narrow the result).
    The IQueryable contains an Expression property that exposes the expression tree that builds the requested query. By debugging I realized that the expression tree contains nesting of MethodCallExpression where the first argument can be the next MethodCallExpression or the ConstantExpression that represents the original IQueryable object (the object we’d like to replace) and the second argument is a UnaryExpression that represents the lambda expression. In light of the above tree structure, I wrote recursive method that goes through the expression tree and creates new expressions with a different source for the query (the offline DomainContext IQueryable that we created in step 2).
private static Expression ChangeQueryableExpressionSource<T>(
                                             Expression expression,
                                             IQueryable<T> newSource)
{
    MethodCallExpression methodCallExpression =
                                        (MethodCallExpression)expression;
    Expression expressionLeftArgument =
                                        methodCallExpression.Arguments[0];
    Expression expressionRightArgument =
                                        methodCallExpression.Arguments[1];

    if (expressionLeftArgument is ConstantExpression)
    {
        return Expression.Call(
                               methodCallExpression.Method,
                               Expression.Constant(newSource),
                               expressionRightArgument);
    }
    else
    {
       return Expression.Call(
                              methodCallExpression.Method,
                              ChangeQueryableExpressionSource(
                                      expressionLeftArgument, newSource),
                              expressionRightArgument);
    }
}

To the above method I just added some basic functionality that loads and saves DomainContext to a fixed-name file on the Isolated Storage.

In the next part I will add submit update functionality that will enable making updates in occasionally connected application.

Feel free to use this code in your applications and let me know what you think about it.

Source Code for this article

Read Full Post »

I got a request to build an internet application that will also be available offline; The user “checks out” part of the database to the client, works offline and checks it in when going back online.

To implement this request I decided to use Silverlight built-in out-of-browser application, RIA services for accessing the remote database and the MSDN sample that enables serializing RIA DomainContext objects to an isolated storage, so I can save database tables locally(part of the “Authentication quick-start” example).

The challenge with the above is that every query against the database requires checking if the application is online or offline and writing a duplicate Data access code for each scenario. The solution I used was to write a helper code that will enable writing one data access code regardless of the application status (online/offline).

For example you can write in your view model the following code:

private AdventureWorksLTDomainContext domainContext =
                              new AdventureWorksLTDomainContext();
this.Customers = await this.domainContext.OCLoadAsync(
                           this.domainContext.GetCustomersQuery());

Things to notice:

  1. The method OCLoadAsync will check the application connectivity status. If it is online it will execute the RIA service against the remote server and if it is offline it will load the information from the isolated storage.
  2. I am using the Async framework (still in CTP) to load the query. For more detail on using Async framework with RIA service please refer to this article.
  3. The query can be customized on the fly, for example the following line is also valid:
    this.Customers = await this.domainContext.OCLoadAsync(
      from customer in this.domainContext.GetCustomersQuery()
      where customer.FirstName.StartsWith(this.Prefix)
      orderby customer.FirstName, customer.LastName
      select customer );
    
  4. Given the way RIA is implemented, only the final complete query is executed against the Database.
  5. The implementation of the offline support will not include any code rules in the domain service methods. The assumption is that your domain service methods will basically return all entities for a specific type. You can add code rules to the domain service, for example security trimming;  The offline version will not include these code rules, however the local storage will only contain the records that the user had access to last time they were online..

Before you will be able to access offline storage you will need to create one. This is also an easy task with the OCSaveAsOfflineContext method. Just execute the following code when the application is online:

await this.domainContext.LoadAsync(
                    this.domainContext.GetCustomersQuery());
this.domainContext.OCSaveAsOfflineDomainContext();

Things to notice:

  1. The method LoadAsync returns the result of the query but also populates the DomainContext with the result, so by saving the domaincontext to the Isolate Storage we can access the information later when the application is offline.
  2. You will need to load all the queries you like to save with the same domainContext and then use the OCSaveAsOfflineDomainContext method to save local version of your database in the Isolated Storage.

To use this solution follow these steps:

  1. Create normal Silverlight project with Entity framework model and RIA services (if you’re not familiar with RIA you can check this link).
  2. Install the Async Framework and add reference to the AsyncCtpLibrary_Silverlight DLL.
  3. Add reference to the SilverlightOccasionallyConnected Dll  download available at the bottom of this article.
  4. Add using-statement to ‘SilverlightOccasionallyConnected” to have access to the extension methods for the DomainContext class.
  5. Start writing your class.

In Part II,  I Will explain in more detail the implementation of the DomainContext extension methods.

Source Code for this article

Read Full Post »

The MSDN – WCF RIA Services Code Gallery features a nice code sample called “Authentication Quick Starts” that shows how to implement Offline authentication. The sample includes a utility that enables serializing RIA DomainContext into Isolated Storage – a feature that became handy when implementing occasionally connected applications.
One small problem I found with the sample is that it is does not support DomainContext with inherent relations. When I tried to load DomainContext with inherit relations from the Isolated Storage using the function “LoadFromIsoStore”, I got the following exception:

System.InvalidOperationException: Cannot retrieve an entity set for the derived entity type ‘[derivedEntityTypeName]’. Use EntityContainer.GetEntitySet(Type) to get the entity set for the base entity type ‘[baseEntityName]’.

A quick fix to the function OfflineStorage.GetEntitySetForOfflinableEntity, which solved the problem, was to add the following lines:

while (entityType.BaseType.Name != "Entity")
{
    entityType = entityType.BaseType;
}

So the complete function looks like this:

private static EntitySet GetEntitySetForOfflinableEntity(DomainContext domainContext, OfflinableEntity offEntity)
{
    Type entityType = offEntity.CurrentEntity.GetType();

    while (entityType.BaseType.Name != "Entity")
    {
        entityType = entityType.BaseType;
    }

    //Get entitylist for entityType
    MethodInfo methodInfo = domainContext.EntityContainer.GetType().GetMethod("GetEntitySet", Type.EmptyTypes);
    Type[] genericArguments = new Type[] { entityType };
    MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
    EntitySet entitySet = (EntitySet)genericMethodInfo.Invoke(domainContext.EntityContainer, null);
    return entitySet;
}

The updated OfflineStorage.cs file can be found here.

Read Full Post »