ASP.NET Hosting

Testing object databases for .NET: Eloquera, STSdb, Siaqodb, Ninja Database Lite

Given all the activity around databases lately with the NOSQL movement, I decided to give a try to some of the new contenders.

Lately, I've added some object database management systems to SharpToolbox. You can find them all in the DBMS category.
I downloaded some of them to see what they look like and determine whether they are stable enough to start developments with them or not.

Here are the databases I tested: Eloquera, STSdb, Siaqodb, Ninja Database Lite. Most of them support LINQ, which is great.

Eloquera Database

I tested versions 2.8 and 3.0 Beta.
It seems that development is active, with new features and bug fixes being released often.
I received replies quickly to my questions in the forum.
Documentation exists but is somewhat light. XML documentation for IntelliSense would have been helpful.

Here are some of Eloquera Database's features:

  • Storing any .NET object without implementing any interface and without inheriting from a specially designed class
  • Queries can be expressed with LINQ or a query language based on SQL
  • Indexes
  • In-memory database support
  • Supports arrays in queries
  • Regular expressions support in queries
  • Bulk inserts and updates
  • Generic object support
  • Inheritance support in queries
  • Client/server or embedded
  • Unique identifier for any object within a database
  • Native GUID identifiers support
You can find more details on Eloquera Database's SharpToolbox page.

Eloquera can be used in client/server mode or embedded. When run in c/s, Eloquera runs as a Windows service. When you want to embed Eloquera in your application, you'll just add references to the Eloquera DLLs and use the Desktop mode. There's no real difference between the two modes from your program's perspective.

My small testing consisted of two classes:

Snippet

public class Company
{
  public string Name { getset; }
}

public class Job
{
  public Company Company { getset; }
  public String Title { getset; }
}

Here is how I used them to persist and save objects:

Snippet

Snippet
const string connectionString = "server=localhost;password=pwd;options=none;";
const string databaseName = "Base1";

using (var db = new DB(connectionString))
{
  db.DeleteDatabase(databaseName, true);
  db.CreateDatabase(databaseName);
  db.OpenDatabase(databaseName);
  db.Store(new Job {
Company = new Company { Name = "Company 1" },
 Title = "Design Engineer"
});
  db.Store(new Job {
Company = new Company { Name = "Company 2" },
Title = "Project Manager"
});
}

using
 (var db = new DB(connectionString))
{
  db.OpenDatabase(databaseName);
var query =
   from Company company in db
  where company.Name.Contains("1")
   join Job job in db on company equals job.Company into jobs
  select new { Company = company, Jobs = jobs };
Snippet
  Console.WriteLine("Jobs by company");
  ObjectDumper.Write(query, 2);
}

Here is a sample SQL-like query:

Snippet

db.ExecuteQuery("SELECT Company WHERE Name LIKE '%1%'")

The API is simple and it was not difficult to get started.

STSdb

I tested version 3.5.6.
I'm not able to tell whether development is active. I haven't seen any release notes or roadmap.
There isn't much activity in the forum. Though, I received a reply to a message I posted there in about 24 hours.
Documentation consists of a partial API reference. XML documentation is available but isn't complete either.

Here are some of STSdb's features:

  • 100% managed
  • Supports different storage modes (on-disk, in-memory and combined)
  • Compression of columns of data
  • ACID transactions
  • LINQ support
You can find more details on STSdb's SharpToolbox page.

STSdb is an embedded database. When you download it, what you get is a 181 KB DLL that you'll reference in your project.

I was not able to use the same classes because instances are not shared between Job and Company classes. Here are the classes I used this time:

Snippet

Snippet
public class Company
{
  public uint ID { getset; }
  public string Name { getset; }
}

public class Job
{
  public uint CompanyID { getset; }
  public String Title { getset; }
}

You can notice that there is no direct reference between the two classes. Object will then be joined by ID only.

Here is my test code:

Snippet

const String filename = "Test.stsdb";

using (var repository = StorageEngine.FromFile(filename))
{
  var jobsTable = repository.Scheme.CreateOrOpenXTable<ulongJob>(
new Locator("Jobs"));
  var companiesTable = repository.Scheme.CreateOrOpenXTable<ulongCompany>(
new Locator("Company"));
Snippet
  companiesTable[0= new Company { ID = 1, Name = "Company 1" };
companiesTable[1= new Company { ID = 2, Name = "Company 2" };
jobsTable[0= new Job { CompanyID = 1, Title = "Design Engineer" };
jobsTable[1= new Job { CompanyID = 2, Title = "Project Manager" };
Snippet
  repository.Scheme.Commit();
  jobsTable.Commit();
  companiesTable.Commit();

// ...


  jobsTable = repository.Scheme.OpenXTable<ulongJob>(
new Locator("Jobs"));
  companiesTable = repository.Scheme.OpenXTable<ulongCompany>(
new Locator("Company"));
 var query =
    from companyEntry in companiesTable
    let company = companyEntry.Record
    where company.Name.Contains("1")
    join jobEntry in jobsTable on company.ID equals jobEntry.Record.CompanyID
 into jobs
    select new { Company = company, Jobs = jobs.Select(job => job.Record) };

  ObjectDumper.Write(query, 2);
}

As you can see, STSdb introduces notions such as XTable and Locator, and it requires you to work explicitly with indexes to add items to the database.
This may make it powerful or fast, but it makes coding less intuitive than with Eloquera.

The fact that I had to remove the reference from Job to Company requires dealing with joins each time, which is something I'd like to avoid. I'm not in a relational world, but objects should be connected together in my book.

Siaqodb

I tested version 3.5.6.
Development and forum seems to be active.
Documentation consists of the API reference, a tutorial and sample projects. XML documentation is missing, which is a mistake because it seems that it exists as the API reference shows.

Here are some of Siaqodb's features:

  • Objects are stored as they are without any conversion/mapping
  • LINQ is the only query engine that can be used to retrieve objects or members of objects from database
  • Full POCO support
  • Automatic object schema refactoring
  • Import/export from/to XML
  • Thread safe
  • Any object has unique OID (object identifier) for its Type
You can find more details on Siaqodb's SharpToolbox page.

Siaqodb is an embedded database for .NET, Mono and Silverlight.
The download installs a 135 KB DLL for .NET, as well as DLLs for Silverlight and Windows Phone 7. It also installs a tool for running queries against Siaqodb databases. The UI is similar to the one of LINQPad.

Again, I had to create new classes because only primitive types are supported:

Snippet

Snippet
Snippet
public class Company
{
  public int OID { getset; }

  public uint ID { getset; }
  public string Name { getset; }
}

public class Job
{
  public int OID { getset; }

  public uint CompanyID { getset; }
  public String Title { getset; }
}
Here is my sample test code:
Snippet
var dbPath = Path.Combine(
Path.GetDirectoryName(Environment.GetCommandLineArgs().First()),
 "Data");

if (Directory.Exists(dbPath))
  Directory.Delete(dbPath, true);
Directory.CreateDirectory(dbPath);
var db = new Sqo.Siaqodb(dbPath);

db.StoreObject(new Company { ID = 1, Name = "Company 1" });
db.StoreObject(new Company { ID = 2, Name = "Company 2" });

db.StoreObject(new Job { CompanyID = 1, Title = "Job 1" });
db.StoreObject(new Job { CompanyID = 2, Title = "Job 2" });

// ...

db = new Sqo.Siaqodb(dbPath);

var query =
  from Company company in db
  where company.Name.Contains("1")
  join Job job in db on company.ID equals job.CompanyID into jobs
  select new {Company = company, Jobs = jobs };

var companyJobs = query.ToArray();

Console.WriteLine("Direct from DB:");
ObjectDumper.Write(companyJobs, 1);

Console.WriteLine();

var realCompanies = companyJobs.Select(item =>
  {
    var company = new RealCompany { Name = item.Company.Name };
    company.Jobs =
item.Jobs
.
Select(job => new RealJob { Company = company, Title = job.Title })
.
ToList();
    return company;
  });

Console.WriteLine("After conversion:");
ObjectDumper.Write(realCompanies, 1);

db.Close();

As you can see, given that there can be no automatic reference between objects, I use some conversion code to convert what comes out of the database to another model.
Here are the target classes:

public class RealCompany
{
  public IList<RealJob> Jobs { getset; }
  public string Name { getset; }
}

public class RealJob
{
  public RealCompany Company { getset; }
  public String Title { getset; }
}

Ninja Database Lite

I tested version 1.0.
As it's a brand new product, it's not possible yet to tell whether development and forum will be active.
There documentation is light, with reference help, a small tutorial, and sample projects. XML documentation is provided.

Here are some of Ninja Database Lite's features:

  • Compact binary serialization
  • Compression
  • Encryption
  • Automatic type registration
  • Supports Windows Phone 7, Silverlight, .NET 3.5 and .NET 4

You can find more details on Ninja Database Lite's SharpToolbox page.

Ninja is an embedded database for .NET, Silverlight and WP7.
The download installs a 58 KB DLL for .NET 4, as well as DLLs for the other platforms.

I had to use IDs again, because although complete object graphs are persisted, the instances don't seem to be shared between the Job and Company classes.

Here is the code:

Snippet

var db = new NinjaDb { DatabaseName = "Base 1" };

var companies = new List<Company> {
new Company {ID = 1, Name = "Company 1"},
new Company {ID = 2, Name = "Company 2"}
};
db.Save(companies);

var jobs = new List<Job> {
  new Job { CompanyID = 1, Title = "Job 1" },
  new Job { CompanyID = 2, Title = "Job 2" }
};
db.Save(jobs);

// ...

companies = db.Load<List<Company>>();
jobs = db.Load<List<Job>>();

var query =
  from company in companies
  where company.Name.Contains("1")
  join job in jobs on company.ID equals job.CompanyID into companyJobs
  select new { Company = company, Jobs = companyJobs };

ObjectDumper.Write(query, 2);
As you can guess from the code, all the objects are loaded and the query doesn't run "in the database".

Conclusion

That's about all I had time to do with these databases for now.
It was enough to get a feel for their usability and stability. It's not enough though to tell whether they are efficient and stable.

For the moment, I consider that they are not mature enough, but they're worth keeping under my radar. I may use them in the future.

PS: I know other object databases exist for .NET (db4o, Versant Object Database, Objectivity, Objectivity/DB, Perst), but I don't like their pricing and/or find them too heavy for the job.

4 Comments

  • Nice review. I liked Eloquera quite much when I tested it back in the day.

    I also recommend db4o to others, I've seen several people complain that it's clear that its code comes from a Java background, but I've been happy with it so far.

  • The other problem with db4o is the pricing: no price is displayed on the db4o website for a commercial license.

  • Should try PERST, has a clear business policy and cheaply, are only 500 dollars and work, on the other hand you are right people have an ambiguous policy Versant and prices out of control.

  • Hi all,

    I've tried db4o and Eloquera (db4o being free for non-commercial use). At first my opinion was that db4o was better since it supported model change, but since Eloquera added that in version 4.1, I've shifted towards Eloquera :

    - It is faster
    - Simpler to do basic config (ID and Index attributes on the POCO classes are useful)
    - Integrated uid's ("long int") that are VERY useful in disconnected situations (web apps, for example), since updating an object that doesn't originate from the database is done transparently (Eloquera uses the id to match and update an existing element of the same type in the database).
    - I've also had some minor problems with db4o while query on an inherited field

Comments have been disabled for this content.