KISS Architecture (part 9)

by Chris 23. January 2009 16:11

In this part, I will continue the implementation of the presentation tier for Windows Mobile, and if you want some background, please see the previous parts:

  • Part 1 was a general introduction
  • Part 2 outlined the architecture (tiers, etc)
  • Part 3 showed the benefit of loosely coupled tiers (distribution, cloud, etc)
  • Part 4 started the implementation by creating the entity data model (using ADO.NET Entity Framework)
  • Part 5 published the entity data model as a data service (using ADO.NET Data Services)
  • Part 6 implemented the business domain (using the data service)
  • Part 7 created the service (using WCF)
  • Part 8 started the implementation of the mobile client (using WCF)
  • The implemented architecture is published on CodePlex in a project called KISS Architecture, and this means that you can access the full source code as well as discuss it, come with suggested improvements, etc. As I walk you through the creation of the architecture, I suggest you keep the source code handy to check out more details.

    In the previous part (8) of this blog series, I showed how the Windows Mobile client can call a WCF service when connected to the network. However, there are a number of times when the mobile device is not connected and still need to allow the application to function. It would be really awesome if the new technologies like ADO.NET Data Services, ADO.NET Entity Framework, LINQ to Entities, etc, were available also for Windows Mobile and .NET Compact Framework, as this would mean that we could use identical code (assemblies/tiers) on the client, but unfortunately they are not (yet).

    So, what is the second best solution? I still want to make use of the entities defined by the (data) service tier, and not maintain any custom entity code. Also, I want the implementation to be as simple as possible (remember the KISS principle) and make it as easy as possible to upgrade when these new technologies are available. That means that I don't want to spend time writing my own custom framework, but rather something simple that just works.

    To allow local data storage, I create a SQL Server Compact database by adding a "Database File" named Northwind.sdf to the Kiss.Mobile project. To that database, I then add the same tables that our order (data) service defines (see part 4 of this blog series). I also add a plain class named Common.cs to the same project that is a singleton holding a single connection to the database.

    With the local database in place and a connection to it, the code for the "Get" menu item can be changed into:

    private void getMenuItem_Click(object sender, EventArgs e)
    {
       
    Customer[] customers;
       
    if(online)
        {
           
    OrderServiceClient.EndpointAddress = new EndpointAddress("http://192.168.0.100:2222/OrderService.svc");
           
    OrderServiceClient service = new OrderServiceClient();
            customers = service.GetCustomersByCity(cityTextBox.Text);
        }
       
    else
        {
           
    OrderHandler data = new OrderHandler();
            customers = data.GetCustomersByCity(cityTextBox.Text);
        }
        dataGrid.DataSource = customers;
    }

    The variable indicating connection state (online) can be either manual or automatic through SystemState.ConnectionsCount. Before we take a look at the OrderHandler class, here's the updated code for the "Update" menu item:

    private void updateMenuItem_Click(object sender, EventArgs e)
    {
       
    Customer customer = ((Customer[])dataGrid.DataSource)[0];
        customer.City = customer.City + "X";

       
    if(online)
        {
           
    OrderServiceClient service = new OrderServiceClient();
            service.UpdateCustomer(customer);
        }
       
    else
        {
           
    OrderHandler data = new OrderHandler();
            data.UpdateCustomer(customer);
        }
    }

    Ok, so with that in place, here's the implementation of the OrderHandler class:

    public class OrderHandler
    {
       
    public Customer[] GetCustomersByCity(string city)
        {
           
    //var q = from c in data.CustomerSet
            //        where c.City.Contains(city)
            //        select c;
            return HandlerHelper.GetObjects<Customer>(string.Format(
               
    "SELECT * FROM Customers WHERE City LIKE '%{0}%'", city));
        }

       
    public void UpdateCustomer(Customer customer)
        {
           
    HandlerHelper.UpdateObject<Customer>(customer, string.Format(
               
    "SELECT * FROM Customers WHERE CustomerID='{0}'", customer.CustomerID));
        }
    }

    Not much happening here except that the handler make use of a helper that I wrote to take care of the plumbing. I can use the entity type and I need to define the query as plain SQL, but note that I've kept the desired LINQ as a comment in preparation for the day when it can be used. Let's look at the implementation of the helper class:

    public static class HandlerHelper
    {
       
    public static T[] GetObjects<T>(string sql) where T : new()
        {
           
    List<T> objects = new List<T>();
           
    SqlCeCommand command = Common.Values.DatabaseConnection.CreateCommand();
            command.CommandText = sql;
           
    SqlCeDataReader reader = command.ExecuteReader();
           
    Type type = typeof(T);
           
    while(reader.Read())
            {
                T entity =
    new T();
               
    for(int i = 0; i < reader.FieldCount; i++)
                {
                    PropertyInfo propertyInfo = type.GetProperty(reader.GetName(i));
                    propertyInfo.SetValue(entity, reader[i],
    null);
                }
                objects.Add(entity);
            }
           
    return objects.ToArray();
        }

       
    public static void UpdateObject<T>(T entity, string sql)
        {
           
    SqlCeCommand command = Common.Values.DatabaseConnection.CreateCommand();
            command.CommandText = sql;
           
    SqlCeResultSet resultSet = command.ExecuteResultSet(ResultSetOptions.Updatable);
            resultSet.Read();
           
    Type type = typeof(T);
           
    for(int i = 0; i < resultSet.FieldCount; i++)
            {
               
    PropertyInfo propertyInfo = type.GetProperty(resultSet.GetName(i));
               
    object value = propertyInfo.GetValue(entity, null);
                if(!value.Equals(resultSet.GetValue(i)))
                    resultSet.SetValue(i, value);
            }
            resultSet.Update();
        }
    }

    The database connection kept by the singleton (Common.Values) is used to either query or update the local database. Both methods are implemented using generics, and reflection is used to find all the attributes (fields). To optimize performance, a data reader is used for the query and a resultset is used for the update. Both methods are somewhat simplified as some extra null handling is necessary but does not add to the discussion. Using reflection is not an ideal solution, but remember that my goal is to keep the implementation as simple as possible and also that this is not a solution for the future, it's a temporary fix in the lack of the right technologies on the device. When that day comes, the helper class can be scrapped, and the handler class either updated or even replaced by something like LINQ to Entities.

    In the next part, I will conclude this series.

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Tags: , , ,

    Architecture | Chris | Compact Framework | Windows Mobile

    KISS Architecture (part 8)

    by Chris 16. January 2009 16:09

    In this part, I will start the implementation of the presentation tier for Windows Mobile, and if you want some background, please see the previous parts:

  • Part 1 was a general introduction
  • Part 2 outlined the architecture (tiers, etc)
  • Part 3 showed the benefit of loosely coupled tiers (distribution, cloud, etc)
  • Part 4 started the implementation by creating the entity data model (using ADO.NET Entity Framework)
  • Part 5 published the entity data model as a data service (using ADO.NET Data Services)
  • Part 6 implemented the business domain (using the data service)
  • Part 7 created the service (using WCF)
  • The implemented architecture is published on CodePlex in a project called KISS Architecture, and this means that you can access the full source code as well as discuss it, come with suggested improvements, etc. As I walk you through the creation of the architecture, I suggest you keep the source code handy to check out more details.

    kisswmform

    I start the implementation of a Windows Mobile client by creating a new project of type "Smart Device Project", name it Kiss.Mobile, select "Windows Mobile 5.0 Smartphone SDK", ".NET Compact Framework Version 3.5", and "Device Application". This will allow it to run on many of the devices on the market, and still use the latest framework with new functionality. That new functionality include WCF, so let's move on by creating a reference to the WCF service I created in the previous part (7). You do that by using the tool NetCFSvcUtil that is included in the Power Toys for .NET Compact Framework 3.5. For more details on using this tool, please see A New Mobile N-tier Architecture (part 8).

    Let's implement a simple user interface by renaming the automatically generated form (Form1) to MainForm, and add three controls (a Label named cityLabel, a TextBox named cityTextBox, and a DataGrid named simply dataGrid) to it. Also, add a MainMenu named mainMenu and add menu options to it named doneMenuItem, getMenuItem, and updateMenuItem. The result should look similar to the figure on the left.

    With the service reference and the user interface in place, it's time to implement some presentation logic. Here's the code for the "Get" menu item:

    private void getMenuItem_Click(object sender, EventArgs e)
    {
        OrderServiceClient.EndpointAddress = new EndpointAddress("http://192.168.0.100:2222/OrderService.svc");
       
    OrderServiceClient service = new OrderServiceClient();
        Customer[] customers = service.GetCustomersByCity(cityTextBox.Text);
        dataGrid.DataSource = customers;
    }

    Note how easy it would be to redirect the client to use the service in another location (other server, cloud, etc) by just changing the service URI. Also note how the definition of the entity (Customer) have come all the way from the data service without any manual coding anywhere, and when it is changed, it's simply a matter of updating the service references in all tiers. The observant will note that the List<Customer> return value of the service is transformed into a plain array when serialized by WCF, but if a list is preferred, this code could be used:

    List<Customer> customers = service.GetCustomersByCity(cityTextBox.Text).ToList();

    The code for the "Update" menu item looks like this:

    private void updateMenuItem_Click(object sender, EventArgs e)
    {
       
    Customer customer = ((Customer[])dataGrid.DataSource)[0];
        customer.City = customer.City + "X";
        OrderServiceClient service = new OrderServiceClient();
        service.UpdateCustomer(customer);
    }

    It first takes the first row in the grid, and adds an "X" to the City attribute. Then the service is called with the updated entity instance (customer). It can't be much simpler than this!

    In the next part, I will continue the implementation of the Windows Mobile client by adding offline support.

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Tags: , ,

    Architecture | Chris | Compact Framework | Windows Mobile

    Powered by BlogEngine.NET 1.4.5.0
    Theme by Mads Kristensen