<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://nhforge.org/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>NHibernate blog : mapping, NHibernate</title><link>http://nhforge.org/blogs/nhibernate/archive/tags/mapping/NHibernate/default.aspx</link><description>Tags: mapping, NHibernate</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP1 (Build: 31106.3070)</generator><item><title>NHibernate Auditing v3 – Poor Man’s Envers</title><link>http://nhforge.org/blogs/nhibernate/archive/2010/07/05/nhibernate-auditing-v3-poor-man-s-envers.aspx</link><pubDate>Tue, 06 Jul 2010 05:25:00 GMT</pubDate><guid isPermaLink="false">45f813f2-f1c4-4eda-a619-288e3cadc793:657</guid><dc:creator>Jason Dentler</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://nhforge.org/blogs/nhibernate/rsscomments.aspx?PostID=657</wfw:commentRss><comments>http://nhforge.org/blogs/nhibernate/archive/2010/07/05/nhibernate-auditing-v3-poor-man-s-envers.aspx#comments</comments><description>&lt;p&gt;First, let me explain the title of this post. The Hibernate folks &amp;ndash; you know, that &lt;a target="_blank" href="http://nhforge.org"&gt;NHibernate&lt;/a&gt; knock off written in the Java (pronounced &amp;ldquo;ex em el&amp;rdquo;) programming language &amp;ndash; have a project called Envers. Among other things, It audits changes to entities, then allows you to easily retrieve the entity as it was at any previous point in time. &lt;/p&gt;
&lt;p&gt;Well, Simon Duduica is porting this over to .NET and NHibernate, and he&amp;rsquo;s making some AMAZING progress. On June 28th, he shared this news with us on the NH Contrib development group:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi everybody,&lt;/p&gt;
&lt;p&gt;I have news regarding Envers.NET. I&amp;#39;ve commited a version that works in basic tests for CUD operations, with entities that have relationships between them, also with entities that are not audited. To make things work I had to make two small modifications of NHibernate, both modifications were tested running all NHibernate unit tests and they all passed. I already sent the first modification to Fabio and the second I will send this evening. I would like to thank Tuna for helping me out with good advices when I was stuck :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;So, on to the topic of this post. For &lt;span style="text-decoration:underline;"&gt;NHibernate 3.0 Cookbook&lt;/span&gt;, I&amp;rsquo;ve included a section that explains how to use NHibernate to generate audit triggers. Originally, I had planned to use the code from &lt;a target="_blank" href="http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate-v2/"&gt;my previous blog post on the topic&lt;/a&gt;, but I didn&amp;rsquo;t like its structure. I also didn&amp;rsquo;t want to include all that plumbing code in the printed book. Instead, I&amp;rsquo;ve rewritten and contributed the &amp;ldquo;framework&amp;rdquo; code to &lt;a target="_blank" href="http://code.google.com/p/unhaddins/"&gt;uNHAddIns&lt;/a&gt;. The &amp;ldquo;how-to use it&amp;rdquo; is explained in the book, so I won&amp;rsquo;t explain it here.&lt;/p&gt;
&lt;p&gt;Today, I was writing an integration test for this contribution, and thought the idea was worth sharing. I have a simple Cat class:&lt;/p&gt;
&lt;p&gt;&lt;img height="143" width="163" src="http://nhforge.org/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/nhibernate/ClassDiagram1_5F00_1E6B8C88.png" alt="ClassDiagram1" border="0" title="ClassDiagram1" style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" /&gt; &lt;/p&gt;
&lt;p&gt;When I do anything to this cat, in addition to the normal INSERT, UPDATE, or DELETE, a database trigger records that action in a table called CatAudit:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://nhforge.org/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/nhibernate/image_5F00_6ECD6DFB.png"&gt;&lt;img height="159" width="206" src="http://nhforge.org/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/nhibernate/image_5F00_thumb_5F00_5B400E5A.png" alt="image" border="0" title="image" style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;I wanted an easy way to investigate the contents of this table to prove that my audit triggers worked. Here&amp;rsquo;s what I came up with, along with help from Jose Romaniello (@jfroma). First, I created a class to match this table:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://nhforge.org/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/nhibernate/ClassDiagram1_5F00_60AE7EFE.png"&gt;&lt;img height="240" width="150" src="http://nhforge.org/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/nhibernate/ClassDiagram1_5F00_thumb_5F00_65B0BCAD.png" alt="ClassDiagram1" border="0" title="ClassDiagram1" style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Next, I mapped it, made it readonly and excluded it from hbm2ddl with this mapping:&lt;/p&gt;
&lt;pre class="brush:xml"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;
&amp;lt;hibernate-mapping xmlns=&amp;quot;urn:nhibernate-mapping-2.2&amp;quot;
				   assembly=&amp;quot;uNhAddIns.Test&amp;quot;
				   namespace=&amp;quot;uNhAddIns.Test.Audit.TriggerGenerator&amp;quot;&amp;gt;
  &amp;lt;typedef class=&amp;quot;NHibernate.Type.EnumStringType`1[[uNhAddIns.Audit.TriggerGenerator.TriggerActions, uNhAddIns]], NHibernate&amp;quot;
           name=&amp;quot;triggerActions&amp;quot; /&amp;gt;
  &amp;lt;class name=&amp;quot;CatAudit&amp;quot; 
         mutable=&amp;quot;false&amp;quot;
         schema-action=&amp;quot;none&amp;quot;&amp;gt;
    &amp;lt;composite-id&amp;gt;
      &amp;lt;key-property name=&amp;quot;Id&amp;quot; /&amp;gt;
      &amp;lt;key-property name=&amp;quot;AuditUser&amp;quot; /&amp;gt;
      &amp;lt;key-property name=&amp;quot;AuditTimestamp&amp;quot; /&amp;gt;
    &amp;lt;/composite-id&amp;gt;
    &amp;lt;property name=&amp;quot;Color&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;AuditOperation&amp;quot; type=&amp;quot;triggerActions&amp;quot; /&amp;gt;
  &amp;lt;/class&amp;gt;
	
&amp;lt;/hibernate-mapping&amp;gt;&lt;/pre&gt;
&lt;p&gt;I made it readonly by setting mutable=&amp;quot;false&amp;quot; and excluded it from hbm2ddl with schema-action=&amp;quot;none&amp;quot;. That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;By the way, the &amp;lt;typedef&amp;gt; along with type=&amp;quot;triggerActions&amp;quot; just tells NHibernate I&amp;#39;ve stored my TriggerActions enum values as strings, not numbers.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://nhforge.org/aggbug.aspx?PostID=657" width="1" height="1"&gt;</description><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/mapping/default.aspx">mapping</category><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/NHibernate/default.aspx">NHibernate</category><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/schema+action/default.aspx">schema action</category><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/uNHAddins/default.aspx">uNHAddins</category></item><item><title>How Test your mappings: the Ghostbuster</title><link>http://nhforge.org/blogs/nhibernate/archive/2008/10/20/how-test-your-mappings-the-ghostbuster.aspx</link><pubDate>Mon, 20 Oct 2008 13:48:00 GMT</pubDate><guid isPermaLink="false">45f813f2-f1c4-4eda-a619-288e3cadc793:94</guid><dc:creator>Fabio Maulo</dc:creator><slash:comments>6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://nhforge.org/blogs/nhibernate/rsscomments.aspx?PostID=94</wfw:commentRss><comments>http://nhforge.org/blogs/nhibernate/archive/2008/10/20/how-test-your-mappings-the-ghostbuster.aspx#comments</comments><description>&lt;p&gt;In NHibernate, when you have the &lt;span style="color:#2b91af;"&gt;FlushMode&lt;/span&gt; configured to &lt;b&gt;AutoFlush&lt;/b&gt;, session.Flush() is called when NH detects a dirty entity instance and when&amp;nbsp;a query with&amp;nbsp;an intersected &lt;i&gt;QuerySpace&lt;/i&gt;&amp;nbsp;is performed. (The &lt;i&gt;QuerySpace&lt;/i&gt; is represented by all tables affected in a query.)&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class="code"&gt;  &lt;span style="color:#0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;class &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Animal&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;id &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Id&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;         &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;generator &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;class&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;hilo&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt;     &amp;lt;/&lt;/span&gt;&lt;span style="color:#a31515;"&gt;id&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;property &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Description&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;joined-subclass &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Reptile&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;         &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;key &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;column&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;animalId&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt;         &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;property &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;BodyTemperature&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt;     &amp;lt;/&lt;/span&gt;&lt;span style="color:#a31515;"&gt;joined-subclass&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;/&lt;/span&gt;&lt;span style="color:#a31515;"&gt;class&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;In the above domain, a query on the &lt;span style="color:#2b91af;"&gt;Reptile&lt;/span&gt; class in a opened session with a dirty instance of &lt;span style="color:#2b91af;"&gt;Animal&lt;/span&gt; would cause&amp;nbsp;session.Flush() will be called.&lt;/p&gt;
&lt;p&gt;After a &lt;span style="font-family:courier new;"&gt;session.Get&amp;lt;&lt;span style="color:#2b91af;"&gt;Animal&lt;/span&gt;&amp;gt;(animalId)&lt;/span&gt; we can be pretty sure that there is no dirty entities in the session, &lt;b&gt;sure ?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Don&amp;rsquo;t be so sure!&lt;/b&gt; The real answer is: &lt;b&gt;&lt;span style="text-decoration:underline;"&gt;It depends&lt;/span&gt;&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;For example try this domain:&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span style="color:#0000ff;"&gt;public enum &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;Sex&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt; Unspecified,&lt;br /&gt; Male,&lt;br /&gt; Female&lt;br /&gt;}&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;public class &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;Person&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public virtual int &lt;/span&gt;Id { &lt;span style="color:#0000ff;"&gt;get&lt;/span&gt;; &lt;span style="color:#0000ff;"&gt;set&lt;/span&gt;; }&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public virtual &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;Sex &lt;/span&gt;Sex { &lt;span style="color:#0000ff;"&gt;get&lt;/span&gt;; &lt;span style="color:#0000ff;"&gt;set&lt;/span&gt;; }&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;with this mapping:&lt;/p&gt;
&lt;pre class="code"&gt;  &lt;span style="color:#0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;class &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Person&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;id &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Id&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;         &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;generator &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;class&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;hilo&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt;     &amp;lt;/&lt;/span&gt;&lt;span style="color:#a31515;"&gt;id&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;     &amp;lt;&lt;/span&gt;&lt;span style="color:#a31515;"&gt;property &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;name&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;Sex&lt;/span&gt;&amp;quot; &lt;span style="color:#ff0000;"&gt;type&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;int&lt;/span&gt;&amp;quot;&lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;br /&gt; &amp;lt;/&lt;/span&gt;&lt;span style="color:#a31515;"&gt;class&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the mapping I define the property Sex of type &lt;span style="color:#0000ff;"&gt;int&lt;/span&gt; but in the class the type is &lt;span style="color:#2b91af;"&gt;Sex&lt;/span&gt;; even if you don&amp;rsquo;t receive an exception, because an &lt;span style="color:#0000ff;"&gt;int&lt;/span&gt; is convertible to &lt;span style="color:#2b91af;"&gt;Sex&lt;/span&gt; and viceversa, your persistence will have a unexpected&amp;nbsp; behavior. NH will detect a modification, of your entity, &amp;ldquo;immediately&amp;rdquo; after session.Get because it having an &lt;span style="color:#0000ff;"&gt;int&lt;/span&gt; in the entity snap-shot (retrieved from DB) and a &lt;span style="color:#2b91af;"&gt;Sex&lt;/span&gt; in the actual state. The example are showing a very simple case of &amp;ldquo;ghosts&amp;rdquo; in your application. In a big environment, with a complex domain, find &amp;ldquo;ghosts&amp;rdquo; it is not so easy.&lt;/p&gt;
&lt;h3&gt;The Ghostbusters&lt;/h3&gt;
&lt;pre class="code"&gt;[&lt;span style="color:#2b91af;"&gt;TestFixtureSetUp&lt;/span&gt;]&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;public void &lt;/span&gt;TestFixtureSetUp()&lt;br /&gt;{&lt;br /&gt; &lt;span style="color:#2b91af;"&gt;XmlConfigurator&lt;/span&gt;.Configure();&lt;br /&gt; cfg = &lt;span style="color:#0000ff;"&gt;new &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;Configuration&lt;/span&gt;();&lt;br /&gt; cfg.Configure();&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;new &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;SchemaExport&lt;/span&gt;(cfg).Create(&lt;span style="color:#0000ff;"&gt;false&lt;/span&gt;, &lt;span style="color:#0000ff;"&gt;true&lt;/span&gt;);&lt;br /&gt; sessions = (&lt;span style="color:#2b91af;"&gt;ISessionFactoryImplementor&lt;/span&gt;) cfg.BuildSessionFactory();&lt;br /&gt; PopulateDb();&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Few words about the TestFixtureSetUp: &lt;/p&gt;
&lt;ul&gt;
&lt;br /&gt;
&lt;li&gt;if you are testing your domain persistence you can run the &amp;ldquo;ghostbuster&amp;rdquo; in each test. &lt;/li&gt;
&lt;li&gt;if you are testing yours &lt;a href="http://en.wikipedia.org/wiki/Data_Access_Object"&gt;DAO&lt;/a&gt;s and you have an implementation of &lt;a href="http://martinfowler.com/bliki/ObjectMother.html"&gt;ObjectMother&lt;/a&gt; or &lt;a href="http://nat.truemesh.com/archives/000714.html"&gt;TestDataBuilder&lt;/a&gt; you can use it in the implementation of &lt;i&gt;PopulateDb()&lt;/i&gt; method. &lt;/li&gt;
&lt;li&gt;If you don&amp;rsquo;t have tests you can leave the &lt;i&gt;PopulateDb()&lt;/i&gt; method empty and configure NH to an existing copy of your DB. &lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="code"&gt;[&lt;span style="color:#2b91af;"&gt;Test&lt;/span&gt;, &lt;span style="color:#2b91af;"&gt;Explicit&lt;/span&gt;]&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;public void &lt;/span&gt;UnexpectedUpdateDeleteOnFetch()&lt;br /&gt;{&lt;br /&gt; PersistingMappings(&lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[&lt;span style="color:#2b91af;"&gt;Test&lt;/span&gt;, &lt;span style="color:#2b91af;"&gt;Explicit&lt;/span&gt;]&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;public void &lt;/span&gt;UnexpectedUpdateDeleteOnFetchSpecific()&lt;br /&gt;{&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;entitiesFilter = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt;[]&lt;br /&gt;                          {&lt;br /&gt;                              &lt;span style="color:#a31515;"&gt;&amp;quot;Person&amp;quot;&lt;br /&gt;                          &lt;/span&gt;};&lt;br /&gt; PersistingMappings(entitiesFilter);&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;In my experience the above two tests are needed. The first sound like &lt;i&gt;&amp;ldquo;close your eyes and pray&amp;rdquo;&lt;/i&gt; the second allow you to analyze some specific entities.&lt;/p&gt;
&lt;p&gt;To avoid breaking the test, on each unexpected DB-hit, I&amp;rsquo;ll use the power of &lt;a href="http://logging.apache.org/log4net/index.html"&gt;log4net&lt;/a&gt; in the whole fixture.&lt;/p&gt;
&lt;p&gt;To intercept unexpected &lt;i&gt;Flush &lt;/i&gt;a possible, easy and quickly, way is an implementation of &lt;span style="color:#2b91af;"&gt;IInterceptor&lt;/span&gt;.&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span style="color:#0000ff;"&gt;private class &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;NoUpdateInterceptor &lt;/span&gt;: &lt;span style="color:#2b91af;"&gt;EmptyInterceptor&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;private readonly &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;IList&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt; invalidUpdates;&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public &lt;/span&gt;NoUpdateInterceptor(&lt;span style="color:#2b91af;"&gt;IList&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt; invalidUpdates)&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.invalidUpdates = invalidUpdates;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public override bool &lt;/span&gt;OnFlushDirty(&lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;entity, &lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;id, &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt;[] currentState, &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt;[] previousState, &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;[] propertyNames, &lt;span style="color:#2b91af;"&gt;IType&lt;/span&gt;[] types)&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;msg = &lt;span style="color:#a31515;"&gt;&amp;quot; FlushDirty :&amp;quot; &lt;/span&gt;+ entity.GetType().FullName;&lt;br /&gt;     log.Debug(msg);&lt;br /&gt;     invalidUpdates.Add(msg);&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;return false&lt;/span&gt;;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public override bool &lt;/span&gt;OnSave(&lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;entity, &lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;id, &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt;[] state, &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;[] propertyNames, &lt;span style="color:#2b91af;"&gt;IType&lt;/span&gt;[] types)&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;msg = &lt;span style="color:#a31515;"&gt;&amp;quot; Save       :&amp;quot; &lt;/span&gt;+ entity.GetType().FullName;&lt;br /&gt;     log.Debug(msg);&lt;br /&gt;     invalidUpdates.Add(msg);&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;return false&lt;/span&gt;;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;public override void &lt;/span&gt;OnDelete(&lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;entity, &lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;id, &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt;[] state, &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;[] propertyNames, &lt;span style="color:#2b91af;"&gt;IType&lt;/span&gt;[] types)&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;msg = &lt;span style="color:#a31515;"&gt;&amp;quot; Delete     :&amp;quot; &lt;/span&gt;+ entity.GetType().FullName;&lt;br /&gt;     log.Debug(msg);&lt;br /&gt;     invalidUpdates.Add(msg);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see I&amp;rsquo;m interested in : unexpected &lt;span style="text-decoration:underline;"&gt;Flush of dirty&lt;/span&gt; instance, unexpected &lt;span style="text-decoration:underline;"&gt;Saves&lt;/span&gt; and unexpected &lt;span style="text-decoration:underline;"&gt;Deletes&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;i&gt;PersistingMappings&lt;/i&gt; is my &amp;ldquo;driver&amp;rdquo; to test each entity. The responsibility of the method is iterate each persistent class known by the &lt;i&gt;SessionFactory&lt;/i&gt; (or the selected in &lt;i&gt;UnexpectedUpdateDeleteOnFetchSpecific&lt;/i&gt; methods), run the test of each entity and reports all issues found.&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span style="color:#0000ff;"&gt;private void &lt;/span&gt;PersistingMappings(&lt;span style="color:#2b91af;"&gt;ICollection&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt; entitiesFilter)&lt;br /&gt;{&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;invalidUpdates = &lt;span style="color:#0000ff;"&gt;new &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt;();&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;nop = &lt;span style="color:#0000ff;"&gt;new &lt;/span&gt;&lt;span style="color:#2b91af;"&gt;NoUpdateInterceptor&lt;/span&gt;(invalidUpdates);&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#2b91af;"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt; entitiesToCheck;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(entitiesFilter == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;)&lt;br /&gt; {&lt;br /&gt;     entitiesToCheck = cfg.ClassMappings.Select(x =&amp;gt; x.EntityName);&lt;br /&gt; }&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;else&lt;br /&gt; &lt;/span&gt;{&lt;br /&gt;     entitiesToCheck = &lt;span style="color:#0000ff;"&gt;from &lt;/span&gt;persistentClass &lt;span style="color:#0000ff;"&gt;in &lt;/span&gt;cfg.ClassMappings&lt;br /&gt;                       &lt;span style="color:#0000ff;"&gt;where &lt;/span&gt;entitiesFilter.Contains(persistentClass.EntityName)&lt;br /&gt;                       &lt;span style="color:#0000ff;"&gt;select &lt;/span&gt;persistentClass.EntityName;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;foreach &lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;entityName &lt;span style="color:#0000ff;"&gt;in &lt;/span&gt;entitiesToCheck)&lt;br /&gt; {&lt;br /&gt;     EntityPersistenceTest(invalidUpdates, entityName, nop);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(invalidUpdates.Count &amp;gt; 0)&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(logError.IsDebugEnabled)&lt;br /&gt;     {&lt;br /&gt;         logError.Debug(&lt;span style="color:#a31515;"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;);&lt;br /&gt;         logError.Debug(&lt;span style="color:#a31515;"&gt;&amp;quot;------ INVALID UPDATES -------&amp;quot;&lt;/span&gt;);&lt;br /&gt;         invalidUpdates.ForEach(x =&amp;gt; logError.Debug(x));&lt;br /&gt;         logError.Debug(&lt;span style="color:#a31515;"&gt;&amp;quot;------------------------------&amp;quot;&lt;/span&gt;);&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt; &lt;span style="color:#2b91af;"&gt;Assert&lt;/span&gt;.AreEqual(0, invalidUpdates.Count, &lt;span style="color:#a31515;"&gt;&amp;quot;Has unexpected updates.&amp;quot;&lt;/span&gt;);&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;To check each persistent entity I&amp;rsquo;m using the &lt;span style="color:#2b91af;"&gt;Configuration&lt;/span&gt;.ClassMappings collection and extracting the &lt;i&gt;EntityName&lt;/i&gt; from the &lt;span style="color:#2b91af;"&gt;PersistentClass&lt;/span&gt;. The use of &lt;i&gt;EntityName&lt;/i&gt; don&amp;rsquo;t mean that I&amp;rsquo;m using the tag &lt;span style="color:#ff0000;"&gt;entity-name&lt;/span&gt; (as you can see in the mapping above).&lt;/p&gt;
&lt;p&gt;The real &amp;ldquo;ghostbuster&amp;rdquo; is:&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span style="color:#0000ff;"&gt;private void &lt;/span&gt;EntityPersistenceTest(&lt;span style="color:#2b91af;"&gt;ICollection&lt;/span&gt;&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&amp;gt; invalidUpdates,&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;entityName, &lt;span style="color:#2b91af;"&gt;IInterceptor &lt;/span&gt;nop)&lt;br /&gt;{&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;const string &lt;/span&gt;queryTemplate = &lt;span style="color:#a31515;"&gt;&amp;quot;select e.{0} from {1} e&amp;quot;&lt;/span&gt;;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;msg = &lt;span style="color:#a31515;"&gt;&amp;quot;s--------&amp;quot; &lt;/span&gt;+ entityName;&lt;br /&gt; log.Debug(msg);&lt;br /&gt;&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;using &lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;s = sessions.OpenSession(nop))&lt;br /&gt; &lt;span style="color:#0000ff;"&gt;using &lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;var &lt;/span&gt;tx = s.BeginTransaction())&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color:#2b91af;"&gt;IList &lt;/span&gt;entityIds = &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;;&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;try&lt;br /&gt;     &lt;/span&gt;{&lt;br /&gt;         &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;queryString = &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;.Format(queryTemplate, DefaultIdName, entityName);&lt;br /&gt;         entityIds = s.CreateQuery(queryString).SetMaxResults(1).List();&lt;br /&gt;     }&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;catch &lt;/span&gt;(&lt;span style="color:#2b91af;"&gt;Exception &lt;/span&gt;e)&lt;br /&gt;     {&lt;br /&gt;         log.Debug(&lt;span style="color:#a31515;"&gt;&amp;quot;Possible METEORITE:&amp;quot; &lt;/span&gt;+ e.Message);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(entityIds != &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;)&lt;br /&gt;     {&lt;br /&gt;         &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(entityIds.Count == 0 || entityIds[0] == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;)&lt;br /&gt;         {&lt;br /&gt;             log.Debug(&lt;span style="color:#a31515;"&gt;&amp;quot;No instances&amp;quot;&lt;/span&gt;);&lt;br /&gt;         }&lt;br /&gt;         &lt;span style="color:#0000ff;"&gt;else&lt;br /&gt;         &lt;/span&gt;{&lt;br /&gt;             &lt;span style="color:#0000ff;"&gt;if &lt;/span&gt;(entityIds.Count &amp;gt; 1)&lt;br /&gt;             {&lt;br /&gt;                 msg = &lt;span style="color:#a31515;"&gt;&amp;quot;&amp;gt;Has &amp;quot; &lt;/span&gt;+ entityIds.Count + &lt;span style="color:#a31515;"&gt;&amp;quot; subclasses&amp;quot;&lt;/span&gt;;&lt;br /&gt;                 log.Debug(msg);&lt;br /&gt;             }&lt;br /&gt;             &lt;span style="color:#0000ff;"&gt;object &lt;/span&gt;entityId = entityIds[0];&lt;br /&gt;             &lt;span style="color:#0000ff;"&gt;try&lt;br /&gt;             &lt;/span&gt;{&lt;br /&gt;                 s.Get(entityName, entityId);&lt;br /&gt;                 &lt;span style="color:#0000ff;"&gt;try&lt;br /&gt;                 &lt;/span&gt;{&lt;br /&gt;                     s.Flush();&lt;br /&gt;                 }&lt;br /&gt;                 &lt;span style="color:#0000ff;"&gt;catch &lt;/span&gt;(&lt;span style="color:#2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br /&gt;                 {&lt;br /&gt;                     &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;emsg = &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;.Format(&lt;span style="color:#a31515;"&gt;&amp;quot;EXCEPTION - Flushing entity [#{0}]: {1}&amp;quot;&lt;/span&gt;, entityId, ex.Message);&lt;br /&gt;                     log.Debug(emsg);&lt;br /&gt;                     invalidUpdates.Add(emsg);&lt;br /&gt;                 }&lt;br /&gt;             }&lt;br /&gt;             &lt;span style="color:#0000ff;"&gt;catch &lt;/span&gt;(&lt;span style="color:#2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br /&gt;             {&lt;br /&gt;                 &lt;span style="color:#0000ff;"&gt;string &lt;/span&gt;emsg = &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;.Format(&lt;span style="color:#a31515;"&gt;&amp;quot;EXCEPTION - Getting [#{0}]: {1}&amp;quot;&lt;/span&gt;, entityId, ex.Message);&lt;br /&gt;                 invalidUpdates.Add(emsg);&lt;br /&gt;                 log.Debug(emsg);&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;         tx.Rollback();&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt; msg = &lt;span style="color:#a31515;"&gt;&amp;quot;e--------&amp;quot; &lt;/span&gt;+ entityName;&lt;br /&gt; log.Debug(msg);&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;The core of the test is:&lt;/p&gt;
&lt;pre class="code"&gt;s.Get(entityName, entityId);&lt;br /&gt;s.Flush();&lt;/pre&gt;
&lt;p&gt;If I Get an entity, from a clear fresh session, without touch the state what I&amp;rsquo;m expect is that the follow Flush don&amp;rsquo;t&amp;nbsp; make absolutely nothing but&amp;hellip; you know&amp;hellip; perhaps there is an ugly &amp;ldquo;ghost&amp;rdquo;. Each &lt;span style="color:#0000ff;"&gt;try&lt;/span&gt;-&lt;span style="color:#0000ff;"&gt;catch&lt;/span&gt;&lt;span style="color:#000000;"&gt; are checking some special situation.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And now lets go to run the &amp;ldquo;ghostbuster&amp;rdquo; in your application. Code available &lt;a href="http://code.google.com/p/unhaddins/source/browse/#svn/HunabKu/src/Ghostbusters/Ghostbusters"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://nhforge.org/aggbug.aspx?PostID=94" width="1" height="1"&gt;</description><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/mapping/default.aspx">mapping</category><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/NHibernate/default.aspx">NHibernate</category><category domain="http://nhforge.org/blogs/nhibernate/archive/tags/Tests/default.aspx">Tests</category></item></channel></rss>