Skip to content

Instantly share code, notes, and snippets.

@dsplaisted
Created July 8, 2012 05:36
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dsplaisted/3069556 to your computer and use it in GitHub Desktop.
Save dsplaisted/3069556 to your computer and use it in GitHub Desktop.
Portable task wrappers
// This shows how you can create a portable task wrapper so that code in a portable library
// can run against async APIs for Metro style apps as well as synchronous APIs for platforms
// such as WP7.
using System;
namespace PortableTasks
{
// Basic interfaces which abstract an operation which may or may not be async
public interface IPortableTask
{
void ContinueWith(Action continuation, Action<Exception> error);
}
public interface IPortableTask<T>
{
void ContinueWith(Action<T> continuation, Action<Exception> error);
}
// Implementations of the IPortableTask interfaces for operations which complete synchronously
public class SynchronousPortableTask : IPortableTask
{
public void ContinueWith(Action continuation, Action<Exception> error)
{
continuation();
}
}
public class SynchronousPortableTaskFailure : IPortableTask
{
Exception _exception;
public SynchronousPortableTaskFailure(Exception exception)
{
_exception = exception;
}
public void ContinueWith(Action continuation, Action<Exception> error)
{
error(_exception);
}
}
public class SynchronousPortableTask<T> : IPortableTask<T>
{
T _result;
public SynchronousPortableTask(T result)
{
this._result = result;
}
public void ContinueWith(Action<T> continuation, Action<Exception> error)
{
continuation(_result);
}
}
public class SynchronousPortableTaskFailure<T> : IPortableTask<T>
{
Exception _exception;
public SynchronousPortableTaskFailure(Exception exception)
{
_exception = exception;
}
public void ContinueWith(Action<T> continuation, Action<Exception> error)
{
error(_exception);
}
}
}
// An implementation of IPortableTask which wraps an actual Task.
// This has to go in a project targeting a platform or platforms which support Task and async/await
using System;
using System.Threading.Tasks;
namespace PortableTasks
{
public class AsyncPortableTask : IPortableTask
{
Task _task;
public AsyncPortableTask(Task task)
{
this._task = task;
}
public async void ContinueWith(Action continuation, Action<Exception> error)
{
try
{
await _task;
}
catch (Exception ex)
{
error(ex);
return;
}
continuation();
}
}
public class AsyncPortableTask<T> : IPortableTask<T>
{
Task<T> _task;
public AsyncPortableTask(Task<T> task)
{
this._task = task;
}
public async void ContinueWith(Action<T> continuation, Action<Exception> error)
{
T result;
try
{
result = await _task;
}
catch (Exception ex)
{
error(ex);
return;
}
continuation(result);
}
}
}
// A portable view model which demonstrates how to consume APIs which may or may not
// be async from portable code using the IPortableTask interfaces.
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace PortableTasks
{
public class FileStorageTestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
IFileStorage _fileStorage;
public FileStorageTestViewModel(IFileStorage fileStorage)
{
_fileStorage = fileStorage;
_filename = "test.txt";
_contents = "Hello, World!";
LoadCommand = new SimpleCommand(Load);
SaveCommand = new SimpleCommand(Save);
DeleteCommand = new SimpleCommand(Delete);
ClearCommand = new SimpleCommand(Clear);
}
string _filename;
public string Filename
{
get { return _filename; }
set
{
if (_filename != value)
{
_filename = value;
RaisePropertyChanged("Filename");
}
}
}
string _contents;
public string Contents
{
get { return _contents; }
set
{
if (_contents != value)
{
_contents = value;
RaisePropertyChanged("Contents");
}
}
}
public ICommand LoadCommand { get; private set; }
public ICommand SaveCommand { get; private set; }
public ICommand DeleteCommand { get; private set; }
public ICommand ClearCommand { get; private set; }
public void Load()
{
_fileStorage.LoadFile(Filename).ContinueWith(
contents =>
{
this.Contents = contents;
},
ex =>
{
this.Contents = ex.ToString();
});
}
public void Save()
{
_fileStorage.SaveFile(Filename, this.Contents).ContinueWith(
() =>
{
},
ex =>
{
this.Contents = ex.ToString();
});
}
public void Delete()
{
_fileStorage.DeleteFile(Filename).ContinueWith(
() =>
{
},
ex =>
{
this.Contents = ex.ToString();
});
}
public void Clear()
{
Contents = string.Empty;
}
void RaisePropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class SimpleCommand : ICommand
{
Action _action;
public SimpleCommand(Action action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
}
// A simple portable abstraction for file save, load, and delete functionality
namespace PortableTasks
{
public interface IFileStorage
{
IPortableTask SaveFile(string filename, string contents);
IPortableTask<string> LoadFile(string filename);
IPortableTask DeleteFile(string filename);
}
}
// An implementation of IFileStorage for Metro style apps, which uses the WinRT storage APIs
using System;
using System.Threading.Tasks;
namespace PortableTasks
{
public class MetroFileStorage : IFileStorage
{
public IPortableTask SaveFile(string filename, string contents)
{
return new AsyncPortableTask(SaveFileAsync(filename, contents));
}
public async Task SaveFileAsync(string filename, string contents)
{
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await localFolder.CreateFileAsync(filename, Windows.Storage.CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(file, contents);
}
public IPortableTask<string> LoadFile(string filename)
{
return new AsyncPortableTask<string>(LoadFileAsync(filename));
}
public async Task<string> LoadFileAsync(string filename)
{
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile file;
try
{
file = await localFolder.GetFileAsync(filename);
}
catch
{
// TODO: figure out what happens if the file doesn't exist, only catch that type of exception
return string.Empty;
}
string contents = await Windows.Storage.FileIO.ReadTextAsync(file);
return contents;
}
public IPortableTask DeleteFile(string filename)
{
return new AsyncPortableTask(DeleteFileAsync(filename));
}
public async Task DeleteFileAsync(string filename)
{
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile file;
try
{
file = await localFolder.GetFileAsync(filename);
}
catch
{
// TODO: figure out what happens if the file doesn't exist, only catch that type of exception
return;
}
await file.DeleteAsync();
}
}
}
// An implementation of IFileStorage for Windows Phone, which uses isolated storage
using System;
using System.IO;
using System.IO.IsolatedStorage;
namespace PortableTasks
{
public class PhoneFileStorage : IFileStorage
{
public IPortableTask SaveFile(string filename, string contents)
{
try
{
var userStore = IsolatedStorageFile.GetUserStoreForApplication();
using (var isoFileStream = new IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, userStore))
{
//Write the data
using (var isoFileWriter = new StreamWriter(isoFileStream))
{
isoFileWriter.Write(contents);
}
}
return new SynchronousPortableTask();
}
catch (Exception ex)
{
return new SynchronousPortableTaskFailure(ex);
}
}
public IPortableTask<string> LoadFile(string filename)
{
try
{
var userStore = IsolatedStorageFile.GetUserStoreForApplication();
using (var isoFileStream = new IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, userStore))
{
using (var isoFileReader = new StreamReader(isoFileStream))
{
string contents = isoFileReader.ReadToEnd();
return new SynchronousPortableTask<string>(contents);
}
}
}
catch (Exception ex)
{
return new SynchronousPortableTaskFailure<string>(ex);
}
}
public IPortableTask DeleteFile(string filename)
{
try
{
var userStore = IsolatedStorageFile.GetUserStoreForApplication();
userStore.DeleteFile(filename);
return new SynchronousPortableTask();
}
catch (Exception ex)
{
return new SynchronousPortableTaskFailure(ex);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment