1. TodoListViewModel
The TodoListViewModel
constructor accepts an IDeviceProperties
instance and an ITodoService
instance, which, is used for storage and retrieval of to-do items. See the following excerpt:
public TodoListViewModel(
ITodoService todoService, IDeviceProperties deviceProperties)
{
...
backupDatabaseCommand = new DelegateCommand(obj => BackupDatabase());
restoreDatabaseCommand = new DelegateCommand(obj => RestoreDatabase());
Load();
}
The viewmodel contains a method that leverages the IDeviceProperties
instance to create a unique ID to use to identify itself to calls to a WCF service.
string GetUserId()
{
string id= deviceProperties.WindowsLiveAnonymousId;
if (string.IsNullOrWhiteSpace(id))
{
id= "Emulator";
}
return id;
}
The anonymous ID is passed to the WCF service
when backing up the local database file and used as part of the URL
when restoring it.
2. Backing Up the Local Database
To transfer the local database to the server,
it must first be copied to a directory in isolated storage. This
prevents the file from being modified by the SQL CE engine while the
transfer is under way. The viewmodel’s BackupDatabase
method creates a temporary directory and then copies the local .sdf database file to the directory, as shown:
string uploadUrl = "http://localhost:60182/BackupService/UploadFile/";
const string localDatabaseName = "Todo.sdf";
const string transferDirectory = "/shared/transfers";
string uploadPath = transferDirectory + "/" + localDatabaseName;
using (IsolatedStorageFile isolatedStorageFile
= IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isolatedStorageFile.FileExists(localDatabaseName))
{
throw new InvalidOperationException(
"Database file does not exist in isolated storage.");
}
if (!isolatedStorageFile.DirectoryExists(transferDirectory))
{
isolatedStorageFile.CreateDirectory(transferDirectory);
}
isolatedStorageFile.CopyFile(localDatabaseName, uploadPath, true);
}
The BackupDatabase
method then constructs a destination URL for the upload. The remote URL
is constructed using the base URL of the upload file path on the
server. This URL is rerouted when it arrives at the server, and its
segments are passed as arguments to the SaveFile
WCF service method. See the following excerpt:
string deviceId = GetUserId();
string remoteUrl = string.Format("{0}{1}/{2}",
uploadUrl,
deviceId,
localDatabaseName);
Uri remoteUri = new Uri(remoteUrl, UriKind.Absolute);
A BackgroundTransferRequest
is constructed, which causes the file to be uploaded to the server. Uploads use the HTTP POST method, as shown:
BackgroundTransferRequest request
= new BackgroundTransferRequest(remoteUri)
{
TransferPreferences = TransferPreferences.AllowBattery,
Method = "POST",
UploadLocation = new Uri(uploadPath, UriKind.Relative)
};
To monitor the progress of the transfer request, while the app is running in the foreground, we subscribe to the TransferStatusChanged
and the TransferProgressChanged
events. The transfer request is then added to the BackgroundTransferService
, which queues the upload. See the following:
request.TransferStatusChanged += HandleUploadTransferStatusChanged;
request.TransferProgressChanged += HandleUploadTransferProgressChanged;
BackgroundTransferService.Add(request);
Message = "Backing up data to cloud.";
ProgressVisible = true;
When the viewmodel’s ProgressVisible
property is set to true, it causes a progress indicator to be displayed in the view.The Progress
property is updated whenever the TransferProgressChanged
event is raised, as shown:
void HandleTransferProgressChanged(
object sender, BackgroundTransferEventArgs e)
{
if (e.Request.BytesSent > 0)
{
Progress = (double)e.Request.TotalBytesToSend / e.Request.BytesSent;
}
else
{
Progress = 0;
}
}
When the request’s TransferStatusChanged
event is raised, if the request has completed, it is removed from the BackgroundTransferStatus
, and the progress indicator is hidden.
Note
A TransferStatus
of Completed does not necessarily mean that the transfer completed
successfully, but rather that the operation has ended for whatever
reason. It is therefore critical to test for the presence of an error
contained in the Request.TransferError
property.
The ViewModelBase
class’s MessageService
is used to display the result to the user, as shown:
void HandleUploadTransferStatusChanged(
object sender, BackgroundTransferEventArgs e)
{
if (e.Request.TransferStatus == TransferStatus.Completed)
{
BackgroundTransferService.Remove(e.Request);
ProgressVisible = false;
if (e.Request.TransferError != null)
{
MessageService.ShowError("An error occured during backup.");
}
else
{
MessageService.ShowMessage("Backup successful.");
}
}
}
After the local database file has been transferred to the server, the user can nominate to restore the database from the backup.