Transcript Slide 1

Object-Relational Mapping in the
Microsoft World
by Benjamin Day
Benjamin Day Consulting, Inc.
About the speaker
• Owner, Benjamin Day Consulting, Inc.
 Email: [email protected]
 Web: http://www.benday.com
 Blog: http://blog.benday.com
• Trainer
 Visual Studio Team System, Team Foundation Server
• Microsoft MVP for C#
• Microsoft VSTS/TFS Customer Advisory Council
• Leader of Beantown.NET INETA User Group
Agenda
•
•
•
•
•
Overview of “the problem”
Brief discussion of the options
LINQ to SQL
NHibernate
Entity Framework
THE PROBLEM
The Problem
• Mismatch between objects and relational databases
 Objects = inheritance, polymorphism, composition
 Database = rows of data with relationships to other rows of data
One Table Per Class
• The class looks like the table
• Great candidate for a typed dataset
 Simple
 Works nicely with DataAdapter because of RowState
Limitations of Typed Dataset
Route
• Tight coupling with the database
• Inheritance is difficult
• How do you validate data?
 Null, data type, and length check validation is handled
 Serious validation lives outside of the dataset  not very object-oriented
• The ‘object’ route is better for validation
 Validation goes in the “set” property
Object - DataRow Hybrid
• Every object wraps a typed DataRow
• Properties control access to columns on the DataRow
 Facilitates more complex validation
• DataRow becomes a data transfer object
 Still works nicely for System.Data integration
Primitive Obsession
• Validation in the get / set properties is ok but is phone
number validation really the responsibility of the Person
class?
• James Shore’s “Primitive Obsession”
 Too many plain scalar values
 Phone number isn’t really just
a string
 http://www.jamesshore.com/Blog/
Coarse-Grained vs. Fine-Grained
Object Model
• Fine-grained = More object-oriented
• Data and properties are split into actual responsibilities
coarse-grained
fine-grained
But how do you save it?
• Four classes go to one table?
• Five instances go to one table?
Inheritance, Collections, and
Relationships
• Employee is a Person
• Employee has Underlings
• Employee has a Supervisor
Limitations of Object - DataRow
Hybrid
• Inheritance  >1 DataRow at once
 One for base class, one for descendent
• Which instance of DataSet is the object using?
 Complicates saves
 Client has to worry about the internal state of the object
• How to save parent-child relationships?
 Employee has FK to a Supervisor  Supervisor needs to be saved
first
• Objects have ability to modify internal state of other objects
wrapping the same DataSet  bugs & encapsulation
• Number of records in the DataSet tends to grow rather than
shrink
 How to clear out old, unused records in the DataSet? How do you
know when they’re old?
Object-Only Model
• No more DataRows
• Classes are member variables, methods and properties
• Clean “Domain Model” pattern
 Business tier worries about itself
 Decoupled from the database
• Straightforward inheritance, polymorphism
Complications in the Object-Only
Model
• Data access (“Mapper”) tier has to “adapt” data and
structures from the database into populated business
objects and vice versa
• How to manage IsDirty for INSERT vs UPDATE?
 Adding IsDirty logic to domain objects is a violation of the “separation of
concerns”
• Concurrency management?
• Transaction management?
Do you really want to solve these
problems?
• Problems are solvable
• Everybody writes their own solution
• Persistence is a distraction from solving The Real Business
Problem
AVAILABLE OPTIONS
Options
•
•
•
•
•
•
LINQ to SQL
Entity Framework
NHibernate
Wilson OR Mapper
LLBLGen
And more…
LINQ to SQL
• Available in Visual Studio 2008
• “Better typed dataset”
LINQ to SQL: Pros / Cons
Pros
• You don’t have to write
data access code
• Good for Rapid
Application Prototyping
• Integrated into Visual
Studio
• Designer support
• Will generate the
database schema for you
Cons
• SQL Server only
• Limited inheritance
modeling (Table Per
Hierarchy only)
• Closely tied to the
database
• Unusual way of handling
stored procedures
• Generates code
Entity Framework
• Microsoft’s first, real ORM solution
 (well…I guess this depends on who you talk to)
• In beta
• Rumored to be released with Visual Studio 2008 sp1
Entity Framework: Pros / Cons
Pros
• Full-featured
• Not open source
• Integrated into Visual
Studio
• Designer support
(eventually)
• Support for non-SQL
Server databases
(eventually)
• Supports LINQ
• Support Table-per-Type
Cons
• Still in beta
• Not open source
• Version 1
NHibernate
•
•
•
•
Full-featured ORM solution
Open source
Based on Java’s Hibernate framework
Uses XML to map tables/columns to objects/properties
NHibernate: Pros / Cons
Pros
• Rich selection of
mappings
• Inheritance modelling
• Polymorphic queries
• Established solution with
lots of current users
• Support for multiple
database vendors
• Open source
• Free
Cons
• Open source
• Free
• Not from Microsoft
• 3rd party library
• Limited to zero GUI
support
• Currently doesn’t support
LINQ
 …but does have HQL
LINQ TO SQL
Using the LINQ to SQL O/R Designer
• Define classes
• Set up inheritance
LINQ to SQL vs. Typed DataSets
• My $0.02 – LINQ to SQL is the new Typed DataSet
LINQ to SQL vs. LINQ to DataSets
•
•
•
•
DataSets in VS2008 are query-able with LINQ
DataTable’s object model has changed
DataTable now extends from TypeTableBase<T>
TypedTableBase<T> extends DataTable
• Backwards compatible
• No special way to fill the DataSet/DataTable with LINQ to
SQL
• Still uses DataAdapter
LINQ to SQL vs. Raw SQL Access
• Hopefully you don’t do this…
• The data access strategy of masochists everywhere
LINQ to SQL vs. NHibernate
• NHibernate is LINQ to SQL’s older, more successful
cousin
• 5+ years in .NET & Java
• Full-featured ORM framework
• Maps tables/columns to classes/properties
• Uses xml mapping files or attributes
• Comprehensive inheritance modeling
• Has LINQ-like object query syntax
• HQL – Hibernate Query Language
• Multi-vendor database support
• Oracle, SQLServer (2000 & 2005), MySql, Sybase, etc
• Lets you focus on the business problem rather than
persistence
Using LINQ to SQL with Unit Tests
• When testing database code, database must be in a known
state
• Easiest way:




Wipe the database between tests
DataContext.DeleteDatabase()
DataContext.CreateDatabase()
Database schema always in sync with code
• Harder way:
 Unit test manages transaction
 Rollback at end of unit test
LINQ to SQL in an n-tier Application
• Common BaseClass
• Hooking into save events
 Auto-updating: ModifiedDate, ModifiedBy
• Keeping your code organized with the
“Service Layer” pattern
Common Business Base Class
• Each business class should probably have similar fields
 Id
 ModifiedDate, ModifiedBy
 CreateDate, CreatedBy
• Bummer: LINQ to SQL isn’t great at this
 (NHibernate does this effortlessly)
 Mapped columns must be defined on the concrete
Code Demo
• Introduce a common business base class
Code Demo
• Implement the partials and auto-populate the base class
properties
Auto-update base class properties
from DataContext
• Generated DataContext & other objects are partial classes
• Generated code gives you partial methods on DataContext
for each object
 InsertXxx(), UpdateXxx(), DeleteXxx()
• Create your own partial class and create your own
implementation of the method
• Don’t forget to call
 ExecuteDynamicInsert(), ExecuteDynamicUpdate() or
ExecuteDynamicDelete()
Other fun stuff with the partial methods
• Your partial implementations wipe out the LINQ to SQL
default implementation
• (Who cares? This is boring.)
• You could put your own implementation that uses stored
procedures in your partials!
Service Layer Pattern
“Defines an application’s
boundary with a layer of services
that establishes a set of available
operations and coordinates the
application’s response in each
operation.”
-Randy Stafford
From “Patterns Of Enterprise Application Architecture”
by Martin Fowler, Randy Stafford, et al.
Chapter 9
Why Service Layer?
• Formally defines supported business tier operations (aka
methods)
• Methods provide ideal target for unit testing
• Keeps code organized
 Code review: anything complex not in the service layer  refactor
 Keeps code out of the UI
• Isolates the Domain Model (business) objects
 Minimize usage of the Domain Model objects outside of the Business tier
Service Layer in LINQ?
• CRUD operations for each business object
• Any specialized “get” operations
 Centralized place for any custom from-where-select’s
• Factory methods
• Create a BusinessFacade<T>
ENTITY FRAMEWORK
Entity Framework Overview
•
•
•
•
Still in beta
Official release with VS2008 sp1
Trying to be more than just an ORM
3 layers
 Conceptual Model (classes)
 Storage / Logical Model (table definitions)
 Mapping layer
NHIBERNATE
What NHibernate isn’t
• “Hibernate” has nothing to do with Windows Hibernate
• Not object serialization
• Not a code generator
NHibernate To The Rescue
• .NET port of the Java-based Hibernate Object-Relational
Framework
 NHibernate 1.2 is (roughly) Hibernate 2.1 + some features of
Hibernate 3.0
• “Hibernate's goal is to relieve the developer from 95 percent of
common data persistence related programming tasks.”
 Facilitates saves and retrieves of objects
• “Hibernate provides transparent persistence, the only
requirement for a persistent class is a no-argument
constructor.”
 Keeps your objects clean
• Open-source, free software under LGPL
 http://www.jboss.org/opensource/lgpl/faq
• Ported by Tom Barrett, Mike Doerfler, Peter Smulovics, and
Sergey Koshcheyev
What is it?
• Object Relational Mapping Framework
• XML-based
• Mapping files
 *.hbm.xml
 Classes/Properties  Tables/Columns
 1+ mapping files
• hibernate.cfg.xml
 Database Dialect: SQL Server, Oracle, MySQL, etc.
 Which assemblies to manage
 Which mapping files to use
What it will do for you.
• Make your life easier
• Simplify your data access
• Allow you to focus on your business tier
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0" >
<session-factory name="NHibernate.Test">
<!–- Writes all SQL to Console -->
<property name="show_sql">true</property>
<property
name="connection.provider">NHibernate.Connection.DriverConnectionProvider</p
roperty>
<property name="query.substitutions">true 1, false 0, yes 'Y', no
'N'</property>
<!–- SQL SERVER -->
<property name="connection.connection_string"
>Server=localhost\sql2005;initial catalog=bugs;User
ID=sa;Password=sa_password;Min Pool
Size=2</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="connection.driver_class“
>NHibernate.Driver.SqlClientDriver</property>
<!-- mapped assemblies -->
<mapping assembly="NHibernateResearch.Business" />
</session-factory>
</hibernate-configuration>
Mapping files (*.hbm.xml)
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class
name="NHibernateResearch.Business.Person,
NHibernateResearch.Business"
table="Person">
<id name="PersonId" type="System.Int32" unsaved-value="0">
<generator class="native" />
</id>
<timestamp name="LastModified" />
<property name="FirstName" not-null="true"></property>
<property name="LastName" not-null="true"></property>
</class>
</hibernate-mapping>
<class>
• Simplest mapping
• Maps a class to a database table
• Attributes
 “name” = name of the persistent class
 Fully qualified class name, assembly name (no “.dll”)
 “table” = name of the table
• Required element
 <id> = defines object identity
• Common elements
 <timestamp>, <version> for optimistic concurrency
 <property> for mapping class properties to database columns
<id>
<id name="PersonId" type="System.Int32" unsaved-value="0">
<generator class="native" />
</id>
•
•
•
Required element of <class> mapping
Used to establish object identity, database primary key
Attributes




•
“name” – name of the property
“column” – (optional) name of the table column
“type” – (optional) data type for the property
“unsaved-value” – used to determine INSERT vs. UPDATE
For “identity” columns, specify a <generator>
 class=“native” uses int identity column (SQL Server) or sequence (Oracle)
 class=“guid” generates guid keys
•
For non-”identity” columns use <composite-id>
 This approach is strongly discouraged
<property>
<property name="LastName" not-null="true"></property>
<property name="Html">
<column name="HtmlContent" sql-type="text" not-null="true"></column>
</property>
•
•
•
Child element to <class>
Used to map a property to a database column
Attributes
 “name” – name of the property
 “column” – (optional) name of the database column
 “not-null” – (optional) describes the nullability of the database column
 defaults to nullable
 “type” – (optional) datatype for the property
•
Optional <column> element describes information about the database column
 “sql-type” attribute – override default column datatype
 “length” attribute – override default column length
Mapping the Person class
<class
name="NHibernateResearch.Business.Person, NHibernateResearch.Business"
table="Person">
<id name="PersonId" type="System.Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="FirstName" not-null="true"></property>
<property name="LastName" not-null="true"></property>
<property name="Email" not-null="true"></property>
<property name="HomePhone" not-null="true"></property>
<property name="WorkPhone" not-null="true"></property>
</class>
The NHibernate Session
• Main point of contact
 ISession interface
• SessionFactory.OpenSession()
• Objects are associated the ISession
 Lazy loading
• Save, delete, and retrieve operations
Saving
• SaveUpdate()
Person person1 = new Person(); // create
person1.Name.FirstName = "firstname1"; // set properties
person1.Name.LastName = "lastname1";
session.SaveOrUpdate(person1);
session.Flush();
session.Close();
Transactions
• Call BeginTransaction() on the session
public virtual void Save(object item)
{
ITransaction tx = null;
try
{
tx = m_session.BeginTransaction();
m_session.SaveOrUpdate(item);
tx.Commit();
}
catch (Exception ex)
{
if (tx != null) tx.Rollback();
throw ex;
}
}
Retrieving
• Query / HQL syntax
 HQL = Hibernate Query Language
 SQL-like queries against the object model
 Use for more complex queries (JOINs)
• Criteria syntax
 Build the query programmatically
• NHibernate Allows Polymorphic Querying
HQL Sample
•Find Employees by Supervisor’s Name
HQL Sample
public IList FindSupervisorEmployees(
string firstName, string lastName)
{
string hqlQuery = @"
from Person p
where p.Supervisor.FirstName = :firstName
and
p.Supervisor.LastName = :lastName";
IQuery query = Session.CreateQuery(hqlQuery);
query.SetParameter("firstName", firstName);
query.SetParameter("lastName", lastName);
return query.List();
}
ICriteria: Load All By Type
• Gets an IList of objects by type or interface
public IList GetList(Type type)
{
ICriteria criteria = m_session.CreateCriteria(type);
return criteria.List();
}
• Polymorphic queries
 Piano : MusicalInstrument
 Flute : MusicalInstrument
 CreateCriteria(typeof(MusicalInstrument))
 Returns mix of Piano and Flute objects
ICriteria: Load By Property Value
• Gets a list of objects by type where a property has a certain
value
public IList Get(Type type,
string propertyName, object propertyValue)
{
ICriteria criteria = m_session.CreateCriteria(type);
criteria.Add(Expression.Eq(propertyName, propertyValue));
IList list = criteria.List();
return list;
}
Deleting
• Session.Delete(object instance)
Making Person More Fine-grained
fine-grained
coarse-grained
fine-grained
Mapping Fine-grained Person
<component>
<component name="Name"
class="NHibernateResearch.Business.Name,
NHibernateResearch.Business">
<property name="FirstName" not-null="true"></property>
<property name="LastName" not-null="true"></property>
</component>
• Used to map columns in a table to properties on a
different class
• Object does not have it’s own “identity”
 No primary key, no <id>
 Only exists in relation to the containing class
 Cannot save an instance of “Name” by itself to the database
Mapping Person Using
<component>
<class name="NHibernateResearch.Business.Person, NHibernateResearch.Business"
table="Person">
<id name="PersonId" unsaved-value="0">
<generator class="native" />
</id>
<component name="Name" class="NHibernateResearch.Business.Name,
NHibernateResearch.Business">
<property name="FirstName" not-null="true"></property>
<property name="LastName" not-null="true"></property>
</component>
<component name="Email" class="NHibernateResearch.Business.Email,
NHibernateResearch.Business">
<property name="Address" column="EmailAddress" not-null="true"></property>
</component>
<component name="WorkPhone" class="NHibernateResearch.Business.Phone,
NHibernateResearch.Business">
<property name="Number" column="WorkPhone" not-null="true"></property>
</component>
<component name="HomePhone" class="NHibernateResearch.Business.Phone,
NHibernateResearch.Business">
<property name="Number" column="HomePhone" not-null="true"></property>
</component>
</class>
Concurrency Columns
•
•
•
•
Child elements of <class>
Must be declared immediately after the <id>
<timestamp> – uses date/time data to manage concurrency
<version> – uses a numeric version id
Mapping Inheritance
• Employee is a Person
<joined-subclass>
<class name="NHibernateResearch.Business.Person, NHibernateResearch.Business" table="Person">
<id name="PersonId" unsaved-value="0">
<generator class="native" />
</id>
...
<joined-subclass name="NHibernateResearch.Business.Employee, NHibernateResearch.Business"
table="Employee">
<key column="EmployeeId"/>
<property name="Title" not-null="true" />
<many-to-one name="Supervisor" column="SupervisorId" not-null="false" cascade="saveupdate"></many-to-one>
<bag name="Underlings" lazy="true" inverse="true" cascade="save-update">
<key column="SupervisorId"></key>
<one-to-many
class="NHibernateResearch.Business.Employee, NHibernateResearch.Business"
></one-to-many>
</bag>
</joined-subclass>
</class>
•
•
•
Subclass gets its own table for its properties/columns
<joined-subclass> element goes inside of the superclass’ <class> or <joined-subclass>
element
<key> element defines the name of the column to use to join from the superclass’ <id>
(Person.PersonId  Employee.EmployeeId)
Mapping Associations &
Collections
• Supervisor is an Employee
• Employee has Underlings
Associations
• Associations define relationships between classes
• NHibernate uses the association mappings to
automatically store and retrieve related objects
• <one-to-one> – file has one owner
• <one-to-many> – directory has many files
• <many-to-one> – sub-directory has one parent
directory (many sub-directories, one parent)
• <many-to-many> – Many users, many roles 
intersection of a user and a role
<many-to-one>, <one-to-many>
<joined-subclass name="NHibernateResearch.Business.Employee, NHibernateResearch.Business" table="Employee">
<key column="EmployeeId"/>
<property name="Title" not-null="true" />
<many-to-one name="Supervisor" column="SupervisorId" not-null="false"
cascade="save-update”></many-to-one>
<bag name="Underlings" lazy="true" inverse="true" cascade="save-update">
<key column="SupervisorId"></key>
<one-to-many
class="NHibernateResearch.Business.Employee, NHibernateResearch.Business“ />
</bag>
</joined-subclass>
•
•
•
•
•
•
SupervisorId is a foreign-key to the supervisor’s EmployeeId
Many-to-one turns the SupervisorId into an instance of Employee
not-null=“false” means that the SupervisorId is nullable
cascade=“save-update” tells NHibernate to check the many-to-one relationship for
changes when an INSERT or UPDATE is requested
The association syntax is also used to populate collections of objects
<bag> populates an IList of Employee objects for the current employee

select * from employee where supervisorId=@currentEmployeeId
Collections
Mapping
Element
Interface
Implementation
Description
<list>
IList
ArrayList
Ordered collection
<bag>
IList
ArrayList
Unordered collection, allows
duplicates
<map>
IDictionary
Hashtable
Dictionary (key/value pairs)
• Use associations to store collections of
reference types
– <one-to-many>, <many-to-many>
• Use <element> tag to store collections of
value types
– Similar syntax to <property>
Collections: <bag>
Collection of objects
<bag name="Children" lazy="true" cascade="all">
<key column="ParentId"></key>
<one-to-many class="classname"></one-to-many>
</bag>
Collection of values
<bag name="Children" lazy="true" cascade="all">
<key column="ParentId"></key>
<element column=“columnName“ type="System.Int32"/>
</bag>
•
•
•
•
System.Collections.IList
If collection of values, allows duplicates
If collection of objects, duplicates get eaten
By default, items in the collection  same order as they in the tables
 “order-by” attribute available to do database sorts on data
Collections: <list>
<list name="Children" lazy="true" cascade="all">
<key column="ParentId"></key>
<index column="indexValue"></index>
<one-to-many class=“classname" ></one-to-many>
</list>
•
•
•
System.Collections.IList
Uses an numeric, zero-based index column for sorting
Missing index values become null
child0.Index = 0;
// no child with Index of 1
child1.Index = 2;
parent.Children.Add(child0);
parent.Children.Add(child1);
session.Save(parent);
// reload the parent
parent.Children[0]  child0;
parent.Children[1]  null;
parent.Children[2]  child1;
Collections: <map>
<map name="Children" cascade="all">
<key column="ParentId"></key>
<index column="indexval" type="System.Int32"></index>
<one-to-many class="classname" />
</map>
•
•
•
•
•
•
System.Collections.IDictionary
Unsorted collection uses Hashtable
Collection of key/value pairs
Use <index> for a value type key
Use <index-many-to-many> for an object key
 <index-many-to-many column="ItemId"

class="classname" />
If sorted, collection uses SortedList
 “order-by” attribute available to do database sorts on data
 “sort” attribute for sorting using an implementation of IComparer
Collections
• Lazy loading
 lazy = “true”
 Expose collections interface not concrete class
 ArrayList  IList
 Hashtable  IDictionary
 Lazy-load proxy loads on first access
• “inverse” attribute for bi-directionality
 “child” has a reference back to the parent
 Employee has Supervisor
 Supervisor has Employees
• “cascade” attribute
The “cascade” attribute
• Controls when changes to child objects are saved
• Options:





“none” – no cascading saves/deletes
“save-update” – cascade for INSERTs and UPDATEs
“delete” – cascade on DELETEs only
“all” – cascade on INSERT, UPDATE, DELETE
“all-delete-orphan” – same as all, automatically delete
any child objects when the parent’s collection
NHibernate, Collections, and
Generics
• Current release is NHibernate 1.2.1
• Now supports generics
Nullable Columns & ValueTypes
• Nullable columns should be avoided even if you don’t use
NHibernate
• Sometimes you need them
• Sometimes you’re stuck with them (legacy databases)
• Under .NET 2.0 – Use the “?” syntax for the nullable
properties on your classes
Auditing Info
• Auditing info common
to all tables / classes
 CreateDate,
CreatedBy
 LastModified,
LastModifiedBy
• Base class
functionality
• How to know when to
update the values?
ILifecycle
• Allows class to perform
actions at certain times
during the NHibernate
“lifecycle” of the instance
• OnSave(), OnUpdate(),
OnDelete(), OnLoad()
• Slightly “pollutes” object
model with NHibernate
specific code
• Now is deprecated
  IInterceptor
Code Demo
• Refactor Person to extend BusinessBase
• Implement auditing on Person
Intellisense for NHibernate
•
•
•
•
•
Eliminate the tedium
Eliminate the potential errors
Learn what else is in there
Download the source
Schemas in “src\NHibernate”



•
Copy to Visual Studio’s “Schemas” directory

•
nhibernate-configuration-2.0.xsd
nhibernate-mapping-2.0.xsd
nhibernate-generic.xsd
“c:\program files\Microsoft Visual Studio 8\Xml\Schemas”
Restart Visual Studio
Contains(), Evict(), Lock(),
Refresh()
• Methods on NHibernate Session
• Contains(object) – Queries the session to
determine if it is managing the supplied object
 Is the object persistent for the session
• Evict(object) – Remove an object from the
session’s control
• Lock(object, LockMode) – Request that the object
becomes persistent for the session
• Refresh(object) – Update the object with the
current data from the database
“assembly” & “namespace”
• Attributes on <hibernate-mapping>
• Specifies default
 Assembly name
 Namespace
• This
name="Com.Benday.ContentManagement.Business.
DomainModel.Folder,
Com.Benday.ContentManagement.Business”

name=“Folder”
Simplify Data Access With
Generics
• Persistent objects operations are almost identical
 ISession  Save(), Get(), Delete()
 ICriteria  GetBy***()
• Leads to code duplication in your façade (aka
Factory, Finder, Data Access) classes
• Use .NET 2.0 generics to
 Simplify
 Gain compile-time type safety
Code Walkthrough
• Com.Benday.NHibernate
• BusinessFacade<>
Mapping: <query>
• Allows you to write HQL complex queries and
store it separately from the code
• Assigned a unique name
• Accessed through ISession.GetNamedQuery()
• Executed via IQuery.List()
Mapping: <sql-query>
•
•
•
•
•
Write SQL queries against the native database
Still brings back persistent objects
ISession.GetNamedQuery(), IQuery.List()
<return class=“classname” alias=“table alias” />
Table alias must be in “{ }”
Design Strategies
• Model to schema





Create the object model first, then create mappings
Have NHibernate generate the database schema
Allows you to be much more database independent
Concentrate on creating a good object model and less about storage
This is the best to start learning NHibernate
 -Write some code, write some mappings
 -Export the schema through NHibernate
 -Look at the database tables and FK relationships and see if it’s what you
expected
• Schema to model
 Database first, then classes, then mappings
 Legacy development
 More difficult, more about the needs of the database and less focus on the object
model
Create Database Using
SchemaExport
• NHibernate.Tool.hbm2ddl namespace
• Reads hbm’s and classes  creates database schema
• Reads the database dialect  generates for your db (MySql, Sql
Server, Oracle, etc etc)
public static void ExportSchema()
{
Configuration config = new Configuration();
config.Configure();
SchemaExport exporter = new SchemaExport(config);
exporter.Drop(true, true);
exporter.Create(true, true);
}
• Great for unit testing
 always have a clean db
 db model always in sync with the hbm’s
Code Demo
•
•
•
•
•
•
Use BusinessFacade<>
Use schema export from unit tests
Create an fixture base class
Create a PersonFixture
Create a Person class
Play around with [TestInitialize] & [TestCleanup]
Mappings: Create a UNIQUE
database constraint
• “unique-key” attribute on the <column> mapping
• Set the same value on all properties that should
participate in the key
Benjamin Day
•
•
•
•
•
•
•
Consultant, Trainer
Architecture, Development, Project Coaching
Microsoft VSTS Customer Advisory Council
MVP for C#
Leader of Beantown .NET User Group
VSLive, O’Reilly Conferences
Team System, Team Foundation Server,
NHibernate, C#, ASP.NET, TDD, WCF
• http://blog.benday.com
• [email protected]