Getting Started with Db4o for Eiffel

by Ruihua Jin (modified: 2010 Jan 28)

This page provides a step-by-step guide, showing you how to use db4o databases within Eiffel for .NET applications.

Step 1: Download Db4o Assembly

Because of a critical bug in the db4o assemblies before version 7.1.26 (see the issue report "SODA queries return wrong query results for .NET interfaces"), it is important that you download a db4o version later than 7.1.26. Go to the db4o download center to download a db4o assembly.

Step 2: Download Reflector for .NET Assembly

In the implementation we take advantage of the Reflector for .NET assembly to examine Eiffel for .NET assemblies. You can download the assembly from the project homepage or from here.

Step 3: Download Source Code of Db4o for Eiffel

The project is implemented in Eiffel, and its source code is to be imported into your project. Here is the source code.

After downloading the zip file, please extract the file and then move the db4o_for_eiffel directory to the directory in which your project will reside, say db4o_example.

Step 4: Download EiffelStudio

It is common that Eiffel developers use EiffelStudio for developing Eiffel applications. You can download it from Eiffel Software or from Origo.

The recommended version for Windows XP is EiffelStudio, other versions of 6.1.x seem to have problems with debugging Eiffel for .NET applications.

Step 5: Create an EiffelStudio Project

Until now you should have a project directory called db4o_example with the db4o_for_eiffel directory in it.

Start EiffelStudio, and in the pop-up window select “Microsoft .NET application” and then click “Create” (see screenshot).
Click “Next >” to continue.
In the next window specify the project name as db4o_example and the project location as the location of your db4o_example directory. Click “Next >”.
Configure the .NET application as shown in the following screenshot:
Click “Finish” to generate and compile the project.

Step 6: Add Assemblies to Your Project

In the “Clusters” panel right-click “Assemblies” and in the context-menu select “Add Assembly ...”. Then in the “Add Assembly” window specify the location of the db4o assembly Db4objects.Db4o.dll to import it into the project.

Perform the same steps for the Reflector for .NET assembly Reflector.exe.

We also need the EiffelSoftware.Runtime assembly which can be selected in the “Add Assembly” window.

Step 7: Rename Classes to Avoid Name Clashes

In Eiffel for .NET every class must have a unique class name, so we have to rename some classes in Db4objects.Db4o.dll and Reflector.exe to avoid name clashes.

Click “Project” in the menu bar and select “Project settings ...”.
In the “Project Settings” window, navigate to the “db4objects.db4o” item in the left panel, then in the right panel click inside the cell next to “Renaming”. A window for editing renaming then pops up. Rename PREDICATE as DB4O_PREDICATE and rename FIELD_INFO as DB4O_FIELD_INFO.
Similar for the Reflector.exe assembly, rename ASSEMBLY as REFLECTOR_ASSEMBLY and rename ICONFIGURATION as REFLECTOR_ICONFIGURATION.

Step 8: Configure Db4o Databases for Eiffel Applications

Before storing and querying for Eiffel objects in db4o databases, we have to install POINTER_TRANSLATOR, which is done in the class EIFFEL_CONFIGURATION. Add the init method in the APPLICATION class and call it in the root procedure make. init is -- Set global database configuration. local eiffel_configuration: EIFFEL_CONFIGURATION do create eiffel_configuration.configure end make is -- Run application. do init --store --retrieve_qbe --retrieve_soda --retrieve_nq end

Step 9: Open and Close a Db4o Database

To open and close a db4o database, add the following features to class APPLICATION: feature -- Database control db: IOBJECT_CONTAINER database_file: STRING is "eiffel.db4o" open_database is -- Open `db' of `database_file'. do db := {DB_4O_FACTORY}.open_file(database_file) end close_database is -- Close `db'. local closed: BOOLEAN do closed := db.close end

Step 10: Store Eiffel Objects

Suppose we have a class PARALLELOGRAM with two attributes height1 and height2: class PARALLELOGRAM create make feature {NONE} -- Initialization make(h1: INTEGER; h2: INTEGER) is -- Initialize `height1' with `h1', -- `height2' with `h2'. require h1_positive: h1 > 0 h2_positive: h2 > 0 do height1 := h1 height2 := h2 end feature -- Access height1: INTEGER height2: INTEGER end

To store some PARALLELOGRAM objects we can write store is local closed: BOOLEAN do open_database {PARALLELOGRAM}.make(10, 30)) {PARALLELOGRAM}.make(20, 40)) close_database rescue if (db /= Void) then closed := db.close end end

Step 11: Retrieve Eiffel Objects

Db4o supplies three querying mechanisms: Query-By-Example, SODA Query API and Native Queries.


The following query uses Query-By-Example to retrieve all the PARALLELOGRAM objects whose height1 is equal to 10 and height2 is equal to 30: retrieve_qbe is local template: PARALLELOGRAM resultos: IOBJECT_SET closed: BOOLEAN do open_database create template.make(10, 30) resultos := db.query_by_example(template) printos(resultos) close_database rescue if (db /= Void) then closed := db.close end end
where printos outputs the PARALLELOGRAM objects in the query result to the console: printos(os: IOBJECT_SET) is local p: PARALLELOGRAM do from until not os.has_next loop p ?= if (p /= Void) then io.put_string("Parallelogram (" + p.height1.out + ", " + p.height2.out + ")") io.put_new_line end end end


To retrieve all PARALLELOGRAM objects whose height1 is greater than 10, you can write the following SODA query: retrieve_soda is local query: QUERY constraint, subconstraint: CONSTRAINT resultos: IOBJECT_SET closed: BOOLEAN do open_database create query.make_from_query(db.query) constraint := query.constrain({PARALLELOGRAM}) subconstraint := query.descend("height1", {PARALLELOGRAM}).constrain(10).greater resultos := query.execute printos(resultos) close_database rescue if (db /= Void) then closed := db.close end end

Native Queries

You should first define a class, say PARALLELOGRAM_PREDICATE, which inherits from DB4O_PREDICATE and implements the match method. The match method defines whether a candidate object is to be included in the query result or not. class PARALLELOGRAM_PREDICATE inherit DB4O_PREDICATE feature match(p: PARALLELOGRAM): BOOLEAN is do Result := p.height1 > 10 end end
Then you can pass a PARALLELOGRAM_PREDICATE instance to the IOBJECT_CONTAINER.query method to get the query result: retrieve_nq is local resultos: IOBJECT_SET closed: BOOLEAN do open_database resultos := db.query(create {PARALLELOGRAM_PREDICATE}) printos(resultos) close_database rescue if (db /= Void) then closed := db.close end end
Note that you have to run the finalized system to have Native Queries run without exceptions.

Step 12: What’s Next

To ensure a correct way of working with db4o databases within Eiffel applications, you may need to read the User's and Developer's Manual. Furthermore, we suggest you to download and try out this advanced example project when you are reading the manual.

For further advanced features of db4o, such as transaction and concurrency control, maintenance, client-server mode, etc. please visit the db4o documentation page.

  • Peter Gummer (9 years ago 5/4/2008)

    Looks Great

    One question, Ruihua. At step 7, instead of renaming individual classes, why didn't you just set assembly-wide prefixes DB4O_ and REFLECTOR_?

  • Ruihua Jin (9 years ago 7/4/2008)

    Prefixes for assemblies

    Thank you for your hint. I did not know about assembly-wide prefixes. I think it's a good alternative to resolve name clashes.