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]