<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Jason Dentler</title>
	
	<link>http://jasondentler.com/blog</link>
	<description>I'm just here for the code</description>
	<lastBuildDate>Thu, 18 Feb 2010 01:02:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/BasiclyEverything" /><feedburner:info uri="basiclyeverything" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Big hairy bugs and other weird looking stuff.</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/rp_5IXZ9Cl4/</link>
		<comments>http://jasondentler.com/blog/2010/02/big-hairy-bugs-and-other-weird-looking-stuff/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 00:52:25 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/2010/02/big-hairy-bugs-and-other-weird-looking-stuff/</guid>
		<description><![CDATA[Yesterday, I spent an embarrassing amount of time searching for a bug. I’m sure this is well-documented somewhere on MSDN. It even generates a compiler warning in some cases. Still, it’s not the behavior one would expect from C#.
When creating lambdas (including LINQ expressions) inside a for each loop, don’t use the iterator variable in [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I spent an embarrassing amount of time searching for a bug. I’m sure this is well-documented somewhere on MSDN. It even generates a compiler warning in some cases. Still, it’s not the behavior one would expect from C#.</p>
<p>When creating lambdas (including LINQ expressions) inside a for each loop, don’t use the iterator variable in your lambda. Let me explain with some code:</p>
<pre class="brush:csharp">foreach (Type controllerType in controllerTypes)
{
     kernel.Bind(controllerType).ToSelf().InRequestScope();
     kernel.Bind&lt;IController&gt;().ToMethod(ctx =&gt; ctx.kernel.Get(controllerType)).Named(GetName(controllerType));
}</pre>
<p>Why am I using a method to get the controller? It just so happens that my AccountController is also a <a href="http://jasondentler.com/blog/2009/11/simple-domain-events/" target="_blank">domain event handler</a> for my AccountNameAlreadyUsed event. This goes back to Ayende&#8217;s tip in my <a href="http://jasondentler.com/blog/2009/11/simple-domain-events/" target="_blank">domain events post</a>: To get a message back to the UI, fire a new event and have the UI listen for it. In this case, I need the UI to complain when the account name they selected is already being used. </p>
<p>In case your mind has wandered to the dark side, throwing exceptions is not an acceptable way of passing messages in an application.</p>
<p>Now, why the odd mappings? Let&#8217;s say I bind IController and IHandle&lt;AccountNameAlreadyUsed&gt; to AccountController in the request scope. It doesn&#8217;t quite work like you would first expect. You will have one instance of AccountController returned for IController and a separate instance for IHandle&lt;AccountNameAlreadyUsed&gt;. </p>
<p>Instead, I’m saying that for each request for an IController, go get an AccountController, essentially delegating the request to the ToSelf() binding. I have a similar ToMethod() lambda binding for IHandle&lt;AccountNameAlreadyUsed&gt;. Since both interface bindings are fulfilled by the ToSelf binding, only one instance of AccountController is created for the request, instead of two.</p>
<p>So, this explains why I need the lambda in the first place. Why didn’t the code above work?</p>
<p>As it turns out, there was some funny business going on underneath the covers between the foreach iterator and the lambda. Here’s the symptom: No matter which “instance” of the lambda was being referenced, inside the lambda, the iterator variable (controllerType) was always the first value that was iterated. No matter which controller I requested, I always got an instance of AccountController, since it happens to be first alphabetically.</p>
<p>Once you realize what’s going on, the fix is simple. Create another variable and use it inside the lambda instead. So, instead of the code above, we get this:</p>
<pre class="brush:csharp">foreach (Type controllerType in controllerTypes)
{
     var controllerType2 = controllerType;
     kernel.Bind(controllerType).ToSelf().InRequestScope();
     kernel.Bind&lt;IController&gt;().ToMethod(ctx =&gt; ctx.kernel.Get(controllerType2)).Named(GetName(controllerType));
}</pre>
<p>If, instead of a lambda, we had a LINQ expression, the compiler would generate a warning about this issue – at least in VB.NET. If I hadn’t already seen that warning from LINQ expressions, I would probably still be bug hunting.</p>
<p>Jason</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/rp_5IXZ9Cl4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2010/02/big-hairy-bugs-and-other-weird-looking-stuff/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2010/02/big-hairy-bugs-and-other-weird-looking-stuff/</feedburner:origLink></item>
		<item>
		<title>February Updates</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/B9xB8S34pkI/</link>
		<comments>http://jasondentler.com/blog/2010/02/february-updates/#comments</comments>
		<pubDate>Fri, 05 Feb 2010 02:49:37 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[NHibernate]]></category>
		<category><![CDATA[Nenverse]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/2010/02/february-updates/</guid>
		<description><![CDATA[I have a few quick announcements.

Fabio has been busy on some really amazing mapping magic.
Aaron Cure’s NHibernate 2.x Beginners Guide from Packt Publishing will be out in May.
I’m making progress on Project Nenverse. The concept is similar to Hibernate’s Envers. I’m not ready to announce it to the world just yet, but you want to [...]]]></description>
			<content:encoded><![CDATA[<p>I have a few quick announcements.</p>
<ul>
<li><a href="http://fabiomaulo.blogspot.com/" target="_blank">Fabio</a> has been busy on some really amazing <a href="http://fabiomaulo.blogspot.com/2010/01/map-nhibernate-using-your-api.html" target="_blank">mapping</a> <a href="http://fabiomaulo.blogspot.com/2010/02/conform-nhibernate-un-mapping.html" target="_blank">magic</a>.</li>
<li>Aaron Cure’s <a href="http://www.packtpub.com/nhibernate-2-x-beginners-guide/book" target="_blank">NHibernate 2.x Beginners Guide</a> from Packt Publishing will be out in May.</li>
<li>I’m making progress on Project <a href="http://code.google.com/p/nenverse/" target="_blank">Nenverse</a>. The concept is similar to Hibernate’s Envers. I’m not ready to announce it to the world just yet, but you want to look over the code or even contribute, it’s out there.</li>
<li>In an effort to avoid meta-blogging at all cost, I won’t mention the cool new stuff over on the right, or the new text up above.</li>
</ul>
<p>That’s it for now.</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/B9xB8S34pkI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2010/02/february-updates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2010/02/february-updates/</feedburner:origLink></item>
		<item>
		<title>Sharpening My C# Skills</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/L5CP3Atsh0I/</link>
		<comments>http://jasondentler.com/blog/2010/01/sharpening-my-c-skills/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 03:34:26 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[C#]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/2010/01/sharpening-my-c-skills/</guid>
		<description><![CDATA[You may have noticed a lack of VB.NET source code on my blog recently. There’s a reason. I’ve always been able to read and write C#. at least enough to order off the menu or ask for directions to the Men’s room. 
The ALT.NET crowd is almost exclusively C#, except where they’ve moved beyond it.&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>You may have noticed a lack of VB.NET source code on my blog recently. There’s a reason. I’ve always been able to read and write C#. at least enough to order off the menu or ask for directions to the Men’s room. </p>
<p>The ALT.NET crowd is almost exclusively C#, except where they’ve moved beyond it.&#160; If I’m going to participate, it’s past time for me to become fluent. Until at least April, I won’t write a single line of VB.NET outside of work. </p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/L5CP3Atsh0I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2010/01/sharpening-my-c-skills/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2010/01/sharpening-my-c-skills/</feedburner:origLink></item>
		<item>
		<title>Exploiting Context_Info for Fun and Audit</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/N5HwUjHt7z8/</link>
		<comments>http://jasondentler.com/blog/2010/01/exploiting-context_info-for-fun-and-audit/#comments</comments>
		<pubDate>Sat, 02 Jan 2010 17:05:22 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[MS SQL Server]]></category>
		<category><![CDATA[NHibernate]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/?p=505</guid>
		<description><![CDATA[This is a continuation of my posts about auditing with triggers. 
The Problem
My previous examples have used system_user as the source of user information in the audit log, which required you to impersonate users down to the database level, that is to open the connection in the context of the application user, not some shared [...]]]></description>
			<content:encoded><![CDATA[<p>This is a continuation of my posts about auditing with triggers. </p>
<h3>The Problem</h3>
<p>My previous examples have used system_user as the source of user information in the audit log, which required you to impersonate users down to the database level, that is to open the connection in the context of the application user, not some shared generic account. There are several reasons why this is not a good idea:</p>
<ol>
<li>You either don’t have per-user credentials appropriate to create SQL server accounts (typical in public-facing web applications), or are unable to do so for various reasons ranging from security concerns to account management workload. </li>
<li>When each user authenticates with their own credentials, this effectively disables connection pooling. Especially in high-volume web scenarios, this will kill performance and put a strain on server resources. </li>
<li>I believe the loss of connection pooling will also burn through your license pool with per-user licensed SQL servers. I may be mistaken on this point, as I haven’t worked with a per-user licensed SQL server for the better part of a decade. </li>
<li>If you are unwilling to use SQL server authentication, you will most likely have to set up Kerberos constrained delegation so that your database server trusts your web server to authenticate users. This is a royal PITA to implement. </li>
</ol>
<h3>The Trouble With Triggers</h3>
<p>As far as input inside a trigger, you only have access to the data being manipulated, system functions, and very little else. You can’t use SELECT statements in triggers either, at least not against normal tables and views. </p>
<h3>SQL Context Info</h3>
<p>Starting with MS SQL Server 2000, you can associate up to 128 bytes of data with the current connection using the SET CONTEXT_INFO command. Starting with SQL 2005, you can use the CONTEXT_INFO function to retrieve this data. This data is available everywhere, including triggers. </p>
<p>If you use connection pooling (and you probably are), this data is not reset when the connection is reused. </p>
<p>To set your context info data, use the <a href="http://msdn.microsoft.com/en-us/library/aa259199(SQL.80).aspx" target="_blank">SET CONTEXT_INFO</a> command like so:</p>
<pre class="brush:sql">DECLARE @Ctx varbinary(128)
SELECT @Ctx = CONVERT(varbinary(128), 'My context data goes here.')
SET CONTEXT_INFO @Ctx</pre>
<p>SET CONTEXT_INFO can only be used with variables and constants, meaning you can’t CONVERT data types or concatenate strings or any other calculation in the same command. </p>
<p>To retrieve the context data, use the <a href="http://msdn.microsoft.com/en-us/library/ms180125(SQL.90).aspx" target="_blank">CONTEXT_INFO</a> function:</p>
<pre class="brush:sql">DECLARE @CtxData varchar(128)
SELECT @CtxData = CONVERT(VarChar(128), CONTEXT_INFO())
PRINT @CtxData</pre>
<p>This will output ‘My context data goes here.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0’ etc. Note – The \0 is ASCII character 0 (NULL). Why? We’re converting the string to fixed-length binary and then converting it back.</p>
<h3>Know Your Roots</h3>
<p><a href="http://jasondentler.com/blog/wp-content/uploads/2010/01/image.png"><img style="border-right-width: 0px; margin: 4px 10px 4px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" align="left" src="http://jasondentler.com/blog/wp-content/uploads/2010/01/image_thumb.png" width="137" height="59" /></a> There was a time when programmers weren’t protected from the fact that strings are just chunks of memory. Just like programmers in the old days, you have two options: Pascal strings or C strings. You can stuff a length byte at the beginning of your context as in Pascal, or you can search for the null termination of your string as in the C language. </p>
<p>Also, because nchar and nvarchar are UNICODE UCS-2 strings, they use two bytes (of your 128 maximum) per character. char and varchar only use one byte per character, but the character set is limited. It’s a trade-off. </p>
<h3>How does this help me?</h3>
<p>We can use context info data to get information from our application in to our audit triggers. For example, we can get the current user name from our application or evem some identifying information about the location in our application, such as the name of the controller and action that triggered the event. Just be conscious of the 128 byte limitation. It&#8217;s probably better to store an id for more information such as the id of the user account, or an id that references some lookup table with application locations.</p>
<h3>The Code, Sir. </h3>
<p>We will override the GetConntion() method of DriverConnectionProvider so that every NHibernate connection will have the current username stored in the context.</p>
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Connection;
using System.Data;

namespace NHibernate.Connection
{
    public class ContextDriverConnectionProvider : DriverConnectionProvider
    {

        public override System.Data.IDbConnection GetConnection()
        {
            var conn = base.GetConnection();
            SetContext(conn);
            return conn;
        }

        private const string CONTEXT_SQL = &quot;declare @Length tinyint\ndeclare @Ctx varbinary(128)\nselect @Length = len(@username)\nselect @Ctx = convert(binary(1), @Length) + convert(varbinary(127), @username)\nset context_info @Ctx&quot;;

        protected virtual void SetContext(IDbConnection conn)
        {
            IDbCommand cmd = conn.CreateCommand();
            IDbDataParameter param = cmd.CreateParameter();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = CONTEXT_SQL;

            param.ParameterName = &quot;@username&quot;;
            param.DbType = DbType.AnsiString;
            param.Size = 127;
            param.Value = System.Environmnt.UserName;
            cmd.Parameters.Add(param);

            cmd.ExecuteNonQuery();
        }

    }
}</pre>
<p>This will run the following SQL code when NHibernate opens a SQL connection. Note that @username is a variable defined as a parameter on our IDbCommand. Also, I chose to use Pascal strings with the length stored in the first byte.</p>
<pre class="brush:sql">declare @Length tinyint
declare @Ctx varbinary(128)
select @Length = len(@username)
select @Ctx = convert(binary(1), @Length) + convert(varbinary(127), @username)
set context_info @Ctx</pre>
<p>We&#8217;ll alter our trigger code to get the username from the context instead of system_user.</p>
<pre class="brush:sql">set nocount on
declare @Username varchar(127)
declare @Length tinyint
declare @Ctx varbinary(128)
select @Ctx = CONTEXT_INFO()
select @Length = convert(tinyint, substring(@Ctx, 1, 1))
select @Username = convert(varchar(127), substring(@Ctx, 2, 1 + @Length))
if (@Username is null) select @Username = SYSTEM_USER</pre>
<h3>Security Implications</h3>
<p>I am not a security expert. I am certainly not a SQL security expert. However, I believe this method is secure for most scenarios. In my opinion, this is at least as secure as auditing through NHibernate interceptors and events. </p>
<p>Here’s a few ways to circumvent the system, and how you can prevent that from happening:</p>
<ul>
<li><strong>Manipulate the audit logs directly.</strong> Obviously, in any audit scenario, you should set permission on your audit tables to make them select and insert only. </li>
<li><strong>Alter the context_info data directly.</strong> A user overwrites their own context_info with that of another user, effectively blaming their actions on someone else. This can be done through a SQL injection attack some other security breach. If you go on vacation with the front door open, don’t be surprised when your house is robbed. </li>
<li><strong>Fail to set context_info.</strong> Again, this requires some other security breach. You may also change your trigger code to ROLLBACK TRANSACTION and RAISERROR when context_info is not set. This will force a rollback of the transaction (or in the case of no explicit transaction, the SQL statement that caused the trigger), abort the remainder of the SQL batch, and report an error. The approach requires everyone, including your DBA, to set an appropriate context_info before altering any audited data. </li>
</ul>
<p>This is certainly my worst blog post this year, but don’t worry. I can do worse. </p>
<p>- Jason</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/N5HwUjHt7z8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2010/01/exploiting-context_info-for-fun-and-audit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2010/01/exploiting-context_info-for-fun-and-audit/</feedburner:origLink></item>
		<item>
		<title>Generate Audit Triggers from NHibernate v2</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/TLcShFCub8s/</link>
		<comments>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate-v2/#comments</comments>
		<pubDate>Thu, 31 Dec 2009 21:55:57 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[MS SQL Server]]></category>
		<category><![CDATA[NHibernate]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/?p=490</guid>
		<description><![CDATA[In my December 23rd post, I showed you how to generate audit tables and triggers from your NHibernate mappings. Since then, I had an amazing conversation with Fabio Maulo, NHibernate project owner, where among other things, he showed me a much better way to accomplish the same task.
In my last post, we used the NHibernate [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate/" target="_blank">December 23rd post</a>, I showed you how to generate audit tables and triggers from your <a href="http://nhforge.org" target="_blank">NHibernate</a> mappings. Since then, I had an amazing conversation with <a href="http://fabiomaulo.blogspot.com/" target="_blank">Fabio Maulo</a>, NHibernate project owner, where among other things, he showed me a much better way to accomplish the same task.</p>
<p>In my last post, we used the NHibernate mapping as source data to create our script by hand. With this version, we’re going to take it a step further. When you add an IAuxilaryDatabaseObject to the mappings, it will be included in the ddl output of SchemaExport. These have no effect outside of SchemaExport. It’s a simple way to include additional items in your database setup scripts.</p>
<p>In our main program, we set up some additional columns to hold our audit data – user, timestamp, and action. These columns will be added to each audit table. Then, we use TriggerStrategy to add auxiliary database objects to our configuration.</p>
<pre class="brush:csharp">        static void AddAuditing(Configuration cfg)
        {
            var columns = new List&lt;auditcolumn&gt;();
            columns.Add(new AuditColumn() {
                Name = &quot;AuditUser&quot;,
                SqlType = &quot;sysname&quot;,
                IsNullable = false,
                ValueFunction = delegate(TriggerActions actions)
                {
                    return &quot;system_user&quot;;
                }
            });

            columns.Add(new AuditColumn()
            {
                Name = &quot;AuditTimestamp&quot;,
                Value = new SimpleValue() { TypeName = NHibernate.NHibernateUtil.DateTime.Name },
                IsNullable = false,
                IncludeInPrimaryKey = true,
                ValueFunction = delegate(TriggerActions actions)
                {
                    return &quot;getdate()&quot;;
                }
            });

            columns.Add(new AuditColumn()
            {
                Name = &quot;AuditOperation&quot;,
                Value = new SimpleValue() { TypeName = NHibernate.NHibernateUtil.AnsiChar.Name },
                Length = 1,
                IsNullable = false,
                ValueFunction = delegate(TriggerActions actions)
                {
                    switch (actions)
                    {
                        case TriggerActions.Insert:
                            return &quot;'I'&quot;;
                        case TriggerActions.Update:
                            return &quot;'U'&quot;;
                        case TriggerActions.Delete:
                            return &quot;'D'&quot;;
                        default:
                            throw new ApplicationException(&quot;Triggers handling multiple event types (INSERT, UPDATE, DELETE) are unsupported. &quot;);
                    }
                }
            });

            new NHibernate.Audit.TriggerStrategy(
                new NHibernate.Audit.AuditTableBuilder(),
                new NHibernate.Audit.MsSqlTriggerBuilder(),
                columns).Configure(cfg);
	}</pre>
<p>Trigger strategy simply hands off the work of audit table builder and trigger builder.</p>
<p>Audit table builder creates an audit table object for each table already in your mapping. It is also responsible for calculating the name of each audit table. Yes, the naming should probably be pulled in to a separate class, but oh well.</p>
<p>Audit table uses NHibernate&#8217;s table object internally to create a duplicate of each table, plus the additional audit columns, then exposes the drop/create scripts for this table object as an IAuxilaryDatabaseObject. I&#8217;ve ommitted some of the name-related static below.</p>
<pre class="brush:csharp">using System.Collections.Generic;
using NHibernate.Mapping;

namespace NHibernate.Mapping
{

    public class AuditTable : AbstractAuxiliaryDatabaseObject
    {

        public AuditTable(string Name, Table DataTable, IEnumerable&lt;AuditColumn&gt; AuditColumns)
        {
            dataTable = DataTable;
            auditTable = new Table();
            auditColumns = AuditColumns;
            BuildAuditTable(Name);
        }

        protected readonly Table dataTable;
        protected readonly Table auditTable;
        protected readonly IEnumerable&lt;AuditColumn&gt; auditColumns;

        protected virtual void BuildAuditTable(string Name)
        {
            auditTable.Catalog = dataTable.Catalog;
            auditTable.Schema = dataTable.GetQuotedSchema();
            auditTable.Name = Name;

            foreach (Column column in dataTable.ColumnIterator)
                CopyColumn(column);

            CopyPrimaryKey(dataTable);

            AddAuditColumns();
        }

        protected virtual void CopyColumn(Column column)
        {
            auditTable.AddColumn((Column)column.Clone());
        }

        protected virtual void CopyPrimaryKey(Table dataTable)
        {
            if (dataTable.PrimaryKey != null)
            {
                var pk = new PrimaryKey();
                auditTable.PrimaryKey = pk;
                foreach (Column c in dataTable.PrimaryKey.ColumnIterator)
                {
                    Column auditTableColumn = auditTable.ColumnIterator.WithName(c.Name);
                    pk.AddColumn(auditTableColumn);
                }
            }
            else
            {
                throw new System.NotSupportedException(&quot;Tables without primary keys are not supported.&quot;);
            }
        }

        protected virtual void AddAuditColumns()
        {
            foreach (AuditColumn source in auditColumns)
            {
                Column dest = (Column)source.Clone();
                auditTable.AddColumn(dest);
                if (source.IncludeInPrimaryKey)
                    auditTable.PrimaryKey.AddColumn(dest);
            }
        }

        public override string SqlCreateString(NHibernate.Dialect.Dialect dialect, NHibernate.Engine.IMapping p, string defaultCatalog, string defaultSchema)
        {
            return auditTable.SqlCreateString(dialect, p, defaultCatalog, defaultSchema);
        }

        public override string SqlDropString(NHibernate.Dialect.Dialect dialect, string defaultCatalog, string defaultSchema)
        {
            return auditTable.SqlDropString(dialect, defaultCatalog, defaultSchema);
        }
    }

}</pre>
<p>That handles our audit tables. Now let’s look at the triggers.</p>
<p>Abstract trigger builder builds insert, update, and delete triggers for each table. You should override BuildTriggerBody to customize the body of your trigger. I’ve created MsSqlTriggerBuilder to output my preferred trigger code. </p>
<p>NHibernate dialects have no concept of triggers and lack the required strings to build a DBMS-specific CREATE TRIGGER statements. Instead, you specify a trigger type inherited from AbstractTrigger specific to your database to build the CREATE TRIGGER statement. I’ve included MsSqlTrigger.</p>
<p>&#160;</p>
<p><a href="http://jasondentler.com/blog/wp-content/uploads/2009/12/Triggers1.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="Triggers" border="0" alt="Triggers" src="http://jasondentler.com/blog/wp-content/uploads/2009/12/Triggers_thumb1.png" width="578" height="821" /></a></p>
<p>&#160;</p>
<p>That’s all folks. You can grab the code from my SVN repository at <a href="http://basiclyeverything.googlecode.com/svn/trunk/AuditLogExample/">http://basiclyeverything.googlecode.com/svn/trunk/AuditLogExample/</a></p>
<p>Since I’m still using the system_user function to get the current user, you will still need to impersonate the user all the way down to the database level. I’ve found a way around this, and I will show it to you next time. Until then, Happy New Year.</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/TLcShFCub8s" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate-v2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate-v2/</feedburner:origLink></item>
		<item>
		<title>Generate Audit Triggers From NHibernate</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/f1vVP-brc-M/</link>
		<comments>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate/#comments</comments>
		<pubDate>Wed, 23 Dec 2009 06:08:07 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[MS SQL Server]]></category>
		<category><![CDATA[NHibernate]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/?p=472</guid>
		<description><![CDATA[Last week, Fabio was showing me the Parallel Models pattern and explaining some of the work he’s doing with it. It is a VERY powerful concept.
What I’m going to show you today is the poor-mans version. It certainly doesn’t qualify as parallel model, but it’s pretty good audit logging if you meet the prerequisites.
The Problem
You [...]]]></description>
			<content:encoded><![CDATA[<p>Last week, <a href="http://fabiomaulo.blogspot.com/" target="_blank">Fabio</a> was showing me the <a href="http://martinfowler.com/eaaDev/ParallelModel.html" target="_blank">Parallel Models</a> pattern and explaining some of the work he’s doing with it. It is a VERY powerful concept.</p>
<p>What I’m going to show you today is the poor-mans version. It certainly doesn’t qualify as parallel model, but it’s pretty good audit logging if you meet the prerequisites.</p>
<h4>The Problem</h4>
<p>You need some audit logs for your current application, but you’re behind. In fact, you started the project already a month behind schedule. This needs to be quick and reliable. It’d be great if it didn’t intrude on the application, because you’ve got enough rattling around in your head already.</p>
<p>It sounds like you want SQL trigger audit logging. By the way, let’s say you’re using MS SQL Server. Let’s also say your application impersonates the user all the way down to the database level. That means your connection string says integrated security=SSPI or your dba enjoys the extra work of maintaining a sql login for each user. Those are prerequisites for this approach.</p>
<h4>What’s a trigger?</h4>
<blockquote><p>A trigger is a special kind of stored procedure that automatically executes when an event occurs in the database server.      <br />- <a href="http://msdn.microsoft.com/en-us/library/ms189799.aspx" target="_blank">CREATE TRIGGER, MSDN</a></p>
</blockquote>
<p>If tables are the data structures in your database and stored procedures are the methods, then triggers are the event handlers. We’re concerned with three specific events – INSERT, UPDATE, and DELETE. We want to log each time those events happen to rows in our tables.</p>
<p>Trigger auditing has been around since at least the 1990s – since before SQL Server was even worth a damn. That’s MSSQL 6.5 folks. It’s sturdy and well-proven, although poorly implemented triggers will bring your server to a screeching halt. Fair warning – mine may be one of them.</p>
<p>We want to know what the data was before the change, what the data is now, who changed it, and when.</p>
<p>When you insert records in a table, the insert trigger for that table executes. The same goes for update and delete.</p>
<p>Triggers have two imaginary tables to help us with this task – inserted and deleted. Inside a trigger, <a href="http://msdn.microsoft.com/en-us/library/ms191300.aspx" target="_blank">they mostly work like real tables</a>. Inserted always has the new data. Deleted always has the old data (from before the change.) So, in the case of an INSERT trigger, deleted is empty. For a DELETE trigger, inserted is always empty, since there is no new data with a deletion. For an UPDATE trigger, they both have data.</p>
<p>How does this help us? Well, suppose we have want to audit a table called Person. Let’s make a table called Audit_Person with the exact same columns as Person. When we delete data from Person, our DELETE trigger on Person will run. Inside our trigger, the deleted table has all of the rows that were just deleted from Person. We can copy that data to our audit table so we have a record of it by doing INSERT INTO Audit_Person SELECT * FROM deleted. Since the columns match up exactly, it works.</p>
<p>Wait a minute though. We need to know who deleted that data and when. Oh, and we need some way to indicate the different events.</p>
<p>Lucky for us, there’s some easy functions in SQL. system_user will give us the login of the current user – either a sql account name or a windows account name. Of course, GetDate() will tell us the current time and we’ll just write that to the table.</p>
<p>Let’s go ahead and write out a full trigger.</p>
<pre class="brush:sql">CREATE TRIGGER Person_onInsert ON Person FOR INSERT AS
SET NOCOUNT ON
INSERT INTO Audit_Person SELECT *, system_user, getdate(), 'I' FROM Inserted</pre>
<p>Oh yeah, we need to add some fields in Audit_Person to hold the extra stuff. system_user is a custom sql type called sysname. It’s really nvarchar(256). Of course, getdate() is a datetime. Finally, we’re passing a capital letter I (for Inserted) as the last field, so we’ll need to make that a char(1) type. </p>
<p>The SET NOCOUNT ON command is there to suppress the extra (X rows affected) messages that SQL spits out. You’ll know what I’m talking about if you’ve ever clicked on the Messages tab after running a query. </p>
<p>So, that’s the audit logging. Now we just need to create an audit table and 3 triggers for each table in your application.</p>
<h4>What does that get us?</h4>
<p>Suppose we set up the audit table and all 3 triggers, then ran these 5 SQL commands:</p>
<pre class="brush:sql">INSERT INTO [Person] VALUES (NEWID(), 'Jason 1')
INSERT INTO [Person] VALUES (NEWID(), 'Jason 2')
INSERT INTO [Person] VALUES (NEWID(), 'Jason 3')
UPDATE Person SET Name = 'Jason 4'
DELETE FROM Person </pre>
<p>Our Audit_Person table would have these rows: (click the picture for 100% size)</p>
<p><a href="http://jasondentler.com/blog/wp-content/uploads/2009/12/image3.png" target="_blank"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://jasondentler.com/blog/wp-content/uploads/2009/12/image_thumb3.png" width="554" height="156" /></a></p>
<p>&#160;</p>
<h4>Being Lazy</h4>
<p>You’ve got 200 tables? Well, you’d better get to typing then. </p>
</p>
<p>OK. Just like you can generate a SQL script using <a href="http://nhforge.org" target="_blank">NHibernate</a>.Tool.hbm2ddl.SchemaExport, we can generate a script to build our audit tables and triggers from the mappings.</p>
<p>Now, we’re not going to parse out the XML in the hbm files. We’re going to read the proposed table schema – what SchemaExport will give you – directly from the NHibernate configuration object. This is the same way SchemaExport does it’s magic.</p>
<p>Here’s the code:</p>
<pre class="brush:csharp">using System.IO;
using NHibernate.Cfg;
using NHibernate.Mapping;
using NHibernate.AdoNet.Util;

namespace AuditLogExample
{

    public class AuditScripter
    {

        public const string USER_DEFAULT = &quot;system_user&quot;;
        public const string TIMESTAMP_DEFAULT = &quot;getdate()&quot;;

        private enum Operations
        {
            Insert,
            Update,
            Delete
        }

        public AuditScripter(Configuration Configuration)
        {
            cfg = Configuration;
            mapping = cfg.BuildMapping();
            dialect = NHibernate.Dialect.Dialect.GetDialect(cfg.Properties);
            formatter = (NHibernate.Util.PropertiesHelper.GetBoolean(NHibernate.Cfg.Environment.FormatSql, cfg.Properties, true) ? FormatStyle.Ddl : FormatStyle.None).Formatter;
        }

        private readonly NHibernate.Dialect.Dialect dialect;
        private readonly Configuration cfg;
        private readonly NHibernate.Engine.IMapping mapping;
        private readonly IFormatter formatter;

        public void Execute(System.IO.TextWriter script)
        {
            var mappings = cfg.CreateMappings(dialect);
            ScriptObjectsForTables(mappings, script);
        }

        private void ScriptObjectsForTables(Mappings mappings, TextWriter script)
        {
            var tables = mappings.IterateTables;
            var auditTableBuilder = new AuditTableBuilder();
            foreach (Table table in tables)
            {
                if (table.IsPhysicalTable)
                {
                    var auditTable = auditTableBuilder.BuildAuditTable(table);
                    ScriptObjectsForTable(table, auditTable, script);
                }
            }
        }

        private void ScriptObjectsForTable(Table dataTable, Table auditTable, TextWriter script)
        {
            ScriptAuditTable(auditTable, script);
            ScriptInsertTrigger(dataTable, auditTable, script);
            ScriptUpdateTrigger(dataTable, auditTable, script);
            ScriptDeleteTrigger(dataTable, auditTable, script);
        }

        private void ScriptAuditTable(Table auditTable, TextWriter script)
        {
            string defaultCatalog = NHibernate.Util.PropertiesHelper.GetString(NHibernate.Cfg.Environment.DefaultCatalog, cfg.Properties, null);
            string defaultSchema = NHibernate.Util.PropertiesHelper.GetString(NHibernate.Cfg.Environment.DefaultSchema, cfg.Properties, null);

            string sqlCreateString = auditTable.SqlCreateString(dialect, mapping, defaultCatalog, defaultSchema);

            script.WriteLine(formatter.Format(sqlCreateString));

            var comments = auditTable.SqlCommentStrings(dialect, defaultCatalog, defaultSchema);

            foreach (string line in comments)
                script.Write(formatter.Format(line));

            script.WriteLine(formatter.Format(&quot;go&quot;)); // create trigger statements must in their own batch
        }

        private void ScriptInsertTrigger(Table dataTable, Table auditTable, TextWriter script)
        {
            ScriptTrigger(string.Format(&quot;{0}_onInsert&quot;, dataTable.Name),
                &quot;insert&quot;, &quot;'I'&quot;, &quot;inserted&quot;,
                dataTable, auditTable, script);
        }

        private void ScriptUpdateTrigger(Table dataTable, Table auditTable, TextWriter script)
        {
            ScriptTrigger(string.Format(&quot;{0}_onUpdate&quot;, dataTable.Name),
                &quot;update&quot;, &quot;'U'&quot;, &quot;inserted&quot;,
                dataTable, auditTable, script);
        }

        private void ScriptDeleteTrigger(Table dataTable, Table auditTable, TextWriter script)
        {
            ScriptTrigger(string.Format(&quot;{0}_onDelete&quot;, dataTable.Name),
                &quot;delete&quot;, &quot;'D'&quot;, &quot;deleted&quot;,
                dataTable, auditTable, script);
        }

        /// &lt;summary&gt;
        /// Scripts out a create trigger sql statement
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;triggerName&quot;&gt;The name of the trigger&lt;/param&gt;
        /// &lt;param name=&quot;triggerOperation&quot;&gt;insert, update, or delete&lt;/param&gt;
        /// &lt;param name=&quot;auditOperationValue&quot;&gt;Value to insert for AuditOperation field&lt;/param&gt;
        /// &lt;param name=&quot;triggerTable&quot;&gt;The &quot;built-in&quot; trigger table with affected data - inserted or deleted&lt;/param&gt;
        /// &lt;param name=&quot;dataTable&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;auditTable&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;script&quot;&gt;&lt;/param&gt;
        private void ScriptTrigger(string triggerName, string triggerOperation,
            string auditOperationValue, string triggerTable,
            Table dataTable, Table auditTable, TextWriter script)
        {

            Table trigger; // Just to calculate the name of the trigger
            trigger = new Table(triggerName);
            trigger.Schema = dataTable.Schema;
            trigger.Catalog = dataTable.Catalog;
            trigger.IsQuoted = dataTable.IsQuoted;

            string dataTableName = dataTable.GetQualifiedName(dialect);
            string auditTableName = auditTable.GetQualifiedName(dialect);
            triggerName = trigger.GetQualifiedName(dialect);

            string triggerHeading;
            string triggerBody;

            triggerHeading = string.Format(&quot;create trigger {0} on {1} for {2} as&quot;, triggerName, dataTableName, triggerOperation);
            triggerBody = string.Format(&quot;insert into {0} select *, {1}, {2}, {3} from {4}&quot;, auditTableName, TIMESTAMP_DEFAULT, USER_DEFAULT, auditOperationValue, triggerTable);

            script.Write(formatter.Format(triggerHeading));
            script.Write(formatter.Format(&quot;set nocount on&quot;));
            script.Write(formatter.Format(triggerBody));
            script.WriteLine(formatter.Format(&quot;go&quot;)); // create trigger statements must in their own batch

        }

    }
}</pre>
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Mapping;

namespace AuditLogExample
{
    public class AuditTableBuilder
    {

        public Table BuildAuditTable(Table dataTable)
        {
            Table auditTable = new Table(string.Format(&quot;Audit_{0}&quot;,dataTable.Name));
            auditTable.Catalog = dataTable.Catalog;
            auditTable.Schema = dataTable.Schema;
            auditTable.IsQuoted = dataTable.IsQuoted;

            foreach(Column column in dataTable.ColumnIterator)
                CopyColumn(column, auditTable);

            CopyPrimaryKey(dataTable, auditTable);
            AddAuditColumns(auditTable);

            return auditTable;
        }

        private void CopyColumn(Column sourceColumn, Table auditTable)
        {
            Column newColumn = (Column)sourceColumn.Clone();
            auditTable.AddColumn(newColumn);
        }

        private void CopyPrimaryKey(Table dataTable, Table auditTable)
        {
            var pk = new PrimaryKey();
            foreach (Column sourceColumn in dataTable.PrimaryKey.ColumnIterator)
            {
                pk.AddColumn(auditTable.Column(sourceColumn.Name));
            }
            auditTable.PrimaryKey = pk;
        }

        private void AddAuditColumns(Table auditTable)
        {

            Column ts = new Column(&quot;AuditTimestamp&quot;);
            ts.SqlType = &quot;datetime&quot;;
            ts.IsNullable = false;
            ts.DefaultValue = AuditScripter.TIMESTAMP_DEFAULT;
            auditTable.AddColumn(ts);
            auditTable.PrimaryKey.AddColumn(ts);

            Column user = new Column(&quot;AuditUser&quot;);
            user.Length = 256; // system_user returns a sysname, which is really nvarchar(256)
            user.SqlType = &quot;sysname&quot;;
            user.IsNullable = false;
            user.DefaultValue = AuditScripter.USER_DEFAULT;
            auditTable.AddColumn(user);
            auditTable.PrimaryKey.AddColumn(user);

            Column op = new Column(&quot;AuditOperation&quot;);
            op.SqlType = &quot;char&quot;;
            op.Length = 1;
            op.IsNullable = false;
            auditTable.AddColumn(op);

        }

    }
}</pre>
<pre class="brush:csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate.Mapping;

namespace NHibernate.Mapping
{
    public static class TableExtensions
    {

        public static Column Column(this Table Table, string ColumnName)
        {
            return Table.ColumnIterator.Where(c =&gt; c.Name == ColumnName).FirstOrDefault();
        }

    }
}</pre>
<p>I have a couple of gotchas and details to explain.</p>
<p>First, this works for me. I’m sure there are edge cases where everything goes horribly wrong. Please let me know when you find them.</p>
<p>Next, CREATE TRIGGER commands are VERY anti-social. They refuse to share a batch with any other statements. That’s why there’s a “go” between everything. In query analyzer (or the SSMS query window now), it starts a fresh batch. </p>
<p>If you know your SQL, this should look pretty simple. Pass an NHibernate configuration to the constructor of AuditScripter, then call Execute. The code loops through all of the physical tables in your configuration, first building a nearly-identical audit table, then creating the three triggers. </p>
<p>The primary key of the audit table has all the fields from the primary key of your data table, plus AuditUser and AuditTimestamp. A single user cannot alter the same record in two different ways at the exact same moment. In fact, I don’t think any two users could, but I’ll let it slide. </p>
<p>The code could probably be improved to automatically include some common indexes on the audit tables.</p>
<p>I’m sure this is the worst possible way to generate code, but I wanted something non-tool specific. I thought about T4 templates since everyone probably has Visual Studio, but this was quicker than figuring out how to get a full NHibernate configuration in to a T4 template. </p>
<h4>Expanding the idea</h4>
<p>You can definitely build on this idea to generate all sorts of repetitive code based on your NHibernate schema. For instance, if you’re unlucky enough to work in a shop with one of those stored-proc crazed database Nazis, this should help quite a bit. You can use this same idea to generate thousands of CRUD stored procedures in a few minutes. </p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/f1vVP-brc-M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/12/generate-audit-triggers-from-nhibernate/</feedburner:origLink></item>
		<item>
		<title>Views and Databases Don’t Mix</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/Nx0pv_1OyDc/</link>
		<comments>http://jasondentler.com/blog/2009/12/views-and-databases-dont-mix/#comments</comments>
		<pubDate>Mon, 21 Dec 2009 19:22:38 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[NHibernate]]></category>
		<category><![CDATA[ViewModel]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/2009/12/views-and-databases-dont-mix/</guid>
		<description><![CDATA[The Problem
In my MVC applications, I sometimes bind my views directly to NHibernate entities. a lot. like always. I also do session-per-request. 
I don’t remember what prompted this thought – probably Twitter – but it seemed questionable to access the database from the view by loading a lazy collection. I knew you shouldn’t put database [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem</h2>
<p>In my MVC applications, I sometimes bind my views directly to NHibernate entities. a lot. like always. I also do session-per-request. </p>
<p>I don’t remember what prompted this thought – probably Twitter – but it seemed questionable to access the database from the view by loading a lazy collection. I knew you shouldn’t put database access <strong><em>code</em></strong> in the view, but this would just be a property, nothing complex at all. <a href="http://ayende.com/Blog/" target="_blank">Ayende</a> set me straight. </p>
<blockquote><p>You should avoid it. It is dangerous to do loading in the     <br />view, it is subject to too many changes.</p>
</blockquote>
<p>How do we prevent it? Of course, the simple answer is “just don’t do it.” Anything more quickly falls in the category of protecting you from yourself, of which I’m usually not a fan. Still, this is easy to overlook. The <a href="http://haacked.com/archive/2005/08/03/DoesMortKnowWeAreTalkingSmackAboutHimBehindHisBack.aspx" target="_blank">Morts</a> of the world – and me &#8211; will fall in to this trap easily. </p>
<h2>ViewModel Solution</h2>
<p>What if we had some sort of model, but instead of being generic for the entire application, it only dealt with the concerns of a specific view. Oh, and what if we called it something weird like viewmodel. </p>
<p>This is the best way to avoid problems. Instead of binding directly to an entity, each view should have a corresponding viewmodel class, that contains all of the data being pushed down to the view as well as all the data collected by the view from the user. It’s a POCO specific to the view, with no association to the NHibernate session, so it can’t accidentally load up some data. </p>
<p>You can easily test your viewmodels. Since they also make your views pretty darn stupid, you can maybe skip some of that time-consuming UI testing. Well, skip it safely this time. ViewModels also work well for all that validation attribute markup. Just sayin’.</p>
<p>This isn’t the point of my post today, and what I’ve described isn’t the traditional Model-View-ViewModel. It’s some weird hybrid of separated presentation patterns. You can read more about view models <a href="http://jeffhandley.com/archive/2008/10/21/viewmodel-pattern.aspx" target="_blank">here</a> and <a href="http://jeffhandley.com/archive/2008/10/27/helloworld.viewmodel.aspx" target="_blank">here</a> and on <a href="http://jfromaniello.blogspot.com/2009/08/nhibernate-and-wpf-viewmodels-and-views.html" target="_blank">Jose’s Chinook Media Manager application series</a>. </p>
<h2>Exploding View Solution</h2>
<p><a href="http://jasondentler.com/blog/wp-content/uploads/2009/12/image.png"><img style="border-bottom: 0px; border-left: 0px; margin: 4px 20px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" align="left" src="http://jasondentler.com/blog/wp-content/uploads/2009/12/image_thumb.png" width="125" height="225" /></a> While I can’t say it’s worse than something silently breaking, I don’t like the idea of something bad silently working. Fragile code only works until the worst possible moment.</p>
<p>So, without going for a full ViewModel implementation, I thought it would be a good thing (or at least slightly better) to make NHibernate throw an exception when we hit the DB in the view so we have to fix it now while we’re debugging instead of later. I initially had thoughts about interceptors or connection providers and all sorts of craziness. Ayende’s answer is simply to close the session at the controller boundary. </p>
<p>How do we implement this? We close the session between the controller action and the view. So, instead of session per (the entire) request, we trim the scope at the end just a bit. </p>
<p>Let’s take a high-level look at a chunk of the <a href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> lifecycle:</p>
<ol>
<li>A controller action is chosen based on the request and action filters like HttpGet or HttpPost.</li>
<li>The action (the actual controller method) executes, returning an action result – a ViewResult, RedirectToActionResult, ContentResult, or some other built-in or custom action result.</li>
<li>The action result executes. In the case of a ViewResult, the view is rendered down to actual HTML and written out to the response stream. </li>
</ol>
<p>We need to close down the session between #2 and #3. Lucky for us, every controller has an overloadable / overridable method called OnResultExecuting. This method gets called just before the action result is executed. We can simply override this method in our application’s base controller class. You have one of those, right? They’re handy for all sorts of stuff.</p>
<p>Just close down any session we may have open inside OnResultExecuting. Considering the references, this takes a little bit of plumbing, but I’ll leave that up to you since it’s dependent on your method of opening and tracking NHibernate sessions through the request. </p>
</p>
<h4>One last thing</h4>
<p>Maybe in a later version of <a href="http://nhprof.com" target="_blank">NH Profiler</a>, the problem I described today will trigger an alert. Maybe not. </p>
<p>If you’re using <a href="http://nhforge.org" target="_blank">NHibernate</a> without NH Profiler, you’re either writing substandard code or wasting time – probably both. If you don’t believe me, download a trial and see what it tells you about your last NHibernate app.</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/Nx0pv_1OyDc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/12/views-and-databases-dont-mix/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/12/views-and-databases-dont-mix/</feedburner:origLink></item>
		<item>
		<title>Authentication, Impersonation, and Dynamic NHibernate Connection Strings</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/AA1hEXVDM9A/</link>
		<comments>http://jasondentler.com/blog/2009/11/authentication-impersonation-and-dynamic-nhibernate-connection-strings/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 03:27:32 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[NHibernate]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Impersonation]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/2009/11/authentication-impersonation-and-dynamic-nhibernate-connection-strings/</guid>
		<description><![CDATA[A web site I recently worked on has two major parts. The first is internet-facing and allows customers to create data records. The other is for internal use to allow office staff to alter data created by the customer. 
SQL Audit Setup
The database uses MS SQL Server trigger-based audit logs to record data changes with [...]]]></description>
			<content:encoded><![CDATA[<p>A web site I recently worked on has two major parts. The first is internet-facing and allows customers to create data records. The other is for internal use to allow office staff to alter data created by the customer. </p>
<h4>SQL Audit Setup</h4>
<p>The database uses MS SQL Server trigger-based audit logs to record data changes with a username and timestamp. They are literally for audit purposes, and not used in the application. Every database table has a matching Audit table with exactly the same fields plus a username, change type, and timestamp field. </p>
<p>Only updates and deletes trigger an entry in the audit log. The data recorded in the log represents the record prior to the change. The username field is defaulted to SYSTEM_USER, a SQL server specific variable that returns the current account name associated with the connection. This can be either a SQL server account or a windows account. Change type is a single character U or D depending on the action being performed. Timestamp is a datetime field defaulted to GETDATE(). </p>
<p>In order to associate audit log records with a particular username, all updates and deletes must run in the context of the user.</p>
<h4>Authentication and Impersonation</h4>
<p>Customers do not log in to the website. There is no authentication. When a customer interacts with the database, we used a generic SQL account specified in the connection string. All customer changes are recorded with the name of this generic account.</p>
<p>When the office staff uses the website, they are required to authenticate with their windows accounts. This account is impersonated all the way to the database so the audit log will record the specific user name. </p>
<p>There are several ways to achieve this impersonation. The option I chose to use is basic authentication. Over SSL (an https website) this is just as secure as any other data. The primary drawback is that users will get the standard, generic popup log in box. Since the username and password are transmitted to the web server, the web server can easily impersonate this user to any resource, including SQL, even on another server. </p>
<p>You can also run Windows authentication. This was referred to as NTLM back in the day. The benefit of this over basic authentication is that Internet Explorer will authenticate with the web server automatically using the current user’s windows credentials. Since this authentication is done with public keys instead of passwords, the web server can’t impersonate the user to resources (including SQL server) on other servers. This means it’s not really a working solution by itself. If you’re running SQL server on your production web server, stop reading now and go fix that. </p>
<p>Fortunately, windows authentication can take advantage of delegated Kerberos authentication. This is difficult to set up and well beyond the scope of this post. Basically, after trading some more public keys around, the SQL server knows that the web server is telling the truth about the current user and agrees to run the SQL statements in the context of that user. Internet Explorer users are still not prompted for their user names and passwords, and it just works.&#160; </p>
<p><em><font color="#ff0000">Warning: Impersonation, no matter which method you choose, is not recommended. It effectively disables connection pooling, which stops your application from scaling. In fact, this whole post is probably one bad idea after another. </font></em></p>
<p>In this particular case, I’m safe. We can’t physically fit more than a dozen “staff” users, and we’ll certainly never get approval to hire even that many. I’ve used this same architecture for hundreds of users before I knew any better – and the hardware was much slower. In short, it’ll work in this case. </p>
<h4>Dynamic Connection Strings</h4>
<p>This led to another small issue. Customers should log in with a generic SQL account. Staff should log in with their windows accounts. </p>
<p>Jose R. led me to <a href="http://nhforge.org/wikis/howtonh/dynamically-change-user-info-in-connection-string.aspx" target="_blank">this article on the NHForge wiki</a> from November 2008. Well, things have changed slightly in the last year. Also, I wanted something a little more pluggable. </p>
<p><a href="http://nhforge.org" target="_blank">NHibernate</a> uses a DriverConnectionProvider to supply it with a connection. To alter the connection string as needed, we only need to override the ConnectionString property of this class. </p>
<pre class="brush:csharp">using NHibernate.Connection;

namespace DynamicConnectionStringExample
{
    public class DynamicConnectionProvider : DriverConnectionProvider
    {

        public static IConnectionStringProvider connectionStringProvider;

        protected override string ConnectionString
        {
            get
            {
                if (null!=connectionStringProvider)
                {
                    return connectionStringProvider.GetConnectionString();
                }
                return base.ConnectionString;
            }
        }

    }
}</pre>
<p>Note: The example I show here assumes you are using one of the standard bytecode providers such as NHibernate.Bytecode.Castle. However, you could very easily use an IoC bytecode provider (except <a href="http://ninject.org/" target="_blank">Ninject</a>) from uNHAddIns and use constructor dependency injection to inject the IConnectionStringProvider dependency. If not, you’ll need to manually set the ConnectionStringProvider.</p>
<p>To use this, just tell NHibernate about it:</p>
<pre class="brush:csharp">Environment.Properties[Environment.ConnectionProvider] = typeof(DynamicConnectionProvider).AssemblyQualifiedName;</pre>
<p>Of course, the “magic” really happens in the IConnectionStringProvider. If we have an authenticated user, GetConnectionString() returns a connection string with Integrated Security = true. If we don’t, we return one with a SQL username and password.</p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/AA1hEXVDM9A" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/11/authentication-impersonation-and-dynamic-nhibernate-connection-strings/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/11/authentication-impersonation-and-dynamic-nhibernate-connection-strings/</feedburner:origLink></item>
		<item>
		<title>Simple Domain Events</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/VVnKhwwIdHY/</link>
		<comments>http://jasondentler.com/blog/2009/11/simple-domain-events/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 03:31:16 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[Patterns]]></category>
		<category><![CDATA[Domain Events]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/?p=449</guid>
		<description><![CDATA[This is my first attempt at the domain event pattern, so use at your own risk. A lot of this post was blatantly ripped off from inspired by Udi Dahan’s posts on the same subject. Other bits and pieces come from around the ‘net. Sorry I’m not giving credit. At the time, I was researching [...]]]></description>
			<content:encoded><![CDATA[<p>This is my first attempt at the <a href="http://martinfowler.com/eaaDev/DomainEvent.html" target="_blank">domain event pattern</a>, so use at your own risk. A lot of this post was <strike>blatantly ripped off from</strike> inspired by <a href="http://www.udidahan.com/2009/06/14/domain-events-salvation/" target="_blank">Udi Dahan’s posts on the same subject</a>. Other bits and pieces come from around the ‘net. Sorry I’m not giving credit. At the time, I was researching a problem, not a blog post.</p>
<h4>Your Problem</h4>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" border="0" align="left" src="http://blog.lib.umn.edu/biomdoc/access_services/Lily_Tomlin_telephone_operator.jpg" width="223" height="246" />Let’s use the ever-popular “preferred customer” example. You’ve built this beautiful ecommerce application. You even implemented coupon code discounts and stuff. Customers put items in the carts, checkout, and magically get their stuff in 2 to 93.5 days. </p>
<p>Today, the sales director emails you about their new “Preferred customer” program. A preferred customer gets an automatic 2% discount on orders over $1000 (up to $20). This will be a piece of cake. You quickly write up a few new tests, slap an IsPreferred boolean property on your customer entity, and build a new order total strategy for this discount scenario. All the tests turn green. </p>
<p>Then you naively email him to ask how a customer becomes “preferred.” The sales director replies “After the 20th order over $1000, the customer automatically becomes preferred.” Simple enough. He continues “When that happens, we need to place an order for a complimentary gift basket and the sales rep needs an email about it. Also, any customer with a single order over $20,000 or two orders over $10,000 is automatically enrolled in the program. Next Tuesday, we’re meeting to discuss the new Gold preferred program. I want you there.” </p>
<p>Let’s step back and analyze what just happened here. Your beautiful application just got owned by Yet Another Tool With an MBA and you have to share oxygen with him for an hour next Tuesday. More importantly, you have a problem. Even though there are multiple ways to become a preferred customer, the customer only becomes preferred once. They only get one gift basket. Also, you need to do two entirely different actions based on the same event – place a gift basket order and send an email to the sales rep. On top of all that, there’s another mess of weird requirements in the works. How will you handle this without turning your beautiful application in to a pile of ugly hacks? Domain events. </p>
<h4>What’s is it?</h4>
<p>The domain events pattern is the code equivalent of the office gossip network. Whenever something interesting happens, the office gossip tells anyone and everyone who might care about the news. </p>
<p>You have a requirement: “When X happens, do Y1, Y2, Y3, and Y4.” X is the event. The Y’s are the resulting actions that your application should take. There may be one action. There may be 15. There may be none. It doesn’t matter really. Let’s say the event is “John B. Customer just ordered 2 widgets and a sprocket.” Obviously, one of your actions will be “Ship 2 widgets and a sprocket to John B. Customer” You’ll also want to email him a receipt of his order. If this is his first order, you’ll want to mail him a catalog and add him to the mailing list. </p>
<p>How would you handle this? Well, you could have your customer’s PlaceOrder method call the services directly. That creates all sorts of tight coupling that you don’t want. Plus the reference is going the wrong direction. You could inject the services and program to an interface, but a lot of people consider that a bad idea. Even with an interface, your entities know more than they need to about your services. </p>
<p>You’ll probably want to use domain events for this. Domain events are especially helpful when X could happen in many different places, or the Y’s change a lot. They keep your coupling down by keeping your domain blissfully ignorant.</p>
<p>The checkout code of your ecommerce site doesn’t need to know how to ship products, email receipts, or anything else. It just needs to tell some central event dispatcher – the office gossip &#8211; “Hey. John B. Customer just placed an order for 2 sprockets and a widget.”</p>
<h4>What’s the solution?</h4>
<p>Basically, if you understand Pub/Sub service bus, toss out the queue and the transactional stuff. Now you understand the layout of domain events. </p>
<p>You have a dispatcher. So that it’s easy to access from anywhere in your application, it’s a static class.</p>
<p>You also have some service that wants to know when some event happens. It tells the central dispatcher “Tell me when [particular type of event] happens.” It registers as a handler of that particular type of event.</p>
<p>Your domain tells the dispatcher “Hey! This just happened.” The dispatcher looks up the handler or handlers for that event and passes along the message. The handler(s) do some work based on the details of that event. </p>
<h4>Another Trio</h4>
<p>All of the really cool patterns have 3 parts… and singleton has thick glasses and bad acne. </p>
<ol>
<li>Events – CustomerBecomesPreferred, CustomerPlacedOrder, etc. Each of these classes will be immutable – they can’t be changed once they’re created. Even though we don’t have any common members, all of our domain events play a particular role in the application. Like Udi says in his post, define role explicitly. All of our event types will implement IDomainEvent. </li>
<li>Handlers &#8211; In Pub/Sub, these would be the subscribers. You can have multiple handlers for a single type of event. The order that these handlers execute is unknown. For this reason, your events should be immutable. Altering the state of an event during the execution of a handler could create unexpected side-effects in a subsequent handler, and the whole thing becomes a game of <a href="http://en.wikipedia.org/wiki/Chinese_whispers" target="_blank">Chinese Whispers</a>. Since handlers also play a role, we’ll have an interface for them. Each handler will implement IHandle&lt;T&gt; where T is the type of event to be handled. We’ll also define a void Handle(T Event); method so all our handlers have a common entry point for the dispatcher. </li>
<li>The dispatcher dispatches events to the various handlers. The internals can be implemented many different ways, but the result is the same. All of the event handlers are registered in the dispatcher. When the dispatcher raises an event, it gets all of the handlers for that type of event. One by one, it executes each handler’s Handle method. We’ll use a service locator internally. <a href="http://ninject.org/" target="_blank">Ninject</a> people will need to use v2.0. </li>
</ol>
<p>Here’s the diagram:</p>
<p><a href="http://yuml.me/diagram/class/[&lt;&lt;IDomainEvent&gt;&gt;{bg:yellow}]^-.-[CustomerBecamePreferred|+Customer;{bg:orange}], [&lt;&lt;IHandle(T)&gt;&gt;{bg:yellow}]&lt;-&gt;[note:where T:IDomainEvent{bg:cornsilk}], [&lt;&lt;IHandle(T)&gt;&gt;]^-.-[&lt;&lt;IHandle(CustomerBecamePreferred)&gt;&gt;{bg:yellow}], [&lt;&lt;IHandle(CustomerBecamePreferred)&gt;&gt;]^-.-[SendPreferredGiftBasket|+Handle(CustomerBecamePreferred);{bg:orange}], [&lt;&lt;IHandle(CustomerBecamePreferred)&gt;&gt;]^-.-[SendMessageToSalesRep|+Handle(CustomerBecamePreferred);{bg:orange}], [Dispatcher|+Raise(T event) where T:IDomainEvent{bg:green}]." target="_blank"><img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" src="http://yuml.me/diagram/class/%5B%3C%3CIDomainEvent%3E%3E%7Bbg%3Ayellow%7D%5D%5E-.-%5BCustomerBecamePreferred%7C+Customer%3B%7Bbg%3Aorange%7D%5D%2C%20%5B%3C%3CIHandle%28T%29%3E%3E%7Bbg%3Ayellow%7D%5D%3C-%3E%5Bnote%3Awhere%20T%3AIDomainEvent%7Bbg%3Acornsilk%7D%5D%2C%20%5B%3C%3CIHandle%28T%29%3E%3E%5D%5E-.-%5B%3C%3CIHandle%28CustomerBecamePreferred%29%3E%3E%7Bbg%3Ayellow%7D%5D%2C%20%5B%3C%3CIHandle%28CustomerBecamePreferred%29%3E%3E%5D%5E-.-%5BSendPreferredGiftBasket%7C+Handle%28CustomerBecamePreferred%29%3B%7Bbg%3Aorange%7D%5D%2C%20%5B%3C%3CIHandle%28CustomerBecamePreferred%29%3E%3E%5D%5E-.-%5BSendMessageToSalesRep%7C+Handle%28CustomerBecamePreferred%29%3B%7Bbg%3Aorange%7D%5D%2C%20%5BDispatcher%7C+Raise%28T%20event%29%20where%20T%3AIDomainEvent%7Bbg%3Agreen%7D%5D" width="640" height="267" /></a></p>
</p>
<p>Here’s the code:</p>
<pre class="brush:csharp">namespace DomainEventsSample
{
    public interface IDomainEvent
    {
    }
}

namespace DomainEventsSample
{
    public interface IHandle&lt;T&gt; where T:IDomainEvent
    {
        void Handle(T Event);
    }
}

using Microsoft.Practices.ServiceLocation;

namespace DomainEventsSample
{
    public static class Dispatcher
    {

        public static void Initialize(IServiceLocator ServiceLocator)
        {
            serviceLocator = ServiceLocator;
        }

        private static IServiceLocator serviceLocator;

        static void Raise&lt;T&gt;(T Event) where T : IDomainEvent
        {
            var handlers = serviceLocator.GetAllInstances&lt;IHandle&lt;T&gt;&gt;();
            foreach (var handler in handlers)
            {
                handler.Handle(Event);
            }
        }

    }
}</pre>
<p>In our main program, we bind some event handlers to their implementations, then make a customer order a bunch of stuff. </p>
<pre class="brush:csharp">using System;
using Ninject;
using Microsoft.Practices.ServiceLocation;
using CommonServiceLocator.NinjectAdapter;

namespace DomainEventsSample
{
    class Program
    {
        static void Main(string[] args)
        {
            IKernel kernel = new StandardKernel();
            IServiceLocator sl = new NinjectServiceLocator(kernel);
            Dispatcher.Initialize(sl);

            kernel.Bind&lt;IHandle&lt;CustomerPlacedOrderEvent&gt;&gt;().To&lt;ShipOrder&gt;();
            kernel.Bind&lt;IHandle&lt;CustomerBecamePreferred&gt;&gt;().To&lt;SendPreferredGiftBasket&gt;();
            kernel.Bind&lt;IHandle&lt;CustomerBecamePreferred&gt;&gt;().To&lt;SendMessageToSalesRep&gt;();

            Customer c = new Customer();
            for (var i = 0; i &lt; 22; i++)
            {
                Order newOrder = new Order(1000.00 + i);
                c.PlaceOrder(newOrder);
                Console.WriteLine(&quot;------------------------------------&quot;);
            }
            Console.ReadLine();

        }
    }
}</pre>
<p>Our events are pretty simple. They just pass along the relevant information. When the event is about something that happened to a customer, we need to know which customer. When the event is about an order, we need to know which order.</p>
<pre class="brush:csharp">namespace DomainEventsSample
{
    public class CustomerPlacedOrder:IDomainEvent
    {

        public CustomerPlacedOrder(Order Order)
        {
            order = Order;
        }

        private readonly Order order;

        public Order Order
        {
            get
            {
                return order;
            }
        }

    }
}</pre>
<p>Here’s a handler. Of course, this is just a stub for some real action you would implement. </p>
<pre class="brush:csharp">using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DomainEventsSample
{
    public class ShipOrder : IHandle&lt;CustomerPlacedOrderEvent&gt;
    {
        #region IHandle&lt;CustomerPlacedOrderEvent&gt; Members

        void IHandle&lt;CustomerPlacedOrderEvent&gt;.Handle(CustomerPlacedOrderEvent Event)
        {
            Console.WriteLine(&quot;Shipping order totalling {0:C}&quot;,Event.Order.Total);
        }

        #endregion
    }
}</pre>
<h4>Bidirectional Communication</h4>
<p>This is the real reason I’m posting. I had a situation where a domain event handler called in to another system to do some action and the log of that action needed to make its way back to the UI. </p>
<p>Just like with Pub/Sub, domain events are one way. I didn’t want to give up domain events, but I had a requirement I couldn’t fill with the pattern. So, after hacking at it for an afternoon and getting nowhere, I sent out a call for help. Ayende wrote me back. </p>
<blockquote>
<p><font color="#000000">Publish a new event that the UI is listening to</font></p>
</blockquote>
<p>He explains in 10 words what takes me 3 paragraphs. There’s your answer folks. When your handler needs to respond, publish a response event. Simple. Elegant. </p>
<h4>Gimme the Code!</h4>
<p>I’m putting all of the code for this blog in SVN at <a href="http://basiclyeverything.googlecode.com/svn/trunk/" target="_blank">http://basiclyeverything.googlecode.com/svn/trunk/</a></p>
<p>The code for this particular post is in <a href="http://basiclyeverything.googlecode.com/svn/trunk/DomainEventsSample" target="_blank">http://basiclyeverything.googlecode.com/svn/trunk/DomainEventsSample</a></p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/VVnKhwwIdHY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/11/simple-domain-events/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/11/simple-domain-events/</feedburner:origLink></item>
		<item>
		<title>First Impressions: TekPub</title>
		<link>http://feedproxy.google.com/~r/BasiclyEverything/~3/bgVIhfwCnDY/</link>
		<comments>http://jasondentler.com/blog/2009/10/first-impressions-tekpub/#comments</comments>
		<pubDate>Sun, 25 Oct 2009 04:03:06 +0000</pubDate>
		<dc:creator>Jason</dc:creator>
				<category><![CDATA[Training]]></category>
		<category><![CDATA[TekPub]]></category>

		<guid isPermaLink="false">http://jasondentler.com/blog/?p=447</guid>
		<description><![CDATA[OK Folks. I don’t do endorsements. I especially dislike commercial / corporate training. The trainers aren’t really experts. They didn’t create this stuff. They don’t even use this stuff. They just *teach* this stuff – day after day, city after city. I can learn 95% of the material with my laptop and 15 minutes with [...]]]></description>
			<content:encoded><![CDATA[<p>OK Folks. I don’t do endorsements. I especially dislike commercial / corporate training. The trainers aren’t really experts. They didn’t create this stuff. They don’t even use this stuff. They just *teach* this stuff – day after day, city after city. I can learn 95% of the material with my laptop and 15 minutes with the slide deck.</p>
<p>What’s the alternative? </p>
<p>Blogs? They’re fine for keeping up with trends and knowing what the cool kids are doing, but if you’re trying to learn something from scratch, you’ve got to dig for the good stuff. The worst part about web 2.0 is that any idiot can start a blog about stuff he doesn’t really understand – even me.</p>
<p>Books? While I could recommend a few, the honest truth is that I’ve had a dozen queued up on my bookshelf for nearly a year. Even if you do take the time to read, the information is out of date before the book even hits your shopping cart. On top of that, you have no guarantee that the author is really an expert.</p>
<p>Here’s something different.</p>
<p><img style="border-bottom: 0px; border-left: 0px; margin: 4px 10px 4px 0px; display: inline; border-top: 0px; border-right: 0px" border="0" alt="TekPub_logo " align="left" src="http://blog.tekpub.com/wp-content/uploads/2009/10/TekPub_logo.png" /><a href="http://blog.wekeroad.com/" target="_blank">Rob Connery</a> <a href="http://twitter.com/robconery" target="_blank">@robconery</a> and <a href="http://averyblog.com/" target="_blank">James Avery</a> <a href="http://twitter.com/averyj" target="_blank">@averyj</a> started <a href="http://tekpub.com" target="_blank">TekPub.com</a>. That’s <a href="http://twitter.com/tekpub" target="_blank">@tekpub</a> on Twitter. It’s a library of professional screencast series by Rob, James, and other subject experts with names you’ll recognize. They’re not little 30 minute channel 9 interviews with obscure Microsoft PMs. First, the message is not corporate in any way, shape, or form. Second, the quality is amazing. The audio is clear. The text is crisp and easily readable. The images are both humorous and relevant to the topic.</p>
<p>They just launched. Here’s what’s out there already:</p>
<ul>
<li>Mastering <a href="http://nhforge.org" target="_blank">NHibernate</a> – Oren Eini aka Ayende Rahien and Rob integrate NHibernate with Kona, an MVC storefront app. When it comes to NHibernate, Oren is a true master. </li>
<li>Building my own Blog – He’s Rob freakin’ Conery. He’s not setting up WordPress. He’s building a blog from scratch with MVC, lean techniques, AgileZen, BDD, MSpec, and a whole lot of other cool stuff – and taking you along for the ride. </li>
<li>Mastering Git – A full 3 hours dedicated to the ins and outs of Git. This will be the first one I watch.</li>
<li>Coder to Developer 2009 – Rob updates a classic book in screencast form. This one is free folks, so go watch.</li>
<li>Concepts – The first one covers IoC and DI with <a href="http://ninject.org/" target="_blank">Ninject</a>. Again, this one is free. The basics – stuff you should know &#8211; are free. This should be pretty awesome for training the new college grad on your team. When you need a break from the training pace, stick him in front of the conference room flat screen with a bucket of popcorn.</li>
</ul>
<p>Even if you’re cheap, the previews and free stuff are worth a look. Personally, I hope they get Udi Dahan to talk about NServiceBus. </p>
<img src="http://feeds.feedburner.com/~r/BasiclyEverything/~4/bgVIhfwCnDY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://jasondentler.com/blog/2009/10/first-impressions-tekpub/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://jasondentler.com/blog/2009/10/first-impressions-tekpub/</feedburner:origLink></item>
	</channel>
</rss>
