I've determined that having a per-platform "core" assembly for portable code (viewmodels, helpers, etc.) and a separate assembly per database/backing store (SqlCe, Sqlite, etc.) that the platform-specific core assemblies reference seems to work. This means that my model classes are still defined in the DAL assemblies, but I at least can provide a simple, common interface (defined in each DAL assembly, unfortunately, because of the DAL model classes) that still provides me with IQueryable support.
Thanks to "copy as link" in Visual Studio, setting up the core assemblies and ensuring that the common database service interface is the same for each DAL assembly is pretty easy. With #ifdef
I can even reuse a lot of the DAL model class files and conditionally compile attributes, database-specific code, etc. which allows me to use "copy as link" for them too.
public interface IDataService
{
IQueryable<ModelType1> ModelType1 { get; }
IQueryable<ModelType2> ModelType2 { get; }
void AddModelType1(ModelType1 item);
void RemoveModelType1(ModelType1 item);
void AddModelType2(ModelType2 item);
void RemoveModelType2(ModelType2 item);
void CreateDatabase();
void ResetDatabase();
}
The resulting map of references is kind of like this:
System.Data.Linq -> App.Data.SqlCe -> App.Core.WP -> App.WP
/ /
(some shared code) (all shared code)
/ /
Sqlite -> App.Data.Sqlite -> App.Core.Win8 -> App.Win8
It's nowhere near as clean as I'd like, but at least it seems to work.