Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.
-
Upload
heather-skinner -
Category
Documents
-
view
227 -
download
0
Transcript of Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.
![Page 1: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/1.jpg)
Object-Relational Mapping in the Microsoft World
by Benjamin DayBenjamin Day Consulting, Inc.
![Page 2: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/2.jpg)
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
![Page 3: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/3.jpg)
Agenda
• Overview of “the problem”• Brief discussion of the options• LINQ to SQL• NHibernate• Entity Framework
![Page 4: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/4.jpg)
THE PROBLEM
![Page 5: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/5.jpg)
The Problem
• Mismatch between objects and relational databases Objects = inheritance, polymorphism, composition Database = rows of data with relationships to other rows of data
![Page 6: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/6.jpg)
One Table Per Class
• The class looks like the table• Great candidate for a typed dataset
Simple Works nicely with DataAdapter because of RowState
![Page 7: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/7.jpg)
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
![Page 8: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/8.jpg)
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
![Page 9: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/9.jpg)
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/
![Page 10: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/10.jpg)
Coarse-Grained vs. Fine-Grained Object Model
• Fine-grained = More object-oriented• Data and properties are split into actual responsibilities
coarse-grained fine-grained
![Page 11: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/11.jpg)
But how do you save it?
• Four classes go to one table?• Five instances go to one table?
![Page 12: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/12.jpg)
Inheritance, Collections, and Relationships
• Employee is a Person• Employee has Underlings• Employee has a Supervisor
![Page 13: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/13.jpg)
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?
![Page 14: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/14.jpg)
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
![Page 15: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/15.jpg)
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?
![Page 16: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/16.jpg)
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
![Page 17: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/17.jpg)
AVAILABLE OPTIONS
![Page 18: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/18.jpg)
Options
• LINQ to SQL• Entity Framework• NHibernate• Wilson OR Mapper• LLBLGen• And more…
![Page 19: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/19.jpg)
LINQ to SQL
• Available in Visual Studio 2008• “Better typed dataset”
![Page 20: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/20.jpg)
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
![Page 21: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/21.jpg)
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
![Page 22: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/22.jpg)
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
![Page 23: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/23.jpg)
NHibernate
• Full-featured ORM solution• Open source• Based on Java’s Hibernate framework• Uses XML to map tables/columns to objects/properties
![Page 24: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/24.jpg)
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
![Page 25: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/25.jpg)
LINQ TO SQL
![Page 26: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/26.jpg)
Using the LINQ to SQL O/R Designer
• Define classes• Set up inheritance
![Page 27: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/27.jpg)
LINQ to SQL vs. Typed DataSets
• My $0.02 – LINQ to SQL is the new Typed DataSet
![Page 28: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/28.jpg)
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
![Page 29: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/29.jpg)
LINQ to SQL vs. Raw SQL Access
• Hopefully you don’t do this…• The data access strategy of masochists everywhere
![Page 30: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/30.jpg)
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
![Page 31: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/31.jpg)
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
![Page 32: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/32.jpg)
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
![Page 33: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/33.jpg)
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
![Page 34: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/34.jpg)
Code Demo
• Introduce a common business base class
![Page 35: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/35.jpg)
Code Demo
• Implement the partials and auto-populate the base class properties
![Page 36: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/36.jpg)
Auto-update base class propertiesfrom 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()
![Page 37: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/37.jpg)
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!
![Page 38: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/38.jpg)
Service Layer Pattern
From “Patterns Of Enterprise Application Architecture”by Martin Fowler, Randy Stafford, et al.Chapter 9
“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
![Page 39: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/39.jpg)
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
![Page 40: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/40.jpg)
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>
![Page 41: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/41.jpg)
ENTITY FRAMEWORK
![Page 42: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/42.jpg)
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
![Page 43: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/43.jpg)
NHIBERNATE
![Page 44: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/44.jpg)
What NHibernate isn’t
• “Hibernate” has nothing to do with Windows Hibernate• Not object serialization• Not a code generator
![Page 45: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/45.jpg)
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
![Page 46: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/46.jpg)
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
![Page 47: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/47.jpg)
What it will do for you.
• Make your life easier• Simplify your data access• Allow you to focus on your business tier
![Page 48: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/48.jpg)
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</property>
<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>
![Page 49: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/49.jpg)
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>
![Page 50: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/50.jpg)
<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
![Page 51: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/51.jpg)
<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
![Page 52: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/52.jpg)
<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
![Page 53: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/53.jpg)
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>
![Page 54: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/54.jpg)
The NHibernate Session
• Main point of contact ISession interface
• SessionFactory.OpenSession() • Objects are associated the ISession
Lazy loading
• Save, delete, and retrieve operations
![Page 55: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/55.jpg)
Saving
• SaveUpdate() Person person1 = new Person(); // createperson1.Name.FirstName = "firstname1"; // set propertiesperson1.Name.LastName = "lastname1";session.SaveOrUpdate(person1);session.Flush();session.Close();
![Page 56: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/56.jpg)
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; }}
![Page 57: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/57.jpg)
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
![Page 58: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/58.jpg)
HQL Sample•Find Employees by Supervisor’s Name
![Page 59: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/59.jpg)
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();}
![Page 60: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/60.jpg)
ICriteria: Load All By Type
• Gets an IList of objects by type or interfacepublic 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
![Page 61: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/61.jpg)
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; }
![Page 62: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/62.jpg)
Deleting
• Session.Delete(object instance)
![Page 63: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/63.jpg)
Making Person More Fine-grained
coarse-grained
fine-grained
fine-grained
![Page 64: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/64.jpg)
Mapping Fine-grained Person
![Page 65: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/65.jpg)
<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
![Page 66: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/66.jpg)
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>
![Page 67: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/67.jpg)
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
![Page 68: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/68.jpg)
Mapping Inheritance• Employee is a Person
![Page 69: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/69.jpg)
<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="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" ></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)
![Page 70: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/70.jpg)
Mapping Associations & Collections• Supervisor is an Employee• Employee has Underlings
![Page 71: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/71.jpg)
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
![Page 72: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/72.jpg)
<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
![Page 73: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/73.jpg)
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>
![Page 74: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/74.jpg)
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
![Page 75: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/75.jpg)
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 1child1.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;
![Page 76: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/76.jpg)
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
![Page 77: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/77.jpg)
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
![Page 78: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/78.jpg)
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
![Page 79: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/79.jpg)
NHibernate, Collections, and Generics• Current release is NHibernate 1.2.1• Now supports generics
![Page 80: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/80.jpg)
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
![Page 81: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/81.jpg)
Auditing Info
• Auditing info common to all tables / classes CreateDate,
CreatedBy LastModified,
LastModifiedBy
• Base class functionality
• How to know when to update the values?
![Page 82: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/82.jpg)
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
![Page 83: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/83.jpg)
Code Demo
• Refactor Person to extend BusinessBase• Implement auditing on Person
![Page 84: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/84.jpg)
Intellisense for NHibernate
• Eliminate the tedium• Eliminate the potential errors• Learn what else is in there• Download the source• Schemas in “src\NHibernate”
nhibernate-configuration-2.0.xsd nhibernate-mapping-2.0.xsd nhibernate-generic.xsd
• Copy to Visual Studio’s “Schemas” directory “c:\program files\Microsoft Visual Studio 8\Xml\Schemas”
• Restart Visual Studio
![Page 85: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/85.jpg)
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
![Page 86: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/86.jpg)
“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”
![Page 87: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/87.jpg)
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
![Page 88: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/88.jpg)
Code Walkthrough
• Com.Benday.NHibernate• BusinessFacade<>
![Page 89: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/89.jpg)
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()
![Page 90: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/90.jpg)
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 “{ }”
![Page 91: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/91.jpg)
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
![Page 92: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/92.jpg)
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
![Page 93: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/93.jpg)
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]
![Page 94: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/94.jpg)
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
![Page 95: Object-Relational Mapping in the Microsoft World by Benjamin Day Benjamin Day Consulting, Inc.](https://reader030.fdocuments.in/reader030/viewer/2022033023/56649dde5503460f94ad79ab/html5/thumbnails/95.jpg)
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]