Amadiere.com

Fourteen and a half crazy frog burpers

10th September 2010

Isolated Storage on Windows Phone 7

Filed under: C#,Databases,Windows Phone 7 — Tags: , , , — Alex Holt @ 9:30 pm

One of the most amazing omissions from the Windows Phone 7 OS is an SQL Server of some description that applications can use (I think there is a SQL Server Compact edition baked into the build, but it’s only exposed to the native apps). This leaves anyone familiar with doing all their storage via relational databases in a bit of a lurch.

Having a look around, there were a few work-arounds, namely third party libraries such as Perst & Sterling. These act as a layer of abstraction that allow you to use LINQ to access your objects and could be of benefit for some of the more complex applications. I found both of them to have their drawbacks.

Perst: From McObject is one alternative, but if you plan on doing a commercial application, you should bare in mind their licensing, of which, their FAQ says:

No, Perst is not free. McObject Perst is available under a dual license. Under the GPL, you may evaluate the source code free of charge and you may use Perst free of charge in an application for which the source code is also freely available. If you wish to use a Perst in an application but do not or cannot redistribute your application source code, you can use Perst under a commercial license.

Sterling: This seemed promising, but I had a number of issues getting the code to work (it was late, in fairness) I decided to give up.

In reality though, most applications that are going to be made don’t need any complicated database structure behind them. Database implementations are most likely going to be overkill. This is where I decided to do what probably the majority of WP7 developers have decided to do, and “Roll-Their-Own” database solution. Mine is based on a general accepted practice, using XML Serialisation to store my POCOs. While it might have limitations and performance issues later down the line, for now – it’s performing very admirably and there seemed no point in premature optimisation just yet.

My Solution

The main class I created (MyDataContext.cs) contains the functions for both saving and loading the data, as well as the ‘schema’ for the entire database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Collections.Generic;
using IsolatedExampleApp.Data.Models;
using System.IO.IsolatedStorage;
using System.IO;
using System.Xml.Serialization;
 
namespace IsolatedExampleApp.Data
{
  public class IsolatedDatabase
  {
    public IsolatedDatabase()
    {
      // For each model that is a list item, you need to add a initialising statement to the Constructor
      Albums = new List();
      Artists = new List();
    }
 
    // These properties effectively form the publicly viewable schema of the database.
    public List Albums { get; set; }
    public List Artists { get; set; }
    public Options Options { get; set; }
 
    public void Load()
    {
      using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
      using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("IsolatedExampleApp.txt", FileMode.OpenOrCreate, FileAccess.Read, store))
      using (StreamReader reader = new StreamReader(stream))
      {
        XmlSerializer serializer = new XmlSerializer(typeof(IsolatedDatabase));
        var unserialized = reader.EndOfStream ? new IsolatedDatabase() : (IsolatedDatabase)serializer.Deserialize(reader);
 
        // Each schema is repopulated with information when the app is loaded.
        Albums = unserialized.Albums;
        Artists = unserialized.Artists;
        Options = unserialized.Options;
      }
    }
 
    public bool Save()
    {
      try
      {
        using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("IsolatedExampleApp.txt", FileMode.Create, FileAccess.Write, store))
        {
          XmlSerializer serializer = new XmlSerializer(typeof(IsolatedDatabase));
          serializer.Serialize(stream, this);
        }
        return true;
      }
      catch (Exception ex)
      {
        return false;
      }
    }
  }
}

As you can see from the code above, as you develop more data stores, the code needs updating in multiple locations.

  • Properties need setting for each one (they are the publicly available access points for the data).
  • Any LISTS<> require their lists initialised by the Constructor method.
  • When the data is ‘Load()’ed, each property must have it’s values manually set.
  • All the Models still need defining elsewhere in your application (in my example above, they are in the IsolatedExampleApp.Data.Models namespace.

If you can overlook this repeated typing, then this seems to be a good starting point for persisting data in your Windows Phone 7 Application. To use it should then be fairly straight forward.

_db = new IsolatedDatabase();
_db.Load();
 
// Adding an album...
_db.Albums.Add(newAlbum);
_db.Save();
 
// Deleting an album...
_db.Albums.Remove(deleteAlbum);
_db.Save();
 
// Add or Update an album...
Album existingAlbum = _db.Albums.Where(x => x.ID == album.ID).FirstOrDefault();
if (existingAlbum != null)
{
  Delete(existingAlbum);
}
Add(album);
_db.Save();
 
// Or just getting a specific album...
IEnumerable albums = _db.Albums as IEnumerable;
return albums.Where(x => x.ID == id).FirstOrDefault();

Any feedback, thoughts, options and optimisations for the above code will be well received. Happy isolating!

20th April 2009

MySQL Two-Way, Master-Master Replication

Filed under: MySQL — Tags: , , — Alex Holt @ 1:32 pm

A bit back, I was looking into database replication and specifically, two-way replication. Meaning that either server could be updated and it would replicate any changes to it’s nice neighbour. Microsoft SQL offer a solution – but it was way out of our price-range. So, we looked into the world of MySQL and at the time, the sparkly and new MySQL 5.0.

MySQL offer quite a bit of useful documentation on replication in general, but for our requirement of Master to Master connections, it offered the wonderful barley’s of:

MySQL replication currently does not support any locking protocol between master and slave to guarantee the atomicity of a distributed (cross-server) update. In other words, it is possible for client A to make an update to co-master 1, and in the meantime, before it propagates to co-master 2, client B could make an update to co-master 2 that makes the update of client A work differently than it did on co-master 1. Thus, when the update of client A makes it to co-master 2, it produces tables that are different from what you have on co-master 1, even after all the updates from co-master 2 have also propagated. This means that you should not chain two servers together in a two-way replication relationship unless you are sure that your updates can safely happen in any order, or unless you take care of miss-ordered updates somehow in the client code.

You should also realize that two-way replication actually does not improve performance very much (if at all) as far as updates are concerned. Each server must do the same number of updates, just as you would have a single server do. The only difference is that there is a little less lock contention, because the updates originating on another server are serialized in one slave thread. Even this benefit might be offset by network delays.

This offers some good advice and you really do need to understand exactly what you are doing before you can continue down this path. But suppose that it doesn’t really matter what order things happen, as long as they happen consistently across all servers, what are we to do then to get it all working? Well, enhancing and copying from my response to a question on the awesome Stack Overflow, here is what I’d do.

The first major problem that you must overcome, is that when a new incremental seed is created, it’s normally created 1, 2, 3, 4, 5 (the glorious one times table). However, this will be no good if the theoretical two-inserts-at-once-on-different-boxes happens. It would great two number 4′s. A BIG data-integrity issue. This is solved simply however, by simply providing a seed which equals the total number of masters you would have. So, in my case (and maybe most), there would be two Masters, one would be counting evens, and the other ones odd. 2,4,6,8 and 1,3,5,7. Following this logic there can be no duplicates. It does create the OCD-Offensive side-effect that there are numbers that are never used – but that doesn’t really matter a great deal as far as things go. In addition, you should ensure both your server_id’s are different (two MySQL servers with the same name in replication has distinct disadvantage to the other setups (it doesn’t work) – but that’s standard replication setup anyway. ;0

Master MySQL 1:

auto_increment_increment = 2
auto_increment_offset = 1

Master MySQL 2:

auto_increment_increment = 2
auto_increment_offset = 2

Using all the functionality and commands of the standard MySQL replication, you should then be able to start both servers up as slaves of the other one. Then to check both are working OK, connect to both machines and perform the command SHOW SLAVE STATUS and you should note that bothSlave_IO_Running and Slave_SQL_Running should both say “YES” on each box.

When creating your code to connect to the database servers, you want to send to both boxes equally? Or maybe favour one because it’s a bit beefier? You could add in death-checks to make sure a server is still serving queries and allowing connections – and if its not, use the other one.

All being told, I really like this solution for replication. For the fuller picture, you’d obviously add a slave somewhere that is remotely connected periodically (or all the time) and allowed to update. It acts as an off-site backup should your building be compromised (burns to the ground in a huge inferno-type-mess scenario. For example ;) ).

Theme designed & built for Amadiere.com by Alex Holt. Powered by WordPress