As a developer you probably often write code for accessing data with OLEDB, DAO, ODBC, ADO or ADO.NET. Or maybe you are using an object relational mapper like NHibernate or even ActiveRecord. You probably noticed that each time you needed more coding than you wanted. In this demo application you will see that accessing your data will never be as easy as with db4o.

Db4objects has now joined the power of querying data with LINQ with the ease of accessing data with db4o. Since February 2008 db4o version 7.2 beta has become available (the production release is now available). This version contains a LINQ provider for db4o.

LINQ will probably be the new Visual Studio 2008 feature that will speak most to your imagination. With LINQ you can use SQL like syntax inside your code for retrieving various kinds of information. By now there are already a couple of dozen LINQ providers. A LINQ provider is the code that lets you query a specific type of data. Visual Studio has a LINQ provider for objects (LINQ), SQL (DLINQ) and XML (XLINQ). Searching the internet you will find many more providers, for instance for Excel, Flickr, Google, Sharepoint, WMI, etc. The cool thing is that the query syntax is the same for every provider. You can even query multiple providers in one query.

The LINQ query syntax looks very much like SQL. The biggest difference is the command order. You will start with the 'from' statement. The reason for this is that intellisence is able to give you hints the moment you have specified the object in the from statement.

Db4o is an open-source native Object-oriented Database Management System (ODBMS) available for both .NET and Java platforms. As a native ODBMS, the database model and application object model are the same, hence, no mapping or transformation is required to persist and query objects with db4o. Regarding usage mode, db4o can be deployed either as a standalone database or a network database. Finally, db4o has support for the schema evolution, indexing, transaction and concurrency, database encryption, and replication service (among db4o databases and certain relational databases). At this moment the latest version of db4o is 7.2 and available under two licenses: GPL and a commercial runtime license.

In February 2008 db4objects released beta version 7.2 of db4o. This was the first release that included the LINQ provider for db4o. Now 2 months later the 7.2 version has been release as a "production" release and an optimized version 7.3 beta is now available. Db4o was already quite easy but now with LINQ for db4o you can do it using standard query syntax instead of finding out how to use db4o's native query syntax. Querying data in a db4o database with LINQ will feel like you are querying data that is already available in your application. You do not have to make object relational mappings and you do not have to use a heavy data access layer. With LINQ for db4o you can do everything with only a couple of lines of code. Linq expressions are analyzed and optimized down to db4o's fast querying interface: SODA. Whenever a suitable translation cannot be found the query executes as a plain Linq for Objects query (a predicate that is tested against every object of the specified type).

For showing you the most basic operations this demo solution includes a very simple console application. This demo creates 2 new objects and stores them in a db4o database. Before and after each save operation a list of all available objects is written to the console. Here is the entire code:

When you run this demo for the 2nd time, then at the end of the demo there will be 4 objects in the database instead of 2. If you declare a new object in code and you save that object to db4o then it will also be a new object in the database. If you want to rerun this demo more often, then just uncomment the line tat deletes de database file.

One other thing that comes to mind very quickly is the question what will happen if you change your object. Just try it out by uncommenting the Age property and the console write that will display that property (make sure that the file delete statement is commented out). As you can see the application keeps on working. The old objects will show the default value.

This demo is not meant to be an exhaustive presentation of all the capabilities of LINQ or db4o. Instead I want to show you how easy it is to use in an ASP.NET website. (using it in a winforms application is just as easy.). This demo will show you how to use LINQ for db4o with some popular .NET databound objects. Every demo will be shown in its own .aspx page and can be executed without the need of the other pages.

The data model for this demo is very simple. There are only 2 objects with a couple of properties. We have an Employee object and an Office object. The 2 objects are related to each other through the property HomeBase in the Employee object which is of type Office. Because the property is of the type Office it is declaring a relation between the two objects. The Employee object also has the property Manager which is of type Employee. This property declarers a hierarchical relation to itself.

Both objects are also overwriting the method ToString to make it easy to show what an object instance is about. I only gave the Office object the methods Select, Insert, Update and Delete. These methods are there so that the object could be used in the DetailsView demo

If you want to use Linq for db4o then you need to add at least 2 references. Db4objects.Db4o is the core library for db4o. This one is always necessery. For using Linq for db4o you will also need the Db4objects.Db4o.Linq library. There are Linq query constructions that need 2 additional libraries. This was the case with the TreeView demo. The libraries that are also needed are Cecil.FlowAnalysis and Mono.Cecil. To bad that LINQ for db4o only gives you a warning about the need of these libraries at runtime. All of these libraries are part of the db4o v7.2 download. Besides those libraries I also added the log4net library. The DataManager is using log4net for creating the log file.

Of course like any other data storage you still have to tell what database to use. The time will also come quick that you want to be able to say how the data storage should be used. And then we also want to do that on one central location. For this I created the static class db4oManager. If you do not need special configuration or logging, then this class only needs to be a couple of lines of code. Here is the most basic version of that class. The WithContainer method is set up in such a way that it is easy to use with a Lambda Expression. The demos will show you how to use this method.

The bad news is that this won't work when you get more than a couple of visitors to your website. If you reference a container by executing the OpenFile method then the file will be locked the moment you write to it. This is OK for most client applications but not for most web applications. Fortunately db4o has a Client-Server mode for overcoming this problem. This could also easily be extended to a Networked Client-Server application where the server could be on a different machine. Here is the code for the db4oManager where the db4o database is accessed using the Embedded Client-Server mode.

This Embedded Client-Server mode is still not optimal. It could easily be optimized by managing the server at the application level or even a read only client at the application level. In the latest version (May 9) of this demo I added static properties for this which are set in the static constructor. We can now even use the db4oManager.Client directly in our LINQ queries without using the WithContainer construction. See the demos below how to use this. Here is the code for the constructor that creates the client object container that we will use.

You do have to be aware that all actions in an container will be handeled as one transaction. So you only can use this db4oManager.Client for read only access. If you want to manipulate data, then use the WithContainer construction. If you want te see an other implementation of a Client-server configuration, then look at the httpHandler that is part of the Db4oSiteCS.vsi template project that can be found at the page: Starter Kit - Web Applications With db4o.

Like most databases you will probably also want to specify indexes and constrains. For most constraints you should just add validation code to your property setters. For declaring the Indexes and unique value constraints the static constructor of the DataManager class is a very nice location. In the code below you will see a strange field notation. This is because the properties are declared using the new C# 3.0 syntax and we do not have private variables. Besides that the declaration is straight forward.

The DataManager class in the demo will also show you how to react on events that de db4o manager raises. Discussing these events is out of scope for this publication. If you want to see more, then have a look at the WithLoggingContainer method. You can also look for more information about db4o events in the db4o reference pages about the Event Registry API. For managing your logfiles there is no better tool than log4net. A nice intro by David Salter about log4net can be found here.

All the demos can be accessed from the Default.aspx page. Besides links to the demos and links to various sources the Default.aspx page also has 2 buttons. One for deleting the database and an other one to create the database and fill it with dummy data. The Default.aspx page will also show the number of offices and employees in the database. Below you will see the demos explained.

One of the new controls in ASP.NET 3.5 that I think will be very popular is the <asp:ListView> control. The ListView control supports the data editing, insertion, deleting, paging and sorting semantics of higher-level controls like the GridView. But - unlike the GridView - it provides you with complete control over the html markup generated. This demo will not do any data editing. You can see how to do data editing in the DetailsView demo.

The ListView in this demo is using a (slightly modified) pager that is written by Luis Ramirez. The usage of this pager is of no concern for the LINQ for db4o demo. Its just a nice looking pager and if you like you could use any other pager. This is what it will look like:

Using a Repeater control inside the ItemTemplate of an other Repeater control is as easy as using any other control. In the inner repeater you will declare the datasource to be the child object using the Eval syntax. For this demo the LINQ query will return a list of objects with only 2 properties. It will return a property of type Office plus a IEnumerable<Employee> property.

For selecting the data your query will become a little more complex. This LINQ query is using a subquery for selecting the list of employees for every office. Instead of using the Office collection as the base for this query it is doing a group query on the Employee table. The benefit of this is that you only get the Offices where there are employees with that office as its homebase. (and we can see how to use the group into syntax)

Usually you do not want to show every object in your database. Adding a where statement is as easy as it would be in SQL. If you want more information about how to do that, then have a look at this page with 101 LINQ samples. This demo will show you how to use a second collection in the LINQ query for filtering data. Although accessing the data in this collection will be done using an other provider than the LINQ for db4o provider, the query syntax will look like the collection is part of the db4o container. The aspx page is quite straightforward. There are no strange things here:

It is very easy to write one LINQ query that uses multiple LINQ providers. This will look very transparent in the LINQ code. This demo uses a CheckBoxList of all the offices for filtering the Employee data and it will show the result in a Repeater. Just select one or more offices and you will see all the employees that have that office as their HomeBase. This is all the code you need:

DataManager.WithLoggingContainer(container =>
{
if (!IsPostBack)
{
// Initially we will fill the checkbox list with all the existing Offices
CheckBoxListOffices.DataSource = (from Office off in db4oManager.Client
select (Office)off).ToList();
CheckBoxListOffices.DataBind();
}
else
{
// After a postback we will show a list of employees which have an office
// in one of the selected offices.
RepeaterEmployees.DataSource = (
from Employee emp in db4oManager.Client
from ListItem off in CheckBoxListOffices.Items
where emp.HomeBase.OfficeName == off.Value
&& (off.Selected == true)
select emp).ToList();
RepeaterEmployees.DataBind();
}
});

If you want to use a <asp:DetailsView> with LINQ for db4o then you need to link the DetailsView to an ObjectDataSource control. In the ObjectDataSource you will set all the methods that are needed for data manipulation. The control expects the methods to be in the Office class itself. This is why the Office class has static methods for Select, Insert, Update and Delete. These methods must have a specific signature.

This details view does not have any code in the code behind file. All data manipulation code is part of the Office class. You do have to be aware that if the office object is passed to one of those procedures, that it is not connected tot the actual db4o container. This is why the Delete and Update methods fist have to retrieve the object from the database. The data manipulation must be performed on the object that is retrieved from the db4o database.

In the code above we are doing an insert without testing if the object already exists. In the DataManager we specified the OfficeName to be unique. Adding an already existing OfficeName will now throw an UniqueFieldValueConstraintViolationException which you could handle. An other option would be to check if the object already exists (for this you could use the select that is in the update method).

A treeview can give you a nice presentation of your hierarchical data. In our case we have Employees each having a manager which is also an Employee. We will build a nice treeview for this. In the aspx page the treeview is declared just like any other treeview.

Populating a treeview with data is usually quite some work. Usually you will use a recursive method that executes a loop for adding child nodes. There is an alternative. You could add IHierarchy support to your data. Fortunately Scott Piegdon made a very nice tutorial that explains how to do that. For more information have a look at his article Implementing IHierarchy Support Into Your Custom Collections. I created the EmployeeCollection class for the IHierarchicalEnumerable collection and the class EmployeeHierarchy which has the Employee class as its base class plus the IHierarchyData interface for the objects. I choose to make s separate class for the IHierachyData implementation instead of embedding it within the Employee class itself. The details for this are out of scope for this article. For more information about those classes, please have a look at Scott's article

When you bind a <ASP:TreeView> control to an IHierarchicalEnumerable collection then the entire tree will be generated. In our case this means that for every Employee a query is executed to look what the child objects are. When I looked at log file, I noticed that that building the tree will execute about 300 queries. On my machine that took about 20 seconds. Of course this performance is unacceptable. Fortunately this could easily be fixed by doing 1 query that retrieves all the objects and then executing the queries on the objects in memory instead of querying the database. For this I created an EmployeeCache class. Now the page shows within 2 seconds.

For the IHierarchy code I needed to convert the result of a LINQ query a couple of times so I created an extension method that can be found in the EmployeeHierarchyExtensions class.

The code created for the IHierarchy support is now considerable. The good thing is that you can now use is wherever you need an object with IHierarchy support. The code is also quite generic and is very easy to port to other objects. The only thing we now have to do in the aspx page is:

LINQ for db4o is extremely easy to use and very powerful. The learning curve is very low. This publication only scratched the surface of the possibilities of db4o. If you are trying to compare it with the RDBMS that you now use then you definitely have to take into consideration that:

The performance is very good. According to this benchmark its one of the best.

All work within db4o ObjectContainer is transactional. (Manual rollback and commit are available.)

History

This demo was created April 2008 for a presentation that I gave for the company that I work for (Mirabeau)

May 9 : Changed the db4o manager so that the Server instance will stay active during the application. I also added a global Client reference so that we could just reference the db4o container in a LINQ query without explicitly instantiating a container. We now even need less code for accessing a db4o database.

So even if db4o assembly is in the GAC and therefore has FullTrust, and has the AllowPartiallyTrustedCaller attribute, when it tries to do something dangerous the system will walk up the stack to make sure the *callers* have the right to do this too -- and of course the medium-trust web app is not going to have permission. The way to get past this would be to have the db4o assembly assert its elevated CAS rights immediately before it does anything needing permission; doing this makes the system forego walking up the stack testing CAS permissions.

Okay, so with some sprinkling of CAS assertions throughout the db4o code, this problem could presumably be solved. But it sounds like there's one more problem: ultimately the process identity of the web app would still not have the rights to do the dangerous functionality. As the article puts it:

"We can have our GAC assemblies assert permissions until the cows come home, but without the blessing of the process identity under which code is running there won’t be access to restricted resources and functionality. To overcome the limitations of application code running as the machine account, a higher privilege account can be impersonated for the duration necessary to access restricted resources and functionality."

Well. Getting the credentials of an elevated account to impersonate on a hosted machine is just about as likely as the host granting your web app FullTrust in the first place!

You probably want to keep the server open for all instances and you only want to open and close clients for processing actions. Actually even better, you may want to have a client associated to a thread, so you don't go through opening and closing clients for processing multiple actions for a thread.

Of course you are right. I thought about adding the http module that is part of the db4o starter kit but desided not to do that. This article was intended to be a beginner tutorial. Adding a more complex client-server model will make this article more difficult to understand. I will have a look at it later this week. I will then probably add a second web project for this.

I just uploaded a modified version where the Server and Client container instance are maintained at the application level. All selects now directly use this Client container instead of using the WithContainer construction.