Code Monkey home page Code Monkey logo

bobbytables's Introduction

BobbyTables

BobbyTables is a .Net ORM library for the Dropbox datastore API. It handles serializing and deserializing objects to and from the remote Dropbox datastore as well as handling pushing/pulling updates. BobbyTables supports .Net versions 2+, Silverlight 4+, Windows Phone 7.5+, and Windows store applications.

API & Usage

DatastoreManager


The DatastoreManager object is used to list/add/remove datastore objects. To create a DatastoreManager, you will need a Dropbox OAuth 2.0 bearer token (You can get this by completing an OAuth 2.0 handshake - see https://www.dropbox.com/developers/core/docs#oa2-authorize for more details)

var manager = new DatastoreManager("oauth_token");

// Getting or creating a datastore
var datastore = await manager.GetOrCreateAsync("test");

// Getting an existing datastore
var existing_datastore = await manager.GetAsync("default");

// Listing available datastores
var datastore_list = await manager.ListAsync();
foreach (var ds in datastore_list) {
  // ...
}

// Deleting a datastore
await manager.DeleteAsync(datastore);

// Wait for up to a minute or for remote changes to occur, whichever comes first
List<Datastore> changed = new List<Datastore>();
if (await manager.AwaitDatastoreChangesAsync(changed)) {
  // Any datastores that have remote changes applied are now in the changed list
}

// Wait for up to a minute or for a change to the list of datastores, whichever comes first
if (await manager.AwaitListChangesAsync()) {
  // calling manager.ListAsync will retrieve the changed list of datastores
}

NOTE: All API methods that make remote requests to dropbox have a synchronous implementation, an async implementation that uses .net 4.5 async/await, and an async implementation that uses callback functions for when async/await is not supported.

Datastore


A Datastore object Is analagous to a Database object in a typical ORM library. A Datastore contains Tables that can contain objects which can be added/updated/removed

Inserting data into a datastore table

class Appointment {
  public string Id;
  public DateTime Time;
  public List<String> People = new List<string>();
}

...

// pull in any remote changes and make sure we are up to date before
// trying to apply our own local changes
await datastore.PullAsync();

// get a reference to the appointments table
var table = datastore.GetTable<Appointment>("appointments");

var new_appointment = new Appointment{ Id = "1", Time = DateTime.Now() };
new_appointment.People.Add("Jules");

// insert the object into the table. An Id can also be specified for this method
// though because this object has a public string field called Id, this is worked
// out automatically (If you want you can inherit your objects from BobbyTables.Record
// which will provide this Id field). 
// Note: If the Id field is null or empty on insertion, it will be populated with an 
// auto generated id.
// Also note: this method is not awaited as it is only recording
// the change as pending locally. No changes have been pushed to Dropbox yet.
table.Insert(new_appointment);

// now lets commit the pending insert and push it out to Dropbox
if (await datastore.PushAsync()) {
  // Yay! the changes were accepted
} else {
  // Oh no! a conflict occurred due to another user submitting a change concurrently.
  // In this case we should revert our local changes, Pull in the latest changes and
  // try again. NOTE: The Transaction feature can help make handling conflicts easier.
  datastore.Revert();
}

Using transactions to handle conflicts

  // every operation inside the transaction will try to be pushed to Dropbox in a single
  // commit. If anything fails, all changes are reverted, the latest changes are pulled
  // from Dropbox, and the changes will be re-applied until the commit succeeds, or the 
  // max number of retries is exceeded
  var success = await datastore.Transaction(()=> {
    var table = datastore.GetTable<Appointment>("appointments");
    
    var new_appointment = new Appointment{ Id = "1", Time = DateTime.Now() };
    new_appointment.People.Add("Jules");
    
    table.Insert(new_appointment);
  }).PushAsync(); // can specify the number of retries as a parameter (default 1)
  

Storing dynamic objects/dictionaries

If you don't want to store strongly typed objects and instead would rather store objects containing a dictionary of key/value pairs you can do so by ensuring your stored objects inherit from the IDictionary<string,object> interface. Note that the .NET 4+ ExpandoObject already implements this interface, so storing dynamic ExpandoObjects is fully supported.

Retrieving and updating existing data

var table = datastore.GetTable<Appointment>("appointments");

// can search for objects using LINQ queries
var appointment = (from appt in table where t.Id == "1" select appt).SingleOrDefault();

// or you can search using the Id directly
appointment = table.Get("1");

appointment.People.Add("Vincent");
appointment.People.Add("Marcellus");

// now lets commit the pending update and push it out to Dropbox
await datastore.Transaction(()=> {
  table.Update(appointment);
}).PushAsync();

Saving and loading local datastore snapshots

// the local state of a datastore can be saved to a streamWriter. In this case we
// are choosing to save the local snapshot to a file on disk
using (var stream = new FileStream("C:\\db.json",FileMode.Create,FileAccess.Write)) {
  using (StreamWriter writer = new StreamWriter(stream))
  {
  	datastore.Save(writer);
  }
}

...

// We can then reload the old state by loading this file
using (var stream = new FileStream("C:\\db.json",FileMode.Open,FileAccess.Read)) {
  using (StreamReader reader = new StreamReader(stream))
  {
  	datastore = manager.Load(reader);
  }
}

Detecting when remote changes occur

while (true) {
  // Waits for up to a minute or for a change to occur, whichever happens first
  if (await datastore.AwaitPullAsync()) {
    // some remote changes occurred within the last minute and have been pulled in
    // to the local snapshot
  } else {
    // no changes occurred
  }
}

The fiddly details of serializing objects

Only public fields, and public readable/writable properties can be serialized. If you want a field to be ignored from serialization tag it with the [BobbyTables.Ignore] attribute.

The dropbox datastore API only has support for the following datatypes so any objects that have fields with an unsupported datatype will not be able to be serialized or deserialized correctly (Lists or Arrays of any of the below data types are also supported)

Dropbox datatype .NET datatype
str string
number float,Single,double
int Enums & int/uint 16,32,64
timestamp DateTime
blob List<byte>, byte[]

Advanced options for handling object ids

While BobbyTables will automatically look for an Id field on your objects when inserting and updating, it is possible to have more finegrained control over exactly which fields are used as the id for record objects. This can be useful in cases where you have domain objects that you cannot/do not wish to change in order to persist them to a datastore.

The first way is to specify the id separately from the object when it is inserted or updated as shown below

var table = datastore.GetTable<Appointment>("appointments");

var new_appointment = new Appointment{ Time = DateTime.Now() };
table.Insert("1",new_appointment);

The other option is to provide an id getter function which will return the value that should be used as the id for the object.

var table = datastore.GetTable<Appointment>("appointments");

var new_appointment = new Appointment{ Time = DateTime.Now() };
table.Insert( obj => obj.Time.ToString(), new_appointment);

Similarly, you can provide an id setter function when deserializing/enumerating objects if the object you are dealing with does not have a public Id field or property

var table = datastore.GetTable<Appointment>("appointments");
var appointment = table.Get( (obj,value) => obj.SetId(value), "1");

bobbytables's People

Contributors

mrsharpoblunto avatar cgallegu avatar

Stargazers

Krzysztof Jendrzyca avatar Javi Campaña avatar Alexei Vinidiktov avatar  avatar Ayulin avatar AddressXception avatar Diego Giacomelli avatar  avatar Taqiyuddin Bakir avatar Stefan Schälle avatar Vic avatar Tom Winzig avatar Ferdinando Santacroce avatar Todd Grotenhuis avatar Václav Slavík avatar Ashok Gelal avatar Harry McIntyre avatar Arkadiusz Grzeskiewicz avatar

Watchers

 avatar  avatar Pavel Luzhetskiy avatar 王继武 avatar  avatar

bobbytables's Issues

Insert using Dictionary instead of Class

Hi!
First of all, thank you for your library. I am writing a plugin that uses your library in a visual scripting program called Grasshopper and was curious if it was possible to insert records into tables via a Dictionary Object rather than using a class. The reason is, I would like users to be able to define the keys/values, their number, and type. Is this possible with this library?

Thank you in advance!
-Zach

Example:

var rec = new Dictionary<String, Object> ();

rec["date"] = DateTime.Now();
rec["items"] = "eggs";

var table = datastore.GetTable<Dictionary<String, Object>>("groceries");
table.Insert(rec);

400 Bad Request Errors

When I use any flavor of the GetOrCreate method (synchronous or asynchronous) for a table name that does yet exist in the data store I get back a 400 error.

The same call to a table that already exists in the datastore works fine.

v. 1.0.0.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.