Dorm .Net (beta version)

What is Dorm?
It’s a light-weight ORM framework for .Net.
The idea is to provide all the productivity of an ORM tool without abstracting entirely the database, therefore giving the developer a finer degree of control on data manipulation.
Since it’s not meant to be a tool pluggable in any type of situations, it has a set of conventions for its use (listed below).


It DOES support:

- POCO / DDD approach;
- Fluent Mapping of entity meta-data;
- Relations One To Many and Many To Many;
- Component mapping (One To One);
- Saving and Deleting with Cascade for related objects;
- Object Oriented Querying with several options;
- Query generation at debug time;
- Eager Load and Paging helpers;
- Generation of Database scripts and Integration Tests;
- Implicit transactions;
- SQL Server 2005 or higher.

It DOES NOT support:
- Linq queries (but there’s a fine fluent interface instead);
- Lazy Loading (just load whatever you want!);
- Dirty tracking (just save whenever you want!);
- Custom column mapping (not intended for existing systems);
- Session ID Maps (it’s just too much!);
- Table per Class Hierarchy (Inheritance Strategy);
- Concurrence control (not yet!).

Why not NHibernate?
Although NHibernate is a very complete and mature framework, its complete abstraction of the database usually implies on a very large learning curve for adapting with a few features that some may find “awkward”, like Proxy classes used for Lazy Loading or the lack of control of the moments that Updates are triggered to the database.
Anyway, is also a conceptual project, and yet another ORM alternative approach purposed!


A simple example

Imagine a domain for a simple Access Control application. You’ll have:
- People (Users),
- Profiles (such as “Admin” or “Common user”),
- Permissions (like “User can access screen XPTO”),
- User Type (such as “Internal” or “External” access).

The Database usage is made through the Database class. A few examples:

// Opens a new Connection
var db = new Database();

// Getting a specific user, eager loading profiles and permissions
var user = db.First(new Query<SystemUser>().GetID(1).LoadAll());

user.Name = "Test";
user.Permissions.Add(new Permission {ScreenName = "XPTO"});

// Updating an user

// Deleting an user

// Selecting all users named “John”, order by id
var query = new Query<SystemUser>()
                        .Filter(u => u.Nome).StartsWith("John")
                        .Order(u => u.ID);

List<SystemUser> users = db.List(query);

// Or just the first page
long totalRecords;
List<SystemUser> usersPage = db.ListPage(query, 1, out totalRecords);

// Or just users with at least one Permission (child queries)
var query = new Query<Person>()
.Filter(p => p.Permissions).IsNotNull();

var users = db.List(query);

// Closes the connection


Now, we'll see the entities and mapping behind the scene:

The POCO classes would look like this:

public class Entity
  public long ID { get; set; }

public class Person : Entity
  public string Name { get; set; }
  public UserType Type { get; set; }
  public List<Profile> Profiles { get; set; }
  public List<Permission> Permissions { get; set; }
  public Address HomeAddress { get; set; }

public class Address
  public string Street { get; set; }
  public int Number { get; set; }
  public string City { get; set; }

public class UserType : Entity
  public string Name { get; set; }

public class Profile : Entity
  public string Name { get; set; }
  public List<Person> Users { get; set; }
  public List<Permission> Permissions { get; set; }

public class Permission : Entity
  public Profile Profile { get; set; }
  public Person SystemUser { get; set; }
  public string ScreenName { get; set; }

Then, the Mapping classes:

public class UserMap : EntityMap<Person>
  public UserMap()
    Map(x => x.Name);
    References(x => x.Type);
    HasManyToMany(x => x.Profiles).Cascade();
    HasMany(x => x.Permissions).Cascade();
    HasComponent(x => x.HomeAddress);

public class AddressMap : ComponentMap<Address>
  public AddressMap()
    Map(x => x.Street);
    Map(x => x.Number);
    Map(x => x.City);

public class ProfileMap : EntityMap<Profile>
  public ProfileMap()
    Map(x => x.Name);
    HasManyToMany(x => x.Users).Cascade();
    HasMany(x => x.Permissions).Cascade();

public class PermissionMap : EntityMap<Permission>
  public PermissionMap()
    References(x => x.Profile);
    References(x => x.SystemUser);
    Map(x => x.ScreenName);

public class UserTypeMap : EntityMap<UserType>
  public UserTypeMap()
    Map(x => x.Name);

Main Conventions
- All entities have an ID column, auto-numerated and PK;
- In One To Many relations, children should reference parents;
- When querying an entity, all reference and component properties are eager loaded;
- All collection properties must be explicitly loaded (no lazy-load, remember?);
- When saving or deleting an entity, cascading will occur only on the loaded collection properties (not null).
- Conventions for all table, fields and constraints names, based on entity meta-data – that’s why it’s recommended the use of database generation scripts.

Running the Test Project
- Download the Test Project from the source control;
- Create a Database on your SQL Server and update the project’s Connection String;

- Generate the data base scripts and run in your Database for creating the table structure;

- Run the Insert Tests, which contains the generation of the test data.
- Run All Query and Save tests!

For running your own tests, follow the steps:
- Reference the assembly Dorm.dll on your Infra/Data layer;
- Besides the Connection String, add a key named “MapsAssemblyName” with the name of the project that contains the entities mapping classes.
- Create your domain and mappings

- Generate the Database scripts and you’re good to go!

Currently, it’s a beta version and is only available for testing purposes. It should not be used on a production system.
Suggestions, critics and feedbacks are welcome! Enjoy!

Last edited Jan 24, 2011 at 5:26 PM by felipeccastro, version 5