Getting Started
QuickStart Tutorial
In this tutorial you will map a set of persistable objects representing a solar system to a set of database tables. In honor of the "Hello World" tradition, you will then retrieve an instance of a planet via HelloWorld(). Next, you will create a new instance of a planet via CreateWorld().
To create and execute this application you'll need an installation of:
- .NET Framework 3.5 SDK
- SQL Server 2005 (This tutorial has not been tested against other databases)
Follow these steps to build the application:
- Create the database tables
- Create the persistable objects
- Create the persistable-object-to-table mapping
- Create the transaction facade class
- Create the client
- Deploy and execute the application
Create the database tables
The following set of tables store information about a solar system. You will map the persistable objects to them:
Use the SQL script at this link to create the database and load data into the above tables.
Create the persistable objects
For each database table you will create one persistable object. Let's take a look at the various parts of the Planet persistable object which will map to the PLANET table. The complete code for the Planet persistable object is here.
The class declaration
Every persistable object must derive from one of the three following abstract base classes: DomainObjects.Facade.Domain.EditableObject, DomainObjects.Facade.Domain.ImmutableObject, or DomainObjects.Facade.Domain.ReadOnlyObject.
Subtype EditableObject if your class requires editable semantics: i.e., instances of your class can be inserted into, updated to or deleted from the database. Subtype ImmutableObject if your class requires immutable semantics: i.e., instances of your class can be inserted into or deleted from the database but must never be edited and therefore updated to the database. And finally, subtype ReadOnlyObject if your class requires read-only semantics: i.e., instances of your class exist in the database (via, for example, an external load process or script) and must never be inserted into, updated to or deleted from the database.
public class Planet : DomainObjects.Facade.Domain.EditableObject
The mapped fields
For each column in the PLANET table you will define one private field member to hold the column value.
// maps to the foreign key column PLANET.STAR_ID private int _starId; // maps to the column PLANET.NAME private string _name; // maps to the column PLANET.ROTATIONAL_PERIOD private decimal _rotationalPeriod;
The .NET field type is based on the corresponding DbType. Below is table of DbType to .NET type mappings:
DbType | .NET Type |
---|---|
Binary | byte[] |
Boolean | bool |
Byte | byte |
Currency | decimal |
Decimal | decimal |
VarNumeric | decimal |
Date | DateTime |
DateTime | DateTime |
Time | DateTime |
Double | double |
Guid | Guid |
Int16 | short |
Int32 | int |
Int64 | long |
Object | object |
SByte | sbyte |
Single | float |
String | string |
StringFixedLength | string |
AnsiString | string |
AnsiStringFixedLength | string |
UInt16 | ushort |
UInt32 | uint |
UInt64 | ulong |
The next set of fields hold the values of objects that are related to this instance of Planet.
// RELATIONSHIP FIELDS // Holds the instance of Star referenced by the '_starId' foreign key field // // DomainObjects automatically populates this type of "reference" field via reflection: // it selects the row in the STAR table with the primary key of '_starId', // reconstructs the Star persistable object from the data reader, then sets the // value of this field to the reconstructed Star. private Reference<Planet, Star> _star; // Holds a strongly-typed collection of PlanetarySatellites based on the // PLANETARY_SATELLITE.PLANET_ID inverse foreign key. // // DomainObjects automatically populates this collection via reflection: it selects // the set of rows in the PLANETARY_SATELLITE table where PLANET_ID equals // this primary key, reconstructs the PlanetarySatellite persistable objects // from the result set, then sets the value of this field to the PlanetarySatellite // collection. private Collection<Planet, PlanetarySatellite> _mySatellites;
The constructors
Every persistable object defines two constructors:
- One that DomainObjects will use to re-construct an existing persistable object instance from the database. This is referred to as the 're-constructor'. It must be protected to ensure that only DomainObjects or subclasses invoke this constructor.
/// <summary> /// The protected constructor that DomainObjects uses to reconstruct an existing instance /// from the database. This constructor must declare all of the fields that /// are mapped to the database. You'll find one parameter for each column in /// the PLANET table. /// </summary> /// <param name="primaryKey"> /// The database primary key for this instance. The base class, DomainObjects.Facade.Domain.EditableObject, /// holds the primary key value, so the primary key must not be declared as /// a member field of this persistable object sub-class; rather, pass the primaryKey /// value to the base class constructor as shown below. /// </param> /// <param name="starId">The foreign key value of the related star.</param> /// <param name="name">The name of the planet</param> /// <param name="rotationalPeriod">The rotational period of the planet.</param> protected Planet(int primaryKey, int starId, string name, decimal rotationalPeriod) : base(primaryKey) { // For each mapped member field, set its value _starId = starId; _name = name; _rotationalPeriod = rotationalPeriod; // Call this method as the last statement in your reconstructor // to ensure that your reference and collection fields are initialized // to non-null values. InitializeRelationships(); }
-
And another that your application will use to construct a new persistable object instance. This is referred to as the 'factory constructor'.
Note that after constructing a new instance of a persistable object in your code, you must call the method 'Manage()' on the newly created
instance so that DomainObjects can manage the persistence-relevant state of the object. Your code must not hold a direct reference to an 'unmanaged' persistable object.
For example, creating a new instance of Planet would look something like:
return new Planet(newStar, planetName, rotationalPeriod).Manage<Planet>();
The factory constructor in Planet follows:
/// <summary> /// The 'factory' constructor that your application will use to construct a /// new instance of Planet. If this constructor is called in the context of /// a transaction, then DomainObjects will automatically insert it into the database. /// /// Generally, as a best practice, this constructor should declare all fields /// that are required, i.e., not null, in the database. That way, (1) the client /// calling this constructor knows which fields are required, and (2) you can /// be more assured that your persistable object instance is constructed such /// that it meets the minimum database constraints. /// /// DomainObjects automatically generates and then sets the primary key via reflection; /// therefore, there's no need to pass a primary key parameter. /// </summary> /// <param name="star"> /// The star around which this planet is orbiting /// </param> /// <param name="name"> /// The name of this planet /// </param> /// <param name="rotationalPeriod"> /// The amount of time required for the planet to perform one complete rotation /// about its own axis relative to the earth /// </param> public Planet(Star star, string name, decimal rotationalPeriod) { // Call this method as the first statement in your factory constructor // to ensure that your reference and collection fields are initialized // to non-null values. InitializeRelationships(); // For each parameter, set the value of the corresponding member field // Based on the instance of Star set here, DomainObjects automatically sets the // value of the '_starId' foreign key field _star.ReferencedObject = star; _name = name; _rotationalPeriod = rotationalPeriod; }
The properties
When an editable object is edited, there are two requirements:
- The object needs to be marked as 'edited'. This flag informs DomainObjects that the object needs to be updated to the database.
- If the edit occurs within the context of a transaction, then DomainObjects needs to associate the editable object with the current transaction, so that when the transaction commits, the object is updated or (rolled back) along with other objects edited or created in the context of the same transaction.
And in the setter below, the MutatorAttribute accomplishes these requirements.
/// <summary> /// You must apply the MutatorAttribute to this property to tell DomainObjects that /// this method modifies a field that has been mapped to a database column. /// When the transaction commits, DomainObjects will automatically update this persistable /// object to the PLANET database table. /// </summary> [Mutator] public string Name { get { return _name; } set { _name = value; } }
Create the persistable-object-to-table mapping
The persistable object-to-table mapping is defined in an xml file named 'Repository.xml'. This file contains metadata that defines which classes map to which tables and which fields map to which columns. DomainObjects uses this metadata to automatically generate INSERT, UPDATE, DELETE AND SELECT statements at runtime. The complete Repository.xml file for this tutorial is here . Read the inline comments for an explanation of the repository elements and attributes.
Create the transaction facade class
This is the class where, in a real-world application, you define those activities that need to occur within the context of a single transaction. The complete TransactionFacade class is here .
All creation, modification, and deletion of persistable objects occur within the context of an DomainObjects object transaction. This 'transactional' behavior is accomplished by a class-level custom attribute defined by DomainObjects:
[Transaction(TransactionOption.Required)] public sealed class TransactionFacade : Service<TransactionFacade>
See the code comments for further explanation.
Create the client
In this simple tutorial the client is a command line interface. Here is the usage:
helloworld [/hello /create <starName> <planetName> <rotationalPeriod>] Available command-line switches: /hello - Call the HelloWorld() method on the TransactionFacade class. /create - Create a new instance of Star with the given <starName> and, in the same transaction, create a new instance Planet associated with the new Star, that has the given <planetName> and <rotationalPeriod>.
Deploy and execute the application
The complete compiled code and resource files required to execute the application are here. Download the file and unzip it. Open the app.config file HelloWorld.exe.config and adjust the ConnectionString value to point to the database instance where you created the tutorial tables. Next, execute the command line switches as specified above. Have fun!