sterling for windows phone 7
DESCRIPTION
Learn about the lightweight NoSQL database for Windows Phone called Sterling.TRANSCRIPT
wintellect.comconsulting training design debugging
Sterling DB for Windows Phone 7
Jeremy LiknessProject Manager, Senior [email protected]
Copyright © 2011
wintellect.comconsulting training design debugging
Founded by top experts on Microsoft – Jeffrey Richter, Jeff Prosise, and John Robbins – we pull out all the stops to help our customers achieve their goals through advanced software-based consulting and training solutions.
Consulting & Debugging• Architecture, analysis, and design services• Full lifecycle custom software development• Content creation• Project management• Debugging & performance tuning
Training• On-site instructor-led training• Virtual instructor-led training• Devscovery conferences
Design• User Experience Design• Visual & Content Design• Video & Animation Production
what we do
who we are
how we do it
consulting training debuggingdesign
wintellect.comconsulting training design debugging
• What is Sterling?• Brief History• Stated Goals• Features• Sterling vs. SQL CE• Who is Using Sterling?• Demo: Recipe Application• Questions?
Agenda
wintellect.comconsulting training design debugging
What is Sterling?
“Sterling is a lightweight NoSQL object-oriented database for .Net 4.0, Silverlight 4 and 5, and Windows Phone 7 that works with your existing class structures. Sterling supports full LINQ to Object queries over keys and indexes for fast retrieval of information from large data sets.”
• Sterling is not a fully relational database • Sterling is not a document database• Sterling is not a transactional, large scale database• Sterling focuses on specific serialization concerns• Sterling is suitable for local caches • Sterling is suitable for local lightweight data storage or a
window of data that synchronizes with a parent server• Sterling is a very easy way to quickly persist existing classes,
especially with dynamic schemas
wintellect.comconsulting training design debugging
Brief History
• Many customers were working with Silverlight 4 because of the ability to have the out-of-browser (OOB) offline mode
• Found that existing serialization strategies tended to be shallow, invasive, and lacked query capabilities
• Used various techniques such as synchronizing lists as individual items and then providing a “keyed” index or view into the list
• Built first version of Sterling to abstract this and introduce concept of both indexes and foreign keys to serialization
• Windows Phone 7 was released and Sterling filled the need for a local database
• Was able to port over to run on Windows Phone 7 within hours (mostly just needed to stub Silverlight 3 – Silverlight 4 differences)
• Popularity grew and requests came in for in-memory, .NET, etc. versions, (1.5 release)
wintellect.comconsulting training design debugging
Stated Goals
• Non-intrusive – don’t modify your classes or derive from some base class just to be able to use it.
• Lightweight – current DLL is < 100 kilobytes.
• Flexible – extensions for adding compression, encryption, triggers, custom serializers, etc.
• Portable – same API in browser, on desktop/server and in phone (possible Mono versions in the pipeline)
wintellect.comconsulting training design debugging
Features
• Recurses complex object graphs and handles cycle conditions• Handles public properties and fields (including
interface/abstract) • Ability to suppress serialization of fields, properties, and types• Flexible “is dirty” support to avoid saving full graph• Driver-model allowing in-memory, isolated storage versions
for phone• Easy configuration – specify table type, key type, and how to
resolve the key or index• “Save as” allows saving derived types to base class• Full “foreign key” allowing child objects to serialize to
separate tables• Binary serialization for much smaller footprint• Extensions for serialization, compression, encryption, etc.• Trigger support• Database backup and restore• LINQ to Object queries on in-memory keys, indexes, “records”
wintellect.comconsulting training design debugging
Sterling DB vs. SQL CEFeature Sterling DB SQL CE
Database Backup
Database Create
Insert Many Items
Delete Items
Truncate Table
Search Substring
Search Key
Search Key w/ Joins
Search Non-Idx Col
Search Index
Serialize Full Graph
Dynamic Types
wintellect.comconsulting training design debugging
Who is Using Sterling?
• HealthCaddy• Sharp DropBox Client
for .NET (WP7 Library)• Expense Report Tracker• Horse Vaccine Tracker• Stacks for Instapaper• SmugSeven• RunKeeper• MyReiningScores• Many more that you can read
about on the Sterling site
wintellect.com
demo
consulting training design debugging
recipe application
wintellect.comconsulting training design debugging
Define your Database
public class RecipeDatabase : BaseDatabaseInstance{ protected override List<ITableDefinition> _RegisterTables() { return new List<ITableDefinition>(); }
/// this is optional, will default to full type name public override string Name { get { return STERLING_RECIPES; } }}
wintellect.comconsulting training design debugging
Define a “Table” and Key
CreateTableDefinition<IngredientModel, int>(i => i.Id)
* Keys are stored in-memory for fast search & lookup (100,000 records w/ Int32 key = 400,000 bytes, < 400Kb memory)
This is provided in the base database class
Type of the “table”
Type of the key
Expression: given the “table” how do I get the key?
wintellect.comconsulting training design debugging
Composite Keys
public static string GetCompositeKey(TestCompositeClass testClass){ if (testClass == null) return string.Empty; return string.Format("{0}-{1}-{2}-{3}", testClass.Key1, testClass.Key2, testClass.Key3, testClass.Key4);}// method 1 uses a stringCreateTableDefinition<TestCompositeClass,string>(GetCompositeKey)
// method 2 uses a class to represent the key // requires a custom serializerCreateTableDefinition<TestCompositeClass, TestCompositeKeyClass>(k=>new TestCompositeKeyClass(k.Key1, k.Key2, k.Key3, k.Key4))
wintellect.comconsulting training design debugging
Define an Index
CreateTableDefinition<FoodModel, int>(f => f.Id)
.WithIndex<FoodModel, string, int>
(IDX_FOOD_NAME, f => f.FoodName)
* Indexes, like keys, are stored in memory so choose wisely!
Extension method for tables
Type of the key
Type of the tableType of the index
Name of the index
Expression: given table, how do I get index?
wintellect.comconsulting training design debugging
Activating the Engine/Databases_engine = new SterlingEngine();
// register custom serializers_engine.SterlingDatabase.RegisterSerializer<TypeSerializer>();
// register any loggers (or use the default logger)_logger = new SterlingDefaultLogger(SterlingLogLevel.Information);
// activate the engine – now ready for your databases_engine.Activate();
// here is a database registrationDatabase = _engine.SterlingDatabase.RegisterDatabase<RecipeDatabase>();
wintellect.comconsulting training design debugging
Drivers
Database = _engine.SterlingDatabase.RegisterDatabase<RecipeDatabase>();
Database = _engine.SterlingDatabase.RegisterDatabase<RecipeDatabase>( new IsolatedStorageDriver());
Database =_engine.SterlingDatabase.RegisterDatabase<RecipeDatabase>( new MyCustomDriver());
wintellect.comconsulting training design debugging
Serializers
// required for any keys that are not core value types// required for any classes that are not easily de-serialized// (i.e. no parameterless constructor, etc. – e.g. “Type” class)
public class TypeSerializer : BaseSerializer { public override bool CanSerialize(Type targetType) { return typeof (Type).IsAssignableFrom(targetType); }
. . .}
wintellect.comconsulting training design debugging
Serializers (cont.)
public class TypeSerializer : BaseSerializer { public override void Serialize(object target, BinaryWriter writer) { var type = target as Type; . . . writer.Write(type.AssemblyQualifiedName); } . . .}
wintellect.comconsulting training design debugging
Serializers (cont.)
public class TypeSerializer : BaseSerializer { public override object Deserialize(Type type, BinaryReader reader) { return Type.GetType(reader.ReadString()); } . . .}
wintellect.comconsulting training design debugging
Triggers
public class IdentityTrigger<T> : BaseSterlingTrigger<T,int> where T: class, IBaseModel, new(){ private static int _idx = 1;
public IdentityTrigger(ISterlingDatabaseInstance database) { // if a record exists, set it to the highest value plus 1 if (database.Query<T,int>().Any()) { _idx = database.Query<T, int>().Max(key => key.Key) + 1; } }}
wintellect.comconsulting training design debugging
Triggers (cont.)
public override bool BeforeSave(T instance){ if (instance.Id < 1) { instance.Id = _idx++; }
return true;}
public override void AfterSave(T instance){ // set dirty flag? return;}
wintellect.comconsulting training design debugging
Database Setup
// register triggerdatabase.RegisterTrigger(new IdentityTrigger<FoodModel>(database));
// check for existing recordsif (database.Query<CategoryModel, int>().Any()) return;
// truncate tablesdatabase.Truncate(typeof (MeasureModel));database.Truncate(typeof (FoodModel));
// save an entity (handles update and insert)database.Save(measure);
database.Flush(); // ensure keys/indexes are persisted!
wintellect.comconsulting training design debugging
Dynamic Tombstoning Supportvar tombstone = new TombstoneModel {SyncType = typeof (IRecipeViewModel)};tombstone.State.Add(RECIPE_ID, _recipe.Id);Tombstone.State.Add("ComplexType", MyComplexType);database.Save(tombstone);
// handles recursion into sub-lists and dictionaries// further recursion on types // will not choke on cycles// take a look at tests for examples// TestCycle, TestEnum, TestInterfaceProperty, TestNestedInstance
wintellect.comconsulting training design debugging
Queries
public IEnumerable<RecipeModel> Recipes{ get { return from r in App.Database.Query<RecipeModel, int, string, int>(RecipeDatabase.IDX_RECIPE_CATEGORYID_NAME) where r.Index.Item1.Equals(CurrentCategory == null ? 0 : CurrentCategory.Id) orderby r.Index.Item2 select new RecipeModel {Id = r.Key, Name = r.Index.Item2}; }}
Dual key handled by tuple
“Covered query” = no deserialization
wintellect.comconsulting training design debugging
Queries (cont.)
get{ if (string.IsNullOrEmpty(_foodText)) { return Enumerable.Empty<FoodModel>(); } var foodTextLower = _foodText.ToLower(); return from f in App.Database.Query<FoodModel, string, int>(RecipeDatabase.IDX_FOOD_NAME) where f.Index.ToLower().Contains(foodTextLower) orderby f.Index select new FoodModel {Id = f.Key, FoodName = f.Index};}
“Covered query” = no deserialization
wintellect.comconsulting training design debugging
Queries (cont.)
get{ return from m in App.Database.Query<MeasureModel, int>() orderby m.LazyValue.Value.FullMeasure
select m.LazyValue.Value;}
Second access will retrieve cached value
Lazy value access will de-serialize
wintellect.comconsulting training design debugging
Compression/Encryption
public class ByteInterceptor : BaseSterlingByteInterceptor{ override public byte[] Save(byte[] sourceStream) { var retVal = new byte[sourceStream.Length]; for (var x = 0; x < sourceStream.Length; x++) { retVal[x] = (byte)(sourceStream[x] ^ 0x80); // xor } return retVal; }
override public byte[] Load(byte[] sourceStream) { var retVal = new byte[sourceStream.Length]; for (var x = 0; x < sourceStream.Length; x++) { retVal[x] = (byte)(sourceStream[x] ^ 0x80); // xor } return retVal; }}
wintellect.comconsulting training design debugging
Compression/Encryption (cont.)databaseInstance.RegisterInterceptor<ByteInterceptor>();databaseInstance.RegisterInterceptor<ByteInterceptor2>();
// called in order of registration on save// called in reverse order on load
// i.e. compression -> encrypt, then decrypt -> decompress
wintellect.comconsulting training design debugging
Sterling Resources
• Full source, documentation, discussions, support:http://sterling.codeplex.com/
• User’s Guide (your BEST guide is the unit tests):http://www.sterlingdatabase.com/
• Telerik todolist “How-to” Application:http://www.telerik.com/products/windows-phone/getting-started/todolists.aspx
• My Blog:http://csharperimage.jeremylikness.com/ (@JeremyLikness on Twitter)
wintellect.com
Questions?
consulting training design debugging
Jeremy LiknessProject Manager, Senior [email protected]