Did you know? DZone has great portals for Python, Cloud, NoSQL, and HTML5!

Visual Studio

  • submit to reddit

Daily Dose - Seam 3 Goes Beta

JBoss' grand, all-encompassing Java development framework is nearly finished.  This weekend, the 3.0 version reached Beta 1 status.  It is the first release to have all of the Seam modules (Drools, Errai, Servlet, JBoss ESB, etc.) bundled into a single...

0 replies - 27551 views - 01/31/11 by Mitchell Pronsc... in Daily Dose

ADO.NET Entity Framework

Object-Relational Mapping and Data Access

By Dane Morgridge

9,704 Downloads · Refcard 131 of 151 (see them all)

Download
FREE PDF


The Essential ADO.NET Entity Framework Cheat Sheet

ADO.NET Entity Framework is a powerful object-relational mapping tool (ORM) that exists inside Microsoft Visual Studio 2010. This DZone Refcard will give you a major advantage when developing the data architecture for your .NET applications. It starts with the basics by showing you how to create a new Data Model. Once you have finished creating the Data Model you’ll learn how to insert, query, update, and delete entities. You’ll understand how to manipulate entities and implement lazy loading, eager loading and explicit loading. The card finishes with a section on POCO support with Entity Framework 4.0.
HTML Preview
ADO.NET Entity Framework Object-Relational Mapping and Data Access

ADO.NET Entity Framework: Object-Relational Mapping and Data Access

By Dane Morgridge

ABOUT THE ADO.NET ENTITY FRAMEWORK

The ADO.NET Entity Framework (EF) is a powerful objectrelational mapping (ORM) tool that exists inside Microsoft Visual Studio 2010. Many new features were introduced with the current release of ADO.NET EF 4.0, which provides superior functionality when compared with EF 1.0. The EF version number was changed to match the version of the .NET framework, and EF 1.0 is frequently referred to as version 3.5 because of this.

THE OBJECTCONTEXT

There are several classes that you will work with when using the EF. The most important of these objects is the all-central ObjectContext, which is the gate keeper for the classes responsible for all the change tracking as well as all database access. EF keeps track of each entity that is attached to it. ObjectContext can become quite large in terms of memory and resource as more entities are attached to it, which may present some problems when it comes time to persist changes to the database.

For example, when a SaveChanges call is made, each object currently being tracked is examined to determine necessary actions. Logically, this process will take more time and will require more resources as the objects that are being tracked increase. The SaveChanges call is also wrapped in an atomic transaction and will roll back if there are any errors from the database.

A general rule of thumb is to use a new ObjectContext (such as a single web form, MVC controller, or any client-side view) with each logical set of operations. Keeping a single ObjectContext limited to a smaller set of operations will decrease the number of objects tracked and, as a result, maintain performance.

THE DATA MODEL

Before you begin working with data, you must create a new model either by working with an existing database or by creating an empty model.

Working with an existing database

First, add a new “ADO.NET Entity Data Model”. In this example, the model is called “ContactModel. edmx” since the data is contact type data. The Choose Model Contents dialog box opens.

database

To work with an existing database, select Generate from database. Click Next. The Choose Your Data Connection dialog box opens.

dialog

The drop-down list contains all databases that you currently have configured inside the Visual Studio Server Explorer. If the database you want is not an option in the list, click the New Connection button to add a new database.

You will see more information in the Entity connection string field than what you would see in a normal ADO.NET connection string. The entity connection string is made up of three parts:

  1. Metadata: points to the actual model. The model file in your solution is ContactModel.edmx, which you don’t see listed. There are three files:
    • ContactModel.csdl: the conceptual model (or what you see in the entity design surface). This represents the classes you work with.
    • ContactModel.ssdl: the storage model (or the physical database model).
    • ContactModel.msl: the mapping between the csdl and ssdl files.

    The .emdx file is an XML file that contains all of the data for the three files. When your project is compiled, these three files are generated and embedded.

  2. Provider: points to the actual ADO.NET provider.
  3. Provider Connection String: the normal ADO.NET connection string that is specific to the provider.

The last option in the Choose your Data Connection dialog box is the “Save entity connection settings in App.Config as” field, which saves the connection string. Make sure to check this checkbox so that you do not have to build the connection string by hand. The name you give to the connection string is also what your ObjectContext class name will ultimately be named.

After you have saved your connection string, click Next. The Choose your Database Objects dialog box opens.

Database Dialog Box

Select the tables, views, and stored procedures that you want to use. In this example, we will select the Addresses and People tables.

The two checkboxes at the bottom of the dialog box allow you to pluralize or singularize your object names or to include foreign keys in the model. Pluralizing and singularizing will help the model make more sense from a code standpoint when working with entities. In this example, the database table is named “People”. You would want the entity name to be “Person” in the singular form. If you were dealing with multiple people, you would want this to be “People”.

The information in the Model Namespace field at the bottom of the dialog box is hidden inside generated code. Since you don’t work with it directly, it is not recommended that you change this.

Click Finish. You will be taken to the model design surface that will show you the model you have created.

2 Boxes

The Person and Address entities are pulled from the database. Notice that they are singular versions of the table names. The lines connecting the entities denote a one-to-many relationship between Person and Address. In other words, one Person can have multiple Addresses and one Address can only have one Person. Navigation Properties is also pluralized and singularized based on the relationship. Navigation properties are how you navigate between entities. You can use the Addresses property of a person to access that person’s address data and, in the same way, you can use the Person property off of an address to access the associated person.

Anything that you want to modify on the model can be done through the properties window in Visual Studio.

Updating the model

When you make changes to your database, you can refresh the model by right clicking on the model design surface and selecting Update Model from Database. A dialog box similar to the Choose Your Database Objects dialog box opens. Any objects that are currently part of the model will be refreshed, and you can add any objects that are currently not part of the model.

Using Model First

To get started, add a new ADO.NET Data Model to your project by selecting Empty model in the Choose Model Contents dialog box and click Finish.

Contents dialog

You will be taken to an empty design surface. To add an entity, right click on the design surface and select Add -> Entity. The Add Entity dialog box opens.

Dialog Box Opens

In this example, the Entity name is set to “Person”, which was pluralized to “People” in the Entity Set field. The Entity Name is the name you will use for the actual entity. The Entity Set will be the name of the plural collection as well as your database table name. If you don’t want it to be pluralized, you can change it here. However, you may encounter problems if you try to make changes to the database directly and then update it. In general, it is recommended that you stick with one design method for your database. Either use Model First consistently throughout or make your database changes externally and update the model. This will make maintaining the model consistency easier.

It is up to your design if you want to change the information in the Property name field to be more descriptive. If you do change it, make a note that you may need to tweak your association names down the road.

Click OK to return to the model design surface with your new entity:

entity 1

To add more items to the entity, right click on the entity and select Add -> Scalar Property. You can also use the Insert or Enter keys on your keyboard to add a new property. In this example, a FirstName property was added:

entity 2

Click FirstName to view the Properties window: window 1

The following table includes descriptions of some of the key properties:

Property Name Description
Fixed Length Set to False by default and will result in a varchar or an nvarchar. If you want a char or nchar, set this to True.
Max Length Set to MAX by default. As a string type, either a varchar(MAX)or an nvarchar(MAX) will be created. Set this to a number to set the max character length.
StoreGeneratedPattern Set to None by default. When using keys, you can set this to determine if you want this key to be an identity or a computed value.
Type Set to String by default. Sets the datatype of the property and supports almost all SQL Server datatypes.
Unicode Set to True by default, giving you the Unicode versions of the datatypes. Determines whether you will get an nvarchar or a varchar.

Creating Associations

Once you flesh out your entities, you need a way to associate them. In SQL Server, you would simply add foreign keys to the tables. In EF, you need to create associations. To do this, right click the entity with the primary key and select Add -> Association. The Add Association dialog box opens.

window 2

In the Add Association dialog box, you can set the two endpoints for both sides of the association as well as the association type. In this example, a one-to-many association between Person and Address is being set up. Using the Multiplicity drop-down menus, select different association types. Checking the Navigation Property checkboxes allows you to set navigation properties to be used when navigating the associations. By default, these are set to pluralized or not based on the type of association. You can rename these to more descriptive names if you want.

Check the Add foreign key properties to the ‘Address’ Entity checkbox to add foreign key properties to the non-primary key endpoint. In this example, checking this box would add a PersonId to the address table. It is important to note that the key that gets added will be a concatenation of the primary key entity name and the primary key itself. If the key was left as simply Id, the foreign key field would be PersonId. If I set the primary key field to be PersonId, the foreign key field would be PersonPersonId.

INSERTING ENTITIES

Now that you have a model, you can start putting some data in it. To create a new person, you must create a new person object. Once you have created the new object, simply add it to the context and then call SaveChanges on the context. In this example, there is address data to add to the person object before saving.


using(EFRefCardEntities context = new EFRefCardEntities())
{
	Person person = new Person();
	person.FirstName = “Dane”;
	person.LastName = “Morgridge”;
	person.DateOfBirth = DateTime.Now;
	
	Address address = new Address();
	address.Street = “123 Somewhere”;
	address.City = “Philadelphia”;
	address.State = “PA”;
	address.Zip = “12345”;
	
	person.Addresses.Add(address);
	
	context.People.AddObject(person);
	context.SaveChanges();
}

Adding the address object to the Addresses collection on the person object will also add the address to the context. When SaveChanges is called, the context will inspect any objects it is tracking and decide what steps to take and what order to do them in. In this example, there are two entities that are new and they have to be submitted in the proper order. Since Address is dependent on Person because of a foreign key constraint, the person must be added first. The EF ensures that they are added in the proper order.

The SaveChanges call is automatically wrapped in a transaction. As such, if anything fails on insert, the whole thing will roll back and an exception will be thrown. To generate your database, right click on the design surface and select Generate Database From Model.

A dialog box opens where you can select the database location just as you did when adding from an existing database. Click Next to view a preview of the SQL file that will be generated. Click Finish to generate the file.

It is important to note that the generated SQL file contains create statements only and does not look at the current state of your database to determine what updates need to be done. It is recommended that you run this file against a compare database and then use a SQL compare tool to sync the schemas. You can use the same database name, but add ” _Compare” to the end. Doing this ensures that you don’t inadvertently erase your test data.

QUERYING ENTITIES

Once you have data in the database, you can get the data back out. The EF can be queried using either LINQ or Entity SQL. You will need to have some familiarity with LINQ and the various available options to take advantage of what the EF has to offer. Entity SQL is very similar to TSQL, but it is executed against the model rather than the database. There are resources online and in print to help you should you need it.

Below is a basic LINQ query that will give you all of the person data.


using (EFRefCardEntities context = new EFRefCardEntities())
{
	var people = from p in context.People
	select p;
	
	foreach (var person in people)
	{
	
	  foreach (var address in person.Addresses)
	  {
	  
	}
  }
}

After you query the data, do a foreach through the people collection and then through the addresses for each person to show the navigation.

The actual query can be written two ways:


var people = from p in context.People
		select p;

or


var people = context.People;

The second option is much cleaner; and unless you need to do joins or any other special LINQ operations, the second method is recommended.

Lazy Loading, Eager Loading, and Explicit Loading

EF 4.0 supports lazy loading by default. Be aware that lazy loading will create more database connections than you may expect. For example, if there were two people in the database, the above code would call the database three times—once for the initial call and once for each person when the Addresses collection gets checked. If a navigation property hasn’t been filled when it is accessed, the EF will make a call out to the database to see if there is any data.

You can turn lazy loading off in the properties window for global options. You can also turn off lazy loading on a call-by-call basis by setting the ContextOptions.LazyLoadingEnabled property to false.

If performance is not an issue, you can leave lazy loading enabled since it will make development quicker by not having to request the additional data. If you need to control the database hits, you have a couple of options. If you plan on serializing the classes, you will want to turn lazy loading off.

To get at association data without using lazy loading, you can use eager loading and explicit loading. With eager loading, you can load the data up front in one trip to the database. With explicit loading, you can load associated data on demand.

To use eager loading, you can use the Include method:


var people = context.People.Include(“Addresses”);

This will cause one round trip to the database and get all person and all address data at once. This will use more bandwidth upfront, but it will be limited to one round trip.

Explicit loading is similar to lazy loading, but you can control when the navigation properties get loaded:


var people = context.People;

foreach (var person in people)
{
	person.Addresses.Load();
	foreach (var address in person.Addresses)
	{

	}
}

Calling Load on the navigation properties will make additional trips to the database to fill the properties, but you can control when it happens.

You can use a mix of all three methods depending on the requirements of the application.

UPDATING ENTITIES

Updating entities is a very simple process. Once you query an entity or a collection of entities, you can make any changes you wish. Calling SaveChanges on the ObjectContext will persist any updates on any entity that it is tracking.


using(EFRefCardEntities context = new EFRefCardEntities())
{
	var people = context.People.Include(“Addresses”);
	
	foreach (var person in people)
	{
	  person.DateOfBirth = DateTime.Now.AddYears(-30);
	}

context.SaveChanges();
}

The above code will update the DateOfBirth to the current time minus 30 years, and the SaveChanges call will persist those changes down to the database.

Any change to any entity that is being tracked by the ObjectContext will be evaluated on the SaveChanges call. This is part of the reason that you should use a single ObjectContext for a small logical set of operations. The more objects being evaluated, the longer this process will take and the more resources it will consume.

DELETING ENTITIES

To delete an entity, call DeleteObject on the ObjectSet:


context.People.DeleteObject(person);
context.SaveChanges();

To delete an object, it must be loaded into the ObjectContext. The SaveChanges call will issue the delete statement to the database. Because of the requirement of an object to be tracked by the ObjectContext, you will likely want to use a stored procedure if you are going to delete a lot of objects. While this can be done using the EF, it will be much simpler in a stored procedure.

Be careful when deleting an entity that is part of an IEnumerable or Ienumberable<T> collection. This will throw an exception as you are modifying the collection while it is being enumerated. Pushing your collection to an array will easily get around this.

POCO SUPPORT

Basic POCO Support with Entity Framework 4.0

The default classes that come with the EF are tied to the framework itself, and the entities themselves inherit System.Data.Objects.DataClasses.EntityObject. Luckily, EF 4.0 provides Plain Old Clr Object (POCO) support. This allows you to build data layers following the persistence ignorance concept. While this is important for many reasons, one of the most significant reasons is that it allows greater testability with your code.

In a nutshell, POCO support allows you to use the POCO objects with the ObjectContext without having to make great changes in your code. In most cases, you can switch from the normal entity generation scheme to POCO with no code changes. However, a few things are missing—specifically, explicit loading. The Load method doesn’t exist on the POCO collections, but you can use ObjectContext.LoadProperty to achieve the same result.

Lazy loading works through the use of proxies. By default, with POCO, there is a proxy object that gets generated for each POCO object that is used when interacting with the ObjectContext. The proxy object sits between the POCO object and the ObjectContext. To the ObjectContext, the POCO object is a real EF object.

It is important to note that this proxy will get in the way of serialization. Therefore, it is important to turn the proxy off. Simply turning off lazy loading will not do the trick. You can turn it off using the ContextOptions.ProxyCreationEnabled property and setting it to false.

Using POCO Support

You have a couple of options if you want to use POCO with EF 4.0. You can hand code the objects, or you can use a Text Template Transformation Toolkit (T4) to generate them for you from your model.

Using the T4 templates is the easiest method. You can download a POCO template from the Visual Studio Gallery, or the Visual Studio Extension Manager. If you search on visualstudogallery.com for the “ADO.NET POCO Entity

Generator”, you will find four (at the time of this writing): two for C# and two for Visual Basic. Each template has a website version if you use the file system-based website feature of Visual Studio. Use the other templates if you are using actual project files. No matter which you use, the process is the same. Open your .edmx file, right click on the design surface, and select “Add Code Generation Item”.

Select either the ADO.NET POCO Entity Generator and name the file. It is recommended that you name it the same as the .edmx file to keep it with the model in the project.

window 3

Click Add. Your model is now converted to use POCO instead of the standard EF objects.

Another reason to use the T4 templates is that if you make a change to the model, it will be reflected automatically by the POCO template. If you build the files by hand, you will have to update them yourself, which could be a time-consuming process if you have a large evolving system.

Now that your project is converted to POCO, you shouldn’t have to make any changes to your project unless you are using anything that requires the full EF objects.

You can now begin to build your persistence ignorant data layer and keep good clean separation of concerns in your application. This will also make it easier for you to implement a repository pattern for your data access layer.

I maintain an open source project at CodePlex to aid you in building a data access layer using POCO with Entity Framework 4. It is an additional T4 template to provide the groundwork for you to build your data layer on. It can be downloaded at http://efrepository.codeplex.com.

Code First Development

In addition to using T4 templates, you can use the newest features of Entity Framework: Code First.

Code First development gives you the ability to define everything in code without having to use a model in an .edmx file. You can still use the same ObjectContext and POCO class concepts, but the model is inferred at runtime. Code First is not baked into EF 4.0 and Visual Studio 2010; rather, it is an out-of-band release targeted for the first quarter of 2011. The final release will be announced on the ADO.NET team blog: http://blogs.msdn.com/b/adonet/.

CONCLUSION & RESOURCES

ADO.NET Entity Framework 4.0 is a powerful Object Relational Mapping tool and is the Microsoft data access strategy moving forward. If you are working with data in your applications, it is well worth taking a solid look.

To keep up with everything going on with the EF, watch the ADO. NET Team Blog at: http://blogs.msdn.com/b/adonet/

The MSDN forums also have valuable information:
http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/threads/

About The Authors

Dane Morgridge

Dane Morgridge

Dane Morgridge has been a developer for 9+ years and has worked with .Net & C# since the first public beta. His current passions are Entity Framework, WPF, WCF, Silverlight and LINQ and is currently a Microsoft MVP for Data Platform Development. He works mostly with C#, but is also a big fan of whatever new technology he happens to come across. In addition to software development, he is the host of the Community Megaphone Podcast (http://communitymegaphonepodcast.com) and also enjoys dabbling in graphic design, video special effects and hockey. When not with his family he is usually learning some new technology or working on some side projects. He can be reached through is blog http://geekswithblogs.net/danemorgridge or on Twitter @danemorgridge.

Recommended Book

Programming Entity Framework

Programming Entity Framework is a thorough introduction to Microsoft’s new core framework for modeling and interacting with data in .NET applications. This highly-acclaimed book not only gives experienced developers a hands-on tour of the Entity Framework and explains its use in a variety of applications, it also provides a deep understanding of its architecture and APIs. Although this book is based on the first version of Entity Framework, it will continue to be extremely valuable as you shift to the Entity Framework version in .NET Framework 4.0 and Visual Studio 2010. From the Entity Data Model (EDM) and Object Services to EntityClient and the Metadata Workspace, this book covers it all.

Share this Refcard with
your friends & followers...

DZone greatly appreciates your support.


Your download should begin immediately.
If it doesn't, click here.

Daily Dose - Still Waiting for the Chrome Web Store

Whatever happened to that October release planned for the Chrome web app store?  It hit a snag apparently, but now there are signs that Google may have it ready in time for a December release, just ahead of Chrome OS.  TechCrunch made the observation that...

0 replies - 15130 views - 12/02/10 by Mitchell Pronsc... in Daily Dose

Daily Dose - WebSockets Updated and RLZ Open Sourced

The Chromium blog announced today the updating of the WebSocket specification and the open sourcing of the RLZ library for Chrome.  The RLZ library has helped Google accurately track and measure the success of marketing promotions and distribution...

0 replies - 11818 views - 06/04/10 by Mitchell Pronsc... in Daily Dose

Optimize Your db4o Techniques in 6 Pages!

Whether you're advanced in db40 and want increased efficiency or you want to learn basic operations, you'll find what you're looking for in this free, 6-page DZone Refcard. Click here to download the PDF. 

0 replies - 6252 views - 05/11/09 by Lyndsey Clevesy in Announcements

Getting Started with db4o

Persisting .NET Object Data

By Stefan Edlich and Eric Falsken

7,117 Downloads · Refcard 53 of 151 (see them all)

Download
FREE PDF


The Essential db4o Cheat Sheet

db4o is an open source object database that allows you to quickly store and retrieve your .NET application data. It was introduced in 2000 with a focus on performance, compactness, zero administration, simplicity, and (most important of all) native object persistence. Developers were finally able to combine the power of a full database engine with plain undecorated objects. This DZone Refcard walks you through db4os basic operations, its various query types and techniques for optimal data access performance.
HTML Preview
Getting Started with db4o: Persisting .NET Object Data

Getting Started with db4o: Persisting .NET Object Data

By Stefan Edlich and Eric Falsken

About db4o and object databases

db4o is the open source object database that enables developers to store and retrieve any application object with one line of code.

The Object Database (ODB) arrived in the software industry with the advent of object oriented languages. The ODB is primarily used as an application specific database in either extreme scale systems or embedded systems where typical DBA activities are automated.

If you are familiar with object-relational mapping (ORM) tools, then you are already an object database expert because many of the APIs and query languages are comparable. You will even find db4o both familiar and yet simpler to use as ORM tools as there is no requirement for XML mapping, annotations or IDs.

db4o was introduced in 2000 with a focus on performance, compactness, zero administration, simplicity and (the most important of all) native object persistence. Developers were finally able to combine the power of a full database engine with plain undecorated objects.

Getting Started

Note

The current version of db4o at the time of this writing is version 7.8.

db4o comes distributed as a few native .NET assemblies. Only Db4objects.Db4o.dll is required for basic db4o operation. Use the Visual Studio "Add Reference" command to add the necessary assemblies. Then use the Solution Explorer to locate the new reference, right-click and open the properties window to ensure that "Copy Local" is set to true for each db4o assembly. This will copy the necessary db4o libraries to your application's bin folder automatically when compiling your project.

Note

Running db4o from the GAC is not supported.

Required Environment

Developing with db4o requires only the .NET SDK version 2.0 or better. (3.5 suggested)

Visual Studio 2008 or better is suggested for the best experience, but any .NET IDE will do. Microsoft Visual Studio 2008 Express editions are available for free download from Microsoft.

Running db4o requires only the .NET Framework to be installed. Some hosting environments, such as shared website hosting providers, do not allow code to run with full trust. When the environment is configured to run with reduced trust, all basic database operations require at least ReflectPermission (MemberAccess and ReflectionEmit) for the classes and types being persisted.

Required Libraries per Database Feature

Depending on the features your application requires, reference and distribute these assemblies when you distribute your application:

(Required for all Installations) Db4objects.Db4o.dll
Client-Server Db4objects.Db4o.CS.dll
LINQ Db4objects.Db4o.Linq.dll
Native Queries Db4objects.Db4o.NativeQueries.dll
Transparent Activation Db4objects.Db4o.Instrumentation.dll

Note

Run-time Optimization of LINQ, NQ, and TA requires the Mono.Cecil.dll and Cecil.FlowAnalysis.dll assemblies. Optimization can also be done at buildtime using Db4oTool.exe. (see db4o Reference Documentation for usage)

ObjectManager for Debugging

Included in the db4o distribution you'll find the installer for ObjectManager Enterprise (OME) which, once installed, will integrate into your Visual Studio Environment and allow you to open and inspect, query, and edit (value types only ) object instances stored in your database file.

Basic Database Operations

A Complete Example


IObjectContainer db =
Db4oFactory.OpenFile([filename]), ([config]);
try{
	// Store a few Person objects
	db.Store(new Person("Petra"));
	db.Store(new Person("Gallad"));
	// Retrieve the Person
	var results = db.Query<Person>(x => x.Name == "Petra");
	Person p = result.First();
	// Update the Person
	p.Name = "Peter";
	db.Store(p);
	// Delete the person
	db.Delete(p);
	// Don't forget to commit!
	db.Commit();
}
catch{
	db.Rollback();
}
finally{
	// Close the db cleanly
	db.Close();
}

Create or Open a Database File


IObjectContainer container = Db4oFactory.OpenFile([filename]);

While a db4o database file is open, it is locked and cannot be accessed by another application at the same time.

Hot Tip

It's important to know that db4o works best if you open the database file when you start working with data, and close the db when all posible operations are completed.

In traditional relational databases, it's common to open a connection, get/update data, close connection, and then perform your operation. Because db4o uses the native object (or referential) identity, it's better to open the database or connection when your application begins, do all your work, then close the database when your program is shutting down. You'll see why when we get to updating an object with our changes.

Starting a db4o Server

By default, the db4o server runs in-process within your application. To start a db4o server, place this code into your application:


IObjectServer server =
	Db4oFactory.OpenServer([filename], [port]);
server.GrantAccess([user], [password]);

To shut down the server:


server.Close();

The port parameter specifies the network port number. Acceptable values are any number above 1024 which are not already in use.

Hot Tip

Using Port 0 for your server creates an "Embedded" server which will not be available remotely. This is useful for multi-threaded operations or web-server style environments where you wish to handle parallel operations in a single process.

The GrantAccess() method must be called for each username/ password combination. It is not required at all for embedded servers.

Connecting to a db4o Server

After starting a db4o server instance, use either of the commands below to open a db4o client connection:


// In-Process mode (embedded server)
IObjectContainer client = server.OpenClient();
// Client/Server mode (remote server)
IObjectContainer client =
	Db4oFactory.OpenClient([serverAddress], [port], [user],
[password]);

To close the client connection to the server:


client.Close();

Storing an Object


db.Store(anObject);
db.Commit()

Just one line of code is all it takes. All of an object's properties and child objects will be stored.

Updating an Object


db.Store(anObject);
db.Commit();

Looks familiar? You can use the same Store(object) command to update an object. One difference, however, is that db4o will (for performance reasons) not automatically check child objects for changes.

Hot Tip

By default, db4o will NOT descend into child objects. Store() must be called for each modified object unless you change the default UpdateDepth (see the UpdateDepth parameter in the Configuration section, below) or configure cascading update for the persisted class.

There is one more thing to be aware of: db4o uses an object's native identity (referential identity) to identify an object and map it to the stored instance of the object. This means that there is only ever 1 instance of an object in memory for each stored instance of the object. (per ObjectContainer or connection) This is important when dealing with class instances that may come from ASP.NET ViewState, Web Services, Interop, or any other serialized source of object data.

Hot Tip

Avoid Confusion: Always make sure that the object you are trying to update or delete was previously stored or retrieved in the database. Calling Store() with 2 User objects both with an ID of "jack" will result in 2 separate instances. However, if you retrieve the user, and modify the first instance, then store it again, you will have only 1 updated instance in the database.

Deleting an Object


db.Delete(anObject);
db.Commit();

You didn't think it was any harder than that, did you? Like updates, db4o will not automatically delete child objects unless you configure cascading deletes for your object will remain in memory until the objects are refreshed or garbage collected.

Database Transactions

Whenever you start making changes to your database (using the Store() and Delete() commands) you are automatically in an open transaction. To close the transaction, use the Commit() method:


db.Commit();

Your changes will be permanently saved. If you wish to cancel or roll back any uncommitted changes, use the Rollback() method:


db.Rollback();

Hot Tip

Useful for beginners: Rollback only undoes uncommitted changes in the database. It will not undo changes to any currently loaded objects. So, when you call Rollback() you will not see any difference to your objects. If concerned about consistency, use the Refresh(object) command to cause the objects to be refreshed with stored database values.

Closing a database cleanly will automatically call Commit() for you, so any uncommitted transactions are committed automatically.

If the database is not closed cleanly, or if the application crashes at any time and uncommitted (or incomplete) transactions are discarded.

Queries

Query by Example (QBE)

QBE lets you pass db4o an example object. db4o will search and return all objects which match the object you specify. To do this, db4o will reflect all of the properties of the object and assemble all non-default property values into a single query expression.

Hot Tip

Useful for beginners: QBE queries are not able to use advanced boolean constraints (AND, OR, NOT) and cannot constrain on default values (zero, empty strings, null). QBE queries also cannot query for value ranges (greater than, less than) or stringbased expressions (contains, starts with). So QBE can be used only to retrieve exact-value matches.

Here's an example QBE query that will contain all Customer objects with "Lee" as their Surname:


Customer proto = new Customer ()
	{Surname = "Lee"};
IObjectSet result = db.Get(proto);

Native Queries (NQ)

Like any query language, Native Queries are capable of expressing complex parameterized constraints, however NQ also have the benefit of being 100% compiler checked and can be refactored using common code refactoring tools. NQ can do all this because they are expressed as native .NET code rather than as strings (like SQL statements)

Hot Tip

Developers are encouraged to use the Native Query interfaces when working with db4o.


// NQ Lambda Expression (.NET 3.5 syntax)
var result = db.Query<type>(
	o => [boolean expression]);
// NQ Anonymous Method (.NET 2.0 syntax)
IList<type> result = db.Query<type>(
	delegate(type o){
		return [boolean expression];
	});

Native Query Examples


// Query all instances of a type.
IList<User> result = db.Query<User>();
// Query Users by Name.
IList<User> result = db.Query<User>(u => u.Name == "Joe");
// Query Users with at least 10 orders
IList<User> result = db.Query<user>(u => u.Orders.Count >= 10);
// Complex Query
IList<User> result = db.Query<User>(
	u => u.Name.StartsWith("Bob")
		&& (u.Country == "Canada" | |
			u.Country == "USA" );

Sorting Native Queries

Native Query results can be sorted by using an IComparer or a comparison delegate. Here is the query syntax:


IList<User> result = db.Query<User>([predicate],[comparer]);

And here's an example:


// All users with "Smith" in their name
// sorted by name
IList<User> result = db.Query<User>(
	u => u.Name.Contains("Smith"),
	(u1, u2) => u1.Name.CompareTo(u2.Name)
	);

LINQ Queries

LINQ was introduced by Microsoft .NET Framework version 3.5 (also called C# 3.0) in November 2007. To enable the use of LINQ queries, you'll need to add a reference to the Db4objects.Db4o.LINQ.dll assembly and import the Db4objects.Db4o.LINQ namespace. (with a using statement)

A full description of LINQ syntax will not fit in this document. You can find the URL to Microsoft's LINQ reference in the Resources section at the end.

LINQ Queries have all the benefit of compiler checking and automated code refactorability that Native Queries have, but are expressed in syntax more familiar to SQL developers. Here's one quick and easy example that gets all of the Customers with "Smith" in their name, and sorts the results by name.


var results =
	from Customer c in db
	where c.Name.Contains("Smith")
	orderby c.Name descending
	select c;

With LINQ queries you can:

  • Use ORDERBY to sort the results.
  • Use JOIN expressions to filter one dataset based on the contents of another.
  • Use Aggregate Expressions to get the sum, min, max, or average values and GROUPBY to group the results.
  • Use Anonymous Types to get back only the fields you wish.
  • Use an expression to process, filter, or format the data as it is returned from the database.
  • Use LINQ extensions like First(), Any(), All(), Single(), and ElementAt() to access or constrain the result set.

SODA Queries

SODA query expressions are a standard that was present in all but the earliest versions of db4o. Using combinations of SODA query and constraint keywords, you can build up what is called a query "graph". A graph is a network of objects which represent a segment of data.

Hot Tip

Before using SODA Queries, you must import the Db4objects.Db4o.Query Namespace. (with a using statement)

In this example, we're querying for all of the customers named "Smith".


IQuery query = db.Query();
query.Constrain(typeof(Customer));
query.Descend("Name").Constrain("Smith");
IObjectSet results = query.Execute();

And in this more complicated example, we'll use a compound constraint:


IQuery query = db.Query();
query.Constrain(typof(Customer));
IConstraint c1 = query
	.Descend("Name")
	.Constrain("Smith");
IConstraint c2 = query
	.Descend("Country")
	.Constrain("USA");
c1.And(c2);
IObjectSet results = query.Execute();

Notice how each of the calls to Constrain() will return an IConstraint? You can keep references to those constraints and then use constraint keywords like And(), Or(), and Not() to relate the constraints together, as we did at the end of that example.

Note that the Descend() method returns an IQuery too. So you could Descend() into an object, and then Execute() at a deeper level to return only the matching child objects, like in this example:


IQuery q1 = db.Query();
q1.Constrain(typeof(Customer)); // start with Customers
q1.Descend("Name")
	.Constrain("Smith");
IQuery q2 = q1.Descend("Orders"); // constrain Orders
// Order totals greater than $100.
q2.Descend("Total")
	.Constrain(100)
	.Greater();
// Since we want to return Orders, execute q2 instead of q1
IObjectSet results = q2.Execute();

SODA Query Interfaces
IQuery Provides the location for constraining or selecting.
IConstraint Constrains the query results with the current IQuery node
SODA Query Keywords
Descend Move from one node to another
OrderAscending Order the result ascending according to the current node.
OrderDescending Order the result descending according to the current node.
Execute Execute the query graph and return the objects at the current node.
SODA Constraint Keywords
And(IConstraint) Performs an AND (&&) comparison between 2 constraints.
Contains() For collection nodes, matches will contain the specified value. For string values, behaves as Like().
EndsWith(bool) For strings, matches will end with the supplied value. Optionally case sensitive.
Equal() Combine with Smaller and Greater to include the specified value. (e.g. >= or <=)
Greater() Matching values will be greater than or larger than the supplied value.
Identity() Matching values will be the same object instance as the supplied value. (referential equality).
Like() For strings, matching values will contain the supplied value anywhere within the match.
Not() Performs a negation comparison. Matching values will NOT equal the supplied value. Added to any other constraint keyword, this will reverse the result.
Or(IConstraint) Performs an OR (||) comparison between 2 constraints.
Smaller() Matching values will be smaller or less than the specified value.
StartsWith(bool) For strings, matches will start with the supplied value. Optionally case sensitive.

Query Performance

  • If you are experiencing poor NQ performance, then you probably forgot to either enable run-time optimization of Native Queries (by including a reference to the assemblies listed in the "Required Libraries" section) or running Db4oTool.exe on your compiled assembly.
  • You can change the Query EvaluationMode configuration to control how and when a query should be evaluated. (See below in the Configuration section)
  • You can index fields to aid query evaluation. Indexing fields causes db4o to store the values of the field in a separate index lookup table in the db. As a result, when evaluating the query, db4o does not have to seek through all of the object data to resolve the query results. (See the Class-Specific Configuration Options below)

Hot Tip

Indexing fields is a great way to increase query performance, but each index table is one more place where a field's value is stored. Too many indexed fields can cause poor insert performance. The application developer should tune the number of indexes with the desired Query and Insert performance.

Dealing with object activation

When dealing with objects that may have relations to other objects quite deep (think of the path of data from Customer to Order to OrderItem to Product with relations to Address objects for billing and shipping and then PO and payment transactions) it would be quite expensive to have to pull all of that data into memory from the DB if all you wanted was the Customer object. Modern object databases use the idea of activation to control the depth to which objects are instantiated and populated when retrieved from the database.

Hot Tip

The default ActivationDepth in db4o is 5. A properly tuned activation depth is the best way to optimize retrieval of data from a db4o database. (See ActivationDepth in the Configuration section for more ideas)

With an ActivationDepth of 5, objects will be populated up to 5 levels deep. Properties of the 5th descendant object will have their values left as default or null.

If you encounter an object that is not yet activated, you can pass it to db4o for manual (late) activation:


db.Activate([unactivatedObject], [depth]);

Objects can also be manually de-activated:


db.Deactivate([activatedObject], [depth]);

Fine-grained activation depth can be configured per class. (see ActivationDepth in the Configuration section below) Activation can also be managed transparently using Transparent Activation. (see below)

Configuration

For all but the simplest db4o use cases, you'll probably want to specify one or more configuration settings when opening your db4o database:


IConfiguration config =
Db4oFactory.NewConfiguration();
// Set configuration properties here
IObjectContainer db= Db4oFactory.OpenFile([config], [filename]);

The IConfiguration object must be passed in the call to open the db4o file, server, or client connection.

Query EvaluationMode

This property controls when and how much of a query is executed.


IConfiguration config =
	Db4oFactory.NewConfiguration();
config.Queries().EvaluationMode([mode]);

Query EvaluationMode Values
Immediate (Best when queries must be deterministic or execute as quickly as possible.) A list of object ID matches is generated completely when the query is executed and held in memory
Lazy (Best for limited resource environments.) An iterator is created against the best index found. Accessing the results will simply iterate through the index until no further matches are found. The result set can be influenced by subsequent transactions in the current context or by other clients causing possible concurrency errors. Almost no memory is needed to hold the result set. Accessing the Count or Length properties will cause full evaluation.
Snapshot (Best for servers and concurrent environments.) Same as Lazy, however the iterator is created against a snapshot of the index. This avoids possible concurrency issues of Lazy evaluation. Since the index snapshot is held in memory, the memory required varies greatly depending on what is being queried.
(All Values) Object data is activated as the users accesses each object in the result set. The currently stored state is used when activating field data, not the object state at the time of query execution. The above values affect only the state of field indexes and when the evaluation is performed. Regardless of when it is run, query constraints against non-indexed data are always performed on the currently stored object state.

Global UpdateDepth

We said earlier that when calling Store() to update an object graph, that db4o will not (by default) descend into child objects to detect changes to the graph. If you know that you'll be often changing child properties, or when changing a parent object often results in changes to child objects, then you may want to change the UpdateDepth.


IConfiguration config =
Db4oFactory.NewConfiguration();
config.UpdateDepth([depth]);

The default value 1 means db4o will not descend into child objects when updating stored object instances.

Setting the UpdateDepth to int.MaxValue will cause db4o to descend as deeply as possible to look for changes.

Setting the UpdateDepth to 0 will prevent any changes from being saved to the database.

Hot Tip

Setting the UpdateDepth too aggressively can cause poor db4o update performance. Higher values should be used to debug UpdateDepth-related issues only.

Global ActivationDepth

As explained in the section on Dealing with Object Activation, the ActivationDepth controls how much data is loaded when an object is retrieved from the database.


Iconfiguration config =
	Db4oFactory.NewConfiguration();
config.ActivationDepth([depth]);

The default value of 5 is a good balance for most applications, but developers should balance this against the weight of their classes and their access patterns.

Setting the ActivationDepth to int.MaxValue will cause all related objects to be instantiated as deeply as possible, restoring the entire object graph to memory.

Setting the ActivationDepth to 0 will cause nothing to be activated. The object returned will have none of its values loaded. You can then call the Activate(object, depth) method to manually activate the object as described above.

Hot Tip

Setting the ActivationDepth too aggressively can cause poor db4o query performance and high memory usage. Higher values should be used to debug ActivationDepth-related issues only.

Transparent Activation (TA)

The Db4oTool.exe tool found in the distribution bin folder can be used to instrument your classes to perform activation on-demand transparently as you navigate between object references. When accessing a child object, db4o can automatically activate the child object from the database if it is not yet already activated.

Transparent Activation must be enabled in the configuration when opening a db4o database and your compiled assembly must be instrumented by Db4oTool.exe as part of the build (as an MSBuild task) or post-build by running the tool manually.

Using Db4oTool.exe is not complicated, but is beyond the scope of this DZone Refcard. You can find complete instructions in the db4o reference documentation.

Cascading Operations, call backs and class-specific configuration

The global UpdateDepth and ActivationDepth configurations are good for general testing. But oftentimes, you will want to configure specific behaviors per-class-type, or per-field. (e.g. field indexing)


IConfiguration config =
Db4oFactory.NewConfiguration();
//Get the class-specific canfiguration
IObjectClass objectClassConfig =
	config.ObjectClass([typeName]);
//Get a field-specific configuration
IObjectField objectFieldConfig =
	objectClassConfig.ObjectField([fieldName]);
// Usually shortened to one line. e.g. config.
objectClass(typeof(Customer)).ObjectField ("Name").Indexed(true);

A full description of Class-specific and field-specific configuration settings can be found in the db4o reference documentation.

db4o also allows you to specify event handlers when an object is retrieved, activated, deleted, etc. These events can be handled globally using the EventRegistryFactory or individually within your classes using any of the IObjectCallbacks methods like objectOnActivate, objectOnDelete, and objectOnNew. These handlers are great for selectively activating, refreshing, or cascading db operations.

Lastly, setting the Transient attribute on your class's field members will prevent db4o from storing the values of those fields. When read from the DB, these members will always be left at their default (null) values, useful for remote connections, non- native data (interop) and temporary state.

db4o Resources

db4o Homepage http://www.db4o.com
db4o Community Forum http://developer.db4o.com
db4o Downloads http://download.db4o.com
db4o Bug Tracker http://tracker.db4o.com
db4o Source Code Repository https://source.db4o.com/db4o/
db4o Reference Documentation http://docs.db4o.com
db4o Community Projects http://projects.db4o.com
LINQ General Programming Guide http://msdn.microsoft.com/en-us/library/bb397912.aspx
Versant Corporation http://www.versant.com
Versant Corporation supports the db4o team and open source community. Versant is a public company (NASDAQ:VSNT) and develops the Versant Object Database technology for users of .NET, C++ and Java who require a database capable of supporting extreme scale systems.
Share this Refcard with
your friends & followers...

DZone greatly appreciates your support.


Your download should begin immediately.
If it doesn't, click here.