Exploring the NHibernate Ecosystem Steve Bohlen Blog:
Steve Bohlen Nearly 20 years developing software LISP, Pascal, C/C++, VB, VB.NET, C# Co-Founder, NYC Alt.Net User Group Contributor: various OSS projects blog:
Oredev2009: Efficiency Relational Persistence Object Relational Mapping with NHibernate Persistence Framework NHibernate-based Frameworks NHibernate Add-ins
Coming Up: A Tour
…Not a Deep Dive
Mapping the Universe
NHibernate Core External NHContrib NH Spatial NH Burrow NH Linq FluentNH Castle ActiveRecord NH Validator NH Proxy Gen Rhino Tools Castle NH Facility uNHAddins NH Caches NH Mapping Attributes NH Shards NH Search Castle ActiveWriter JetDriver Lambda Extensions NH Prof
Non-Relational Data Sources Mapping, Configuration, and Query Infrastructure and Frameworks Relational Data Sources NHibernate Core NHSpatial NHSearch NH LINQ FluentNH Castle ActiveRecord NH Validator JetDriver Rhino Tools Castle NH Facility NHBurrow NH Attribute Mapping uNHAddins NH Caches Lambda Extensions
NHibernate Implementation Framework (plus a lot more)
A Complete Infrastructure Stack Unit-of-Work Abstraction IoC Container Convenience Services Assumes Castle Windsor NH Session lifecycle management for ASP.NET apps Conversation-per-Business-Transaction NHRepository implementation Multiple, concurrent DB support Lots more
Rhino IRepository public interface IRepository { // Methods long Count(); long Count(DetachedCriteria criteria); T Create(); DetachedCriteria CreateDetachedCriteria(); DetachedCriteria CreateDetachedCriteria(string alias); void Delete(T entity); void DeleteAll(); void DeleteAll(DetachedCriteria where); object ExecuteStoredProcedure(string sp_name, params Parameter[] parameters); ICollection ExecuteStoredProcedure (Converter converter, string sp_name, params Parameter[] parameters); bool Exists(); bool Exists(DetachedCriteria criteria); ICollection FindAll(params ICriterion[] criteria); ICollection FindAll(Order order, params ICriterion[] criteria); ICollection FindAll(Order[] orders, params ICriterion[] criteria); ICollection FindAll(DetachedCriteria criteria, params Order[] orders); ICollection FindAll(string namedQuery, params Parameter[] parameters); ICollection FindAll(int firstResult, int numberOfResults, params ICriterion[] criteria); ICollection FindAll(DetachedCriteria criteria, int firstResult, int maxResults, params Order[] orders); ICollection FindAll(int firstResult, int numberOfResults, Order selectionOrder, params ICriterion[] criteria); ICollection FindAll(int firstResult, int numberOfResults, string namedQuery, params Parameter[] parameters); ICollection FindAll(int firstResult, int numberOfResults, Order[] selectionOrder, params ICriterion[] criteria); T FindFirst(params Order[] orders); T FindFirst(DetachedCriteria criteria, params Order[] orders); T FindOne(params ICriterion[] criteria); T FindOne(DetachedCriteria criteria); T FindOne(string namedQuery, params Parameter[] parameters); FutureValue FutureGet(object id); FutureValue FutureLoad(object id); T Get(object id); T Load(object id); ICollection ReportAll (ProjectionList projectionList); ICollection ReportAll (DetachedCriteria criteria, ProjectionList projectionList); ICollection ReportAll (ProjectionList projectionList, params ICriterion[] criterion); ICollection ReportAll (ProjectionList projectionList, params Order[] orders); ICollection ReportAll (ProjectionList projectionList, bool distinctResults); ICollection ReportAll (string namedQuery, params Parameter[] parameters); ICollection ReportAll (DetachedCriteria criteria, ProjectionList projectionList, params Order[] orders); ICollection ReportAll (ProjectionList projectionList, Order[] orders, params ICriterion[] criteria); ProjT ReportOne (DetachedCriteria criteria, ProjectionList projectionList); ProjT ReportOne (ProjectionList projectionList, params ICriterion[] criteria); T Save(T entity); T SaveOrUpdate(T entity); T SaveOrUpdateCopy(T entity); void Update(T entity); }
Abstractions, Tools, and a WHOLE lot more!
uNhAddins: a Smörgåsbord! NH UserTypes NH Event Listeners Inflector NH Session Abstraction Query Pagination Tolerant Query Cache Conversation- Per-Business Transaction NH Audit Event Listeners IoC Container Abstraction Castle Windsor Adapter Spring.NET Adapter Ninject Adapter NH Session Mgt for WCF NH Session Mgt for WPF Validation Abstraction NH Validator Adapter Data Annotations Adapter Castle Validator Adapter Validation Ent. Application Block Adapter
Efficient Database Caching
Cache Providers MemCache Implementation for MemCached Prevalence Bamboo.Prevalence engine SharedCache Inspired by MemCached but 100% managed code (C#) Velocity Microsoft’s Distributed Caching Engine (CTP2) SysCache ASP.NET Cache Provider SysCache2 ASP.NET Cache Provider ○ with SQLServer call-back-invalidate support
Simpler Data Access
ActiveRecord Example [ActiveRecord] public class Category : ActiveRecordBase { [PrimaryKey] public int Id { get; set; } [Property] public string Name { get; set; } [BelongsTo("parent_id")] public Category Parent { get; set; } [HasMany] public IList<Category> SubCategories { get; set; } }
Integrated Validation Framework
Using NHValidator 1. Get and Build it (NHContrib) 2. Add References 3. Register Event Listeners in code or hibernate.cfg.xml file 4. Off and Running!
Let’s Look at Some Code!
Death to String-Literals!!!!
Using NHLambdaExtensions 1. Download the Assembly (googlecode) Add Reference to Assembly 2. Off and Running!
LambdaExtensions In Action session.CreateCriteria<Customer>().Add(Restrictions.Eq(“Firstname”, “Steve”).List<Customer>(); session.CreateCriteria<Customer>().Add<Customer>(c => c.Firstname == “Steve”).List<Customer>();
One Query Language to Rule Them All!
Using NHLINQ 1. Download the Assembly (sourceforge) v1.0 NH 2.1GA release 2. Add Reference to Assembly 3. Off and Running!
NHLINQ in Action using (var session = sessionFactory.OpenSession()) { using (var tx = session.BeginTransaction()) { var customers = session.Linq<Customer>().Where(c => c.Firstname == “Steve”); foreach (var customer in customers) { Console.WriteLine(customer.Firstname); } tx.Commit(); } }
Stateful NHibernate Session Management for ASP.NET WebForms
Using Burrow 1. Get it and Build it (NHContrib) 2. Add References 3. Add NHibernate.Burrow config section to web.config 4. Add Burrow HTTP Module to your web.config
Modify web.config for Burrow
Register Burrow HTTPModule
Burrow Conversation Pattern BurrowFramework bf = new BurrowFramework(); bf.CurrentConversation.SpanWithPostBacks(TransactionStrat egy.BusinessTransaction); //do a bunch of work in a bunch of postbacks BurrowFramework bf = new BurrowFramework(); bf.CurrentConversation.FinishSpan(); //commit to DB… bf.CurrentConversation.GiveUp(); //…or abandon!
Spatial Queries
Understanding Spatial Data Latitude / Longitude Coordinate Systems (Spatial Reference ID) SRID Projections
Supported Spatial Engines MS SQLServer 2008 Includes SQLServer 2008 Express! MySQL PostGIS (PostGre-based) Oracle (work-in-progress)
Using NH Spatial 1. Get and Build it (NHContrib) 2. Add References (GeoAPI, Spatial, etc.) 3. Change Dialect in hibernate.cfg.xml 4. Optional: add support for spatial metadata to the Configuration instance before building SessionFactory 5. Map properties as ‘Geometry Type’ 6. Off and Running!
NHSpatial: Dialect NHibernate.Driver.SqlClientDriver Server=(local);initial catalog=nhibernate;Integrated Security=SSPI 10 false NHibernate.Spatial.Dialect.MsSql2008SpatialDialect, NHibernate.Spatial.MsSql2008 true 60 true 1, false 0, yes 'Y', no 'N' NHibernate.ByteCode.LinFu.ProxyFactoryFactory,NHibernate.ByteCode.LinFu
Add Spatial Metadata Classes Configuration cfg = new Configuration(); cfg.Configure(); Metadata.AddMapping(cfg, MetadataClass.GeometryColumn); Metadata.AddMapping(cfg, MetadataClass.SpatialReferenceSystem); var sessionFactory = cfg.BuildSessionFactory(); //rest of your app here!
Add Geometry Type + Mapping using GeoAPI.Geometries public class MyThing { public virtual IGeometry Geometry {get;set;} //more of our class } <!-- short version --> <property name="Geometry" column="the_geom" type = "NHibernate.Spatial.Type.GeometryType, NHibernate.Spatial" /> <!-- long version --> <property name="Geometry" column="the_geom"> <type name = "NHibernate.Spatial.Type.GeometryType, NHibernate.Spatial"> <param name="srid">4326</param> <param name="subtype">POLYGON</param> </type> </property>
Perform Spatial Queries var country = session.CreateCriteria<Country>().Add(SpatialExpression.Contains("Boundaries", new Point(-70.40, ))).UniqueResult<Country>(); IList<Town> towns = session.CreateCriteria<Town>().Add(SpatialExpression.Filter("Boundaries", new Envelope(-70, -68, -32, -34))).Add(Restrictions.Not(SpatialExpression.Contains("Bounda ries", new Point(-70.40, )))).List<Town>();
Querying Unstructured Text Indices
The Power of Lucene.NET Databases are efficient and querying relational data Databases are inefficient at querying unstructured text Better tools exist to do that Lucene.NET ○ A port of the Lucene project to.NET ○ High-performance indexed searching of text content
NHibernate Search NHibernateQuery Lucene.NET Document Index Relational Database Select all Customers who have more than 10 orders and whose comments on their Invoices contain the word “’dissatisfied”
Using NHSearch 1. Get and build it (NHContrib) 2. Add References 3. Add index-related properties to hibernate.cfg.xml 4. Register Ins, Upd, Del event listeners to trigger updates to index on change 5. Add attributes to your classes to indicate what should be indexed 6. Off and Running!
Modify Configuration File NHibernate.Driver.SqlClientDriver Server=(local);initial catalog=nhibernate;Integrated Security=SSPI 10 false NHibernate.Dialect.MsSql2000Dialect true 60 true 1, false 0, yes 'Y', no 'N' NHibernate.ByteCode.LinFu.ProxyFactoryFactory,NHibernate.ByteCode.LinFu NHibernate.Search.Store.FSDirectoryProvider, NHibernate.Search c:\MyIndex event
Register Event Listeners //register in code var cfg = new Configuration(); cfg.SetListener(NHibernate.Event.ListenerType.PostUpdate, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostInsert, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostDelete, new FullTextIndexEventListener());
Add Attributes for Index Engine public class Document { [DocumentId] public virtual int Id { get; set; } [Field(Index.Tokenized, Store=Store.Yes)] public virtual string Title { get; set; } [Field(Index.Tokenized)] public virtual string Body { get; set; } }
Perform Indexed Queries using (var session = sessionFactory.OpenSession()) { using(var textsearch = Search.CreateFullTextSession(session)) { using (var tx = session.BeginTransaction()) { var results = textsearch.CreateFullTextQuery (“Title:Oredev").SetMaxResults(10).List (); }
Mapping and Configuration without XML
Using FluentNHibernate 1. Get it ( ) 2. Add References 3. Off and Running!
Sample Classes
XML Mappings
Fluent Mappings public class CustomerMap : ClassMap<Customer> { public CustomerMap() { Id(c => c.Id).Column("CustomerId"); Map(c => c.Firstname); Map(c => c.Lastname); HasMany<Order>(c => c.Orders).Table("Order").KeyColumn("CustomerId").Inverse().Generic(); } }
XML Configuration NHibernate.Driver.SqlClientDriver Server=(local)\sqlserver2005;initial catalog=FluentNHibernateDemo;user=sa;password=password 10 true NHibernate.Dialect.MsSql2005Dialect true 60 true 1, false 0, yes 'Y', no 'N' NHibernate.ByteCode.Castle.ProxyFactoryF actory, NHibernate.ByteCode.Castle
Fluent Configuration sessionFactory = Fluently.Configure().Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.Ms Sql2005.ConnectionString(CONNSTRING).AdoNetBatchSize(10).ProxyFactoryFactory <NHibernate.ByteCode.Castle.ProxyFactoryFactory>().UseOuterJoin()).Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>()).BuildSessionFactory();
Convention Mapping Enables Fluent NHibernate to ‘infer’ your mappings from your objects Uses conventions Baked into FNH Overrides provided by yourself Identity field convention Many-to-many intermediate table convention Foreign-key id column convention Many, many more
Production-Class Profiling for ORMs
Metrics, Analysis, Recommendations
Summary If you’re doing your data-access by hand… YOU’RE DOING IT WRONG If you’re doing the rest of the stuff you saw here today by hand… YOU’RE DOING IT WRONG NHibernate has one of the richest ecosystems of extensions, frameworks, and tools of any.NET technology, OSS or otherwise… LEARN TO LEVERAGE THEM for EFFICIENCY
*NHibernate GA Released!* November 1, 2009 Probably the final 2.x release before 3.0 Primarily bug-fix, no breaking changes Most of these tools will work with Most will need to be recompiled against the new release before use ○ Binary dependency on NH assemblies HORNGET.NET is your friend here!
Resources NHForge NHContrib uNhAddins NHProf HornGet.NET
~fini~