One of the beauties of .NET framework is that it works the same way across all Microsoft platforms including PCs, tablets, gaming consoles, phones and browsers. However there are still some major differences between CLR and BCL versions to make the same code truly cross-platform.
For example, WinRT platform (Windows Store Apps) lacks FileStream class and, to accommodate COM reflection, turns CLR reflection along with messed up namespaces upside down. Windows Phone 7 BCL lacks HashSet and several Linq.Expression classes. Silverlight 5 lacks slim versions of synchronization primitives like ReaderWriterLockSlim, Stopwatch and BufferedStream classes.
It is not always that lacking features are just missing. It could be that the certain version of platform offers some other ways of doing the same things. Theoretically, if we stay in subset of .NET which is common between all these platforms, we could produce truly cross-platform assembly. We just need to use only those methods and classes which are available across all target platforms we’d like to address.
This technique is already available (built-in in VS2012) and called Portable Class Library. By selecting different combinations of target platforms (.NET 4.0-4.5, SL 4-5, WP 7-7.5-8, .NET for Windows Store Apps, Xbox 360) we limit our API set to lowest common denominator of those target platforms. The produced assembly is compatible with all target platforms on binary level, so no recompilation required.
Isn’t this a dream?
But practical experience proves that portable API set is too limited to be useful. The more-or-less complex library like Lex.DB is just impossible to make 100% portable across all target platforms: no files to read/write data, no thread synchronization and no expressions to make Lex.DB fast. Consider all issues and there is no Lex.DB.
What about abstracting some platform-dependent parts from Lex.DB into interfaces and implementing them in platform-specific assemblies? Nice idea, but what will we end up with? We’ll get a portable assembly that requires platform-dependent sidekick anyway. Right now we have just one platform-dependent all-in-one assembly, so why bother?
Wait; there should be some way to make portable database access. Yes, there is. Abstracting data access logic of your app itself.
The idea is simple. We put everything what could be portable in our portable Common Logic assembly. Some components have to interact with platform specific code. To make them portable with need to break the dependency by abstracting interaction via “service” interface.
- Data Entities – data classes to read/write in Lex.DB
- View Models – non-visual models of UI in your app
- Business Logic to work with data entities and view models
- Resources – text localization and other
- Interfaces to abstract underlying platform features (IMessageBox, IDialog, IApplication)
- IDataAccessService interface to abstract data access
- Service Locator or IoC (Inversion-Of-Control) singleton to obtain platform-specific service implementations
Platform-specific application references platform-specific Lex.DB and portable Common Logic assembly, gaining access to all classes above. During initialization phase our application sets the Service Locator or IoC-container up with platform-specific implementations of these services to allow Common Logic interact with the platform.
I wrote an article to Nokia Developer wiki, in details explaining this technique. The example provided is a Contacts application, that works on all MS platforms: WinRT, Windows Phone 8, Silverlight 5, .NET (WPF). 10000 records are loaded in a split second. Check it out.
In the example I use my Visual Studio plugin KindOfMagic, which makes implementation of INotifyPropertyChanged objects completely effortless. This plugin saved me lot of nerves and time. Check it also out.
That’s it for today.