To access a database with a Genome-mapped business layer, the client needs to set up a DataDomain. The DataDomain tells Genome where to access the database (connection string) and how to access the database (schema assembly). It is used by the client to retrieve objects from the database as well as create new objects.
For the sake of simplicity, we will use a console application as client. The Genome Console Application Wizard helps you creating a Console Application client project that connects to a Genome mapped Business layer. To start the wizard, open the Add new project dialog in Visual Studio and select the Visual C# project types folder. From the Templates selection list select the Genome Console Application template:

Adding a project dialog for the client project
After adding a Console Application project to the solution, an assembly reference has to be set to the Genome runtime API (TechTalk.Genome.dll), the business layer project and the DataDomain schema project. The Genome Console Application Wizard has already set-up these references.
After these steps your solution should look like this:

Solution explorer view of the sample project
The generated program.cs file contains an initialisation method that initializes the DataDomain. It is important that all clients work only with a single DataDomain for the same database, however, since our console application client is single threaded, we don’t have to apply any specific synchronization pattern (for server components, like ASP.NET web applications, the DataDomain initialization has to be protected against parallel access, as shown below).
class Program
{
static private DataDomain InitializeDataDomain()
{
string dataDomainRole =
ConfigurationManager.AppSettings["DataDomainRole"];
DataDomainConfiguration ddConfig = new DataDomainConfiguration();
ddConfig.Schema = DataDomainSchema.LoadFrom("Business.Mapping");
ddConfig.ConnectionString = ConfigurationManager
.ConnectionStrings[dataDomainRole].ConnectionString;
ddConfig.Role = dataDomainRole;
ddConfig.LockServers.Add(typeof(object),
TechTalk.Genome.Locking.PersistentOptimisticLockServer.Value);
return new DataDomain(ddConfig);
}
static void Main(string[] args)
{
DataDomain dataDomain = InitializeDataDomain();
...
}
}
Client/Program.cs: DataDomain initialization
private static readonly object dataDomainSynchRoot = new object();
private static DataDomain dataDomain = null;
public static DataDomain DB
{
get
{
EnsureDataDomainInitialized();
return dataDomain;
}
}
public static void EnsureDataDomainInitialized()
{
if (dataDomain != null)
return;
lock (dataDomainSynchRoot)
{
if (dataDomain != null)
return;
string dataDomainRole = WebConfigurationManager.
AppSettings["DataDomainRole"];
DataDomainConfiguration ddConfig = new DataDomainConfiguration();
ddConfig.Schema = DataDomainSchema.LoadFrom("Business.Mapping");
ddConfig.ConnectionString = WebConfigurationManager
.ConnectionStrings[dataDomainRole].ConnectionString;
ddConfig.Role = dataDomainRole;
ddConfig.LockServers.Add(
typeof(object), PersistentOptimisticLockServer.Value);
ddConfig.Namespaces.Add("TechTalk.Genome");
ddConfig.Namespaces.Add("Zoo");
DataDomain newDataDomain = new DataDomain(ddConfig);
System.Threading.Thread.MemoryBarrier();
dataDomain = newDataDomain;
}
}
DataDomain initializationfor server components
Note that the schema assembly name is provided as a parameter to the DataDomain configuration, as the DataDomain is dynamically loaded during runtime.
The namespaces added to the DataDomainConfiguration are used to qualify OQL expressions during runtime. When not using LINQ integration, Genome OQL that is directly called from the client is parsed and compiled during runtime. Using directives of the client source code are therefore not effective anymore.
It is a recommended practice to manage the DataDomain in the client project utilising the business layer and not in the business layer directly. This brings the advantage that a business layer can be reused in different client implementations using different setups of one or multiple DataDomains and referencing arbitrary combinations of different business layers.
As a consequence of this practice static methods of the business layer will need the DataDomain to work on as a parameter in their calling signature:
public abstract class Customer : Persistent
{
public static Set<Customer> AllCustomers(DataDomain dd)
{
return dd.Extent<Customer>();
}
}
Static method of Customer receiving the DataDomain as a parameter
When accessing the DataDomain from an instance method the this.DataDomain property derived from Persistent can be used if the access should occur in the same DataDomain as this instance:
public abstract class Customer : Persistent
{
public Order CreateOrder()
{
Order order = this.DataDomain.New<Order>();
order.Customer = this;
return order;
}
}
Instance method of Customer using this.DataDomain
Genome has already compiled a database create script that can be used to set up the database schema.
As another option, there is also a Genome runtime API call that can set up the database schema for a given database. Please note that initialising the database schema deletes all data from the initialised database tables.
We add a static method to the Program class to initialise the database schema:
private static void InitializeDatabase(DataDomain dataDomain)
{
dataDomain.Schema.CreateDbSchema(dataDomain.ConnectionString);
}
Client/Program.cs – additional static method
Genome provides an event for monitoring queries sent to the database. For better understanding of Genome’s database communication behaviour, in this guide we subscribe to the event to output the database commands issued by Genome to the Console:
using System;
using TechTalk.Genome;
using TechTalk.Genome.Diagnostics;
namespace Client
{
class Program
{
static void Main(string[] args)
{
DataDomain dataDomain = InitializeDataDomain();
DiagnosticServices.BeforeDbCommandExecuted
+=
new DbCommandEventHandler(
DiagnosticServices_BeforeDbCommandExecuted);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
static void DiagnosticServices_BeforeDbCommandExecuted(
object sender, DbCommandEventArgs args)
{
Console.WriteLine(args.Command.CommandText);
Console.WriteLine();
}
}
}
Finally, we call the database initialisation method of the helper class to set up our database. To successfully run the sample, you need to set up the database specified by the connection string of the DataDomain:
static void Main(string[] args)
{
DataDomain dataDomain = InitializeDataDomain();
DiagnosticServices.BeforeDbCommandExecuted
+=
new DbCommandEventHandler(DiagnosticServices_BeforeDbCommandExecuted);
Console.WriteLine("Setting up database:");
InitializeDatabase(dataDomain);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Once everything is set up correctly, you will see the database create script issued by Genome printed out on the Console when running the sample:

Console output of Client running InitialiseDatabase()
The database is now initialised to work with the implemented schema assembly.
Of course Genome also allows you to map existing databases without initialising the schema of the database. Genome provides flexible mapping strategies for accessing legacy databases with pre-existing database structures. The Genome Database Reverse Engineering Wizard (DBREW) can be used to create business layer projects and DataDomain schema projects from existing database structures.
Additionally, Genome Database Management Extensions (GDMX) provides a set of tools integrated in Visual Studio that allow comparing the schema of a Genome mapped domain model with the schema of an existing database. The detected schema differences can be used for deriving transformations in both directions (change script generation or updating the domain model implementation and the mapping).
| Mapping persistent objects | Working with persistent objects (Create, Update, Delete) |