<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
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/"
><channel><title>The Inquisitive Coder - Davy Brion&#039;s Blog &#187; Build Your Own DAL</title> <atom:link href="http://davybrion.com/blog/category/build-your-own-dal/feed/" rel="self" type="application/rss+xml" /><link>http://davybrion.com/blog</link> <description>inquisitive: adjective. given to inquiry, research, or asking questions; eager for knowledge; intellectually curious</description> <lastBuildDate>Sun, 29 Jan 2012 10:48:12 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Build Your Own Data Access Layer: Enabling Bulk Inserts</title><link>http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/</link> <comments>http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/#comments</comments> <pubDate>Mon, 12 Oct 2009 10:29:43 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1733</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. I know i wrapped up the series already, but i just had to add the ability to do bulk inserts to this data layer so i figured i'd might as well post about it. Ayende already talked about how to [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series. Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>I know i wrapped up the series already, but i just had to add the ability to do bulk inserts to this data layer so i figured i'd might as well post about it.  Ayende already talked about how to enable the ability to batch inserts (or updates and deletes) <a
href="http://ayende.com/Blog/archive/2006/09/13/OpeningUpQueryBatching.aspx">here</a> and <a
href="http://ayende.com/Blog/archive/2006/09/14/ThereBeDragonsRhinoCommonsSqlCommandSet.aspx">here</a> so i'm going to skip that part.  I used the exact same trick and created a PublicSqlCommandSet class which wraps the hidden SqlCommandSet class.  Again, if you have no idea what i'm talking about in that last sentence then you need to read Ayende's 2 posts that i just linked to <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><p>After that, adding the bulk insert feature to the DAL was as simple as creating this class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class BulkInsertAction : DatabaseAction
    {
        public BulkInsertAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache) {}
 
        public void Insert&lt;TEntity&gt;(IEnumerable&lt;TEntity&gt; entities, int batchSize, int commandTimeOut)
        {
            var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
            var insertStatement = tableInfo.GetInsertStatementWithoutReturningTheIdentityValue();
 
            var sqlCommandSet = new PublicSqlCommandSet { CommandTimeout = commandTimeOut, Connection = GetConnection(), Transaction = GetTransaction() };
 
            foreach (var entity in entities)
            {
                var currentCommand = CreateCommand();
                currentCommand.CommandText = insertStatement;
 
                foreach (var parameterInfo in tableInfo.GetParametersForInsert(entity))
                {
                    currentCommand.CreateAndAddInputParameter(parameterInfo.DbType, &quot;@&quot; + parameterInfo.Name, parameterInfo.Value);
                }
 
                sqlCommandSet.Append(currentCommand);
 
                if (sqlCommandSet.CommandCount == batchSize)
                {
                    ExecuteCurrentBatch(sqlCommandSet);
                    sqlCommandSet = new PublicSqlCommandSet { CommandTimeout = commandTimeOut, Connection = GetConnection(), Transaction = GetTransaction() };
                }
            }
 
            if (sqlCommandSet.CommandCount &gt; 0)
            {
                ExecuteCurrentBatch(sqlCommandSet);
            }
        }
 
        private void ExecuteCurrentBatch(PublicSqlCommandSet sqlCommandSet)
        {
            try
            {
                sqlCommandSet.ExecuteNonQuery();
            }
            finally
            {
                sqlCommandSet.Dispose();
            }
        }
    }
</pre></div><p>And then adding this to the Session class:</p><div><pre class="brush: csharp; title: ; notranslate">
        public void BulkInsert&lt;TEntity&gt;(IEnumerable&lt;TEntity&gt; entities)
        {
            CreateAction&lt;BulkInsertAction&gt;().Insert(entities, 50, 200);
        }
</pre></div><p>Obviously, the method signature was also added to the ISession interface.  The batch-size and commandtimeout parameters are currently hardcoded but they should come from some kind of configuration file.</p><p>All in all, pretty easy stuff <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F10%2Fbuild-your-own-data-access-layer-enabling-bulk-inserts%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/"  data-text="Build Your Own Data Access Layer: Enabling Bulk Inserts" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Conclusions</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/#comments</comments> <pubDate>Sat, 29 Aug 2009 13:42:10 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1534</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. Building your own DAL is almost never a cost-efficient solution. In this case, i wrote this DAL in 24 working hours, but it is limited in scope, power, flexibility and functionality. Having said that, i do think it's better than [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>Building your own DAL is almost never a cost-efficient solution.  In this case, i wrote this DAL in 24 working hours, but it is limited in scope, power, flexibility and functionality.  Having said that, i do think it's better than every single custom DAL i've come across so far.  Taking it to the next step however would take a lot more effort, which truly is never worth it.  As you try to provide more functionality, overall complexity of your custom DAL will increase heavily and the effort you'll eventually spend on it will more than outweigh any of the downsides that might come with using something that already exists.  Building your own DAL is an undertaking that should always be questioned, and shouldn't be considered unless the alternative of doing so is even worse.</p><p>If however, you're in a situation where it does make sense, then i hope this series might have been helpful for you.  I've shown that you can come up with something relatively decent without having to resort to code generation, without having to spend an insane amount of effort on it, and without having to write repetitive and error-prone code in your application code.  Those were the goals i had in mind when i started working on this DAL, and i think i've succeeded at achieving those goals.  The final result is an easy-to-use DAL which is far from as powerful as already existing solutions, but it is pretty good for the scenario's where we intend to use this.</p><p>The code itself is clear, easy to maintain and in some cases, very easy to extend as well.  In total, this DAL is slightly less than 1100 lines of code, and i think the complexity of the code is relatively low so everyone should be able to understand what's going on, how it works, and where things could be modified in order to fix issues or to add new features.</p><p>Also, this series of blog posts helps in figuring out how it works since pretty much every aspect of it is now documented pretty extensively <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><p>All in all, i had a lot of fun in writing both the DAL and this series of posts (which took another 8 hours in total).</p><p>In the introduction of this series, i mentioned the following:</p><blockquote> The purpose of this series is basically to: <br/> * Show you that you really don’t need to resort to code generation to build your own custom DAL<br/> * Show you what kind of complexity is involved with the implementation of a good DAL<br/> * Convince you that you typically are better off with simply using something that is already available as a mature, powerful and proven solution</blockquote><p>So tell me, did the series succeed in these listed goals? Would you still go for code generation if you had to create a custom DAL? Would you still prefer to use a custom DAL over something that already exists? How would you react to having to use this particular DAL?  What would need to be added or modified before you would find it acceptable?  What are your thoughts on this in general?</p><p>I'd be very interested in hearing about it, so please do post your opinions in the comments <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-conclusions%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/"  data-text="Build Your Own Data Access Layer: Conclusions" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/feed/</wfw:commentRss> <slash:comments>15</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Bringing It All Together</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/#comments</comments> <pubDate>Sat, 29 Aug 2009 13:42:02 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1532</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. By now we've already covered everything that this DAL has to offer, which admittedly isn't all that much. All of the classes you've seen so far are pretty good at whey they should do, but nobody in their right mind [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>By now we've already covered everything that this DAL has to offer, which admittedly isn't all that much.  All of the classes you've seen so far are pretty good at whey they should do, but nobody in their right mind would want to use any of these things directly in application code.  Any easy-to-use DAL should offer a simple facade which sits on top of the underlying system and makes it very easy to perform the most typical tasks that you need it to perform for you.  You shouldn't need to know about specific classes to be able to use it (that goes for most good frameworks and libraries btw).</p><p>So once again, i based my approach on what NHibernate does, and with that the ISession interface was born:</p><div><pre class="brush: csharp; title: ; notranslate">
    public interface ISession : IDisposable
    {
        void Commit();
        void Rollback();
 
        IQuery CreateQuery(string sql);
        IQuery CreateQuery&lt;TEntity&gt;(string whereClause);
 
        TEntity Get&lt;TEntity&gt;(object id);
        IEnumerable&lt;TEntity&gt; FindAll&lt;TEntity&gt;();
 
        TEntity Insert&lt;TEntity&gt;(TEntity entity);
        TEntity Update&lt;TEntity&gt;(TEntity entity);
        void Delete&lt;TEntity&gt;(TEntity entity);
 
        TableInfo GetTableInfoFor&lt;TEntity&gt;();
 
        void ClearCache();
        void RemoveFromCache(object entity);
        void RemoveAllInstancesFromCache&lt;TEntity&gt;();
 
        SqlConnection GetConnection();
        SqlTransaction GetTransaction();
    }
</pre></div><p>Everything that you need to be able to do with this DAL is provided by this single interface.  And the implementation of the Session class is very easy as well, since we can simply delegate pretty much everything to each of the classes we've covered in the other posts in the series:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class Session : ISession
    {
        private readonly string connectionString;
        private SqlConnection connection;
        private SqlTransaction transaction;
        private readonly MetaDataStore metaDataStore;
        private readonly EntityHydrater hydrater;
        private readonly SessionLevelCache sessionLevelCache;
 
        public Session(string connectionString, MetaDataStore metaDataStore)
        {
            this.connectionString = connectionString;
            this.metaDataStore = metaDataStore;
            sessionLevelCache = new SessionLevelCache();
            hydrater = new EntityHydrater(metaDataStore, this, sessionLevelCache);
        }
 
        private void InitializeConnection()
        {
            connection = new SqlConnection(connectionString);
            connection.Open();
            transaction = connection.BeginTransaction();
        }
 
        public SqlConnection GetConnection()
        {
            if (connection == null)
            {
                InitializeConnection();
            }
 
            return connection;
        }
 
        public SqlTransaction GetTransaction()
        {
            if (transaction == null)
            {
                InitializeConnection();
            }
 
            return transaction;
        }
 
        public IQuery CreateQuery(string sql)
        {
            var command = GetConnection().CreateCommand();
            command.Transaction = GetTransaction();
            command.CommandText = sql;
            return new Query(command, metaDataStore, hydrater);
        }
 
        public IQuery CreateQuery&lt;TEntity&gt;(string whereClause)
        {
            return CreateQuery(metaDataStore.GetTableInfoFor&lt;TEntity&gt;().GetSelectStatementForAllFields() + &quot; &quot; + whereClause);
        }
 
        public TableInfo GetTableInfoFor&lt;TEntity&gt;()
        {
            return metaDataStore.GetTableInfoFor&lt;TEntity&gt;();
        }
 
        public void Commit()
        {
            transaction.Commit();
        }
 
        public void Rollback()
        {
            transaction.Rollback();
        }
 
        public void Dispose()
        {
            if (transaction != null) transaction.Dispose();
            if (connection != null) connection.Dispose();
        }
 
        private TAction CreateAction&lt;TAction&gt;()
            where TAction : DatabaseAction
        {
            return (TAction)Activator.CreateInstance(typeof(TAction), GetConnection(), GetTransaction(),
                metaDataStore, hydrater, sessionLevelCache);
        }
 
        public TEntity Get&lt;TEntity&gt;(object id)
        {
            return CreateAction&lt;GetByIdAction&gt;().Get&lt;TEntity&gt;(id);
        }
 
        public IEnumerable&lt;TEntity&gt; FindAll&lt;TEntity&gt;()
        {
            return CreateAction&lt;FindAllAction&gt;().FindAll&lt;TEntity&gt;();
        }
 
        public TEntity Insert&lt;TEntity&gt;(TEntity entity)
        {
            return CreateAction&lt;InsertAction&gt;().Insert(entity);
        }
 
        public TEntity Update&lt;TEntity&gt;(TEntity entity)
        {
            return CreateAction&lt;UpdateAction&gt;().Update(entity);
        }
 
        public void Delete&lt;TEntity&gt;(TEntity entity)
        {
            CreateAction&lt;DeleteAction&gt;().Delete(entity);
        }
 
        public void InitializeProxy(object proxy, Type targetType)
        {
            CreateAction&lt;InitializeProxyAction&gt;().InitializeProxy(proxy, targetType);
        }
 
        public void ClearCache()
        {
            sessionLevelCache.ClearAll();
        }
 
        public void RemoveFromCache(object entity)
        {
            sessionLevelCache.Remove(entity);
        }
 
        public void RemoveAllInstancesFromCache&lt;TEntity&gt;()
        {
            sessionLevelCache.RemoveAllInstancesOf(typeof(TEntity));
        }
    }
</pre></div><p>As you can see, there's nothing special here and it's all very straightforward.  Application code can now perform database operations pretty easily once it has a reference to an ISession instance.  And obtaining a reference to an ISession instance can be done through the ISessionFactory interface:</p><div><pre class="brush: csharp; title: ; notranslate">
    public interface ISessionFactory
    {
        ISession CreateSession();
    }
</pre></div><p>And its implementation:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class SessionFactory : ISessionFactory
    {
        private string connectionString;
        private MetaDataStore metaDataStore;
 
        public static ISessionFactory Create(Assembly assembly, string connectionString)
        {
            var sessionFactory = new SessionFactory {connectionString = connectionString, metaDataStore = new MetaDataStore()};
            sessionFactory.metaDataStore.BuildMetaDataFor(assembly);
            return sessionFactory;
        }
 
        private SessionFactory() {}
 
        public ISession CreateSession()
        {
            return new Session(connectionString, metaDataStore);
        }
    }
</pre></div><p>The static Create method takes an assembly and a connection string.  The assembly (containing your entity types) will be used to to build up the metadata model as covered in the <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/">Mapping Classes To Tables post</a>.  You would typically call the Create method in your application's startup code, and then you'd have to store a reference to the ISessionFactory somewhere.  Your application code can then simply call the ISessionFactory's CreateSession method and that's all there is to it.</p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-bringing-it-all-together%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/"  data-text="Build Your Own Data Access Layer: Bringing It All Together" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Executing Custom Queries</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/#comments</comments> <pubDate>Fri, 28 Aug 2009 14:34:47 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1530</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. You've already seen that this DAL offers you the ability to query each entity by primary key, or to retrieve a list of all instances of an entity type. Obviously, this isn't even sufficient for the most trivial applications so [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>You've already seen that this DAL offers you the ability to query each entity by primary key, or to retrieve a list of all instances of an entity type.  Obviously, this isn't even sufficient for the most trivial applications so we need a way to execute custom queries.  So i needed to provide something that would enable you to easily execute custom queries and get a list of entity instances, or a single result without having to muck around with transforming the results on your own.</p><p>Due to the extremely simplistic nature of this DAL, i can't support much more than queries which either return a single value (being one entity instance, or a scalar value) or a list of entities.  As soon as you need to execute queries that return joined results, this DAL won't be able to deal with the results automatically.  I'll get back to this part later on in the post so for now, let's focus on queries whose result can be transformed to an entity automatically.</p><p>My first version of this DAL hardly had any support for this, and when using the DAL for custom queries, you basically had to deal with SqlCommands and their results manually.  Well, you could pass the SqlCommand to the EntityHydrater, but you still had to deal with a lot of ugly code.  I had asked <a
href="http://ayende.com/blog/">Ayende</a> to do a private review before i started with this series, and he quickly pointed out that i needed something better for executing SQL queries and suggested something like (surprise, surprise) what NHibernate offers for SQL queries.  So yes, this solution is once again heavily inspired by NHibernate <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><p>Let's go over the details... First of all, we have this simple IQuery interface:</p><div><pre class="brush: csharp; title: ; notranslate">
    public interface IQuery
    {
        void AddParameter(string name, object value, DbType dbType);
        TResult GetSingleResult&lt;TResult&gt;();
        IEnumerable&lt;TResult&gt; GetResults&lt;TResult&gt;();
        int ExecuteNonQuery();
    }
</pre></div><p>That is what you should be able to do with a query once you've created it.  Creating a query is possible through my session API (which will be covered in the next post):</p><div><pre class="brush: csharp; title: ; notranslate">
        IQuery CreateQuery(string sql);
        IQuery CreateQuery&lt;TEntity&gt;(string whereClause);
</pre></div><p>Through the regular CreateQuery method, you can provide the full SQL string and you have full control over the actual SQL.  The CreateQuery&lt;TEntity&gt; overload only requires you to provide the WHERE clause because it will generate a SELECT clause for the given entity which automatically retrieves all of the columns for this entity.  This also ensures that the result of CreateQuery&lt;TEntity&gt; can always be cleanly transformed to a list of entities or a single entity instance through the EntityHydrater.</p><p>This is the implementation of both CreateQuery methods:</p><div><pre class="brush: csharp; title: ; notranslate">
        public IQuery CreateQuery(string sql)
        {
            var command = GetConnection().CreateCommand();
            command.Transaction = GetTransaction();
            command.CommandText = sql;
            return new Query(command, metaDataStore, hydrater);
        }
 
        public IQuery CreateQuery&lt;TEntity&gt;(string whereClause)
        {
            return CreateQuery(metaDataStore.GetTableInfoFor&lt;TEntity&gt;().GetSelectStatementForAllFields() + &quot; &quot; + whereClause);
        }
</pre></div><p>And here's the actual implementation of the Query class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class Query : IQuery
    {
        private readonly SqlCommand command;
        private readonly MetaDataStore metaDataStore;
        private readonly EntityHydrater hydrater;
 
        public Query(SqlCommand command, MetaDataStore metaDataStore, EntityHydrater hydrater)
        {
            this.command = command;
            this.metaDataStore = metaDataStore;
            this.hydrater = hydrater;
        }
 
        public void AddParameter(string name, object value, DbType dbType)
        {
            command.CreateAndAddInputParameter(dbType, name, value);
        }
 
        public TResult GetSingleResult&lt;TResult&gt;()
        {
            var tableInfo = metaDataStore.GetTableInfoFor&lt;TResult&gt;();
 
            if (tableInfo == null)
            {
                var scalar = (TResult)command.ExecuteScalar();
                command.Dispose();
                return scalar;
            }
 
            var result = hydrater.HydrateEntity&lt;TResult&gt;(command);
            command.Dispose();
            return result;
        }
 
        public IEnumerable&lt;TResult&gt; GetResults&lt;TResult&gt;()
        {
            var tableInfo = metaDataStore.GetTableInfoFor&lt;TResult&gt;();
 
            if (tableInfo == null)
            {
                var listOfValues = GetListOfValues&lt;TResult&gt;();
                command.Dispose();
                return listOfValues;
            }
 
            var result = hydrater.HydrateEntities&lt;TResult&gt;(command);
            command.Dispose();
            return result;
        }
 
        private IEnumerable&lt;TResult&gt; GetListOfValues&lt;TResult&gt;()
        {
            using (var reader = command.ExecuteReader())
            {
                var list = new List&lt;object&gt;();
                while (reader.Read())
                {
                    list.Add(reader.GetValue(0));
                }
                return list.Cast&lt;TResult&gt;();
            }
        }
 
        public int ExecuteNonQuery()
        {
            var rowsAffected = command.ExecuteNonQuery();
            command.Dispose();
            return rowsAffected;
        }
    }
</pre></div><p>Pretty simple, right? The ability to get a strong typed result is something that i find very important when using any DAL, and the Query class makes this very easy to do.  If you want to return a list of single value results, you can do that easily.  If you want to return a single scalar result, you can do that easily.  A single entity instance? No problem, the EntityHydrater takes care of that for us.  Same thing goes for a list of entities.  Custom delete or update statements? No problem, the ExecuteUpdate method can be used for that and will return the typical number of affected rows as reported by the database.</p><p>And now you can do things like this pretty easily:</p><div><pre class="brush: csharp; title: ; notranslate">
            var query = session.CreateQuery&lt;Customer&gt;(&quot;where this.NumberOfPurchases &gt; @numberOfPurchases&quot;);
            query.AddParameter(&quot;@numberOfPurchases&quot;, 100, DbType.Int32);
            var goodCustomers = query.GetResults&lt;Customer&gt;();
</pre></div><p>or</p><div><pre class="brush: csharp; title: ; notranslate">
            var query = session.CreateQuery(&quot;select max(NumberOfPurchases) from Customer&quot;);
            int max = query.GetSingleResult&lt;int&gt;();
</pre></div><p>or</p><div><pre class="brush: csharp; title: ; notranslate">
            var query = session.CreateQuery&lt;Customer&gt;(&quot;where this.NumberOfPurchases = (select max(NumberOfPurchases) from Customer))&quot;);
            var bestCustomer = query.GetSingleResult&lt;Customer&gt;();
</pre></div><p>I'm sure you get the idea by now <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><p>Note: when using the CreateQuery&lt;TEntity&gt; method, the SELECT clause that is generated automatically prefixes each selected column from the entity with the 'this' prefix.  This makes it easier to refer to the entity's properties in other clauses while you still have to ability to join on another table in the from clause, though you obviously can't add any columns to the select clause anymore.</p><p>Now, this is all pretty good for queries where you only need to return scalar values or specific entities, but what if you want to return values from multiple tables in one query? This DAL doesn't have any support for that, but what you could do instead, is to create a view for your query and map an 'entity' to that view instead.  Then you could still get a typed result while querying the view, though the results wouldn't be able to be transformed into your 'real' entities.  But for filling grids or just to write typical overview queries, this might already be sufficient.</p><p>As you can tell, there's not a lot of power or flexibility behind this approach.  But try to think of the complexity involved with trying to deal with results from multiple tables.  It makes <strong>everything</strong> a whole lot more complex.  At that time, you really need to consider if it's still worth writing your own DAL because the effort you'll spend on getting it right will be very significant.</p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-executing-custom-queries%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/"  data-text="Build Your Own Data Access Layer: Executing Custom Queries" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Lazy Loading</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/#comments</comments> <pubDate>Thu, 27 Aug 2009 14:51:35 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1528</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. In the post about entity hydration, i mentioned the following: If we can’t find the referenced entity instance in the first level cache, what should we do? We obviously can’t load it automatically because that could in turn cause referenced [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>In the post about entity hydration, i mentioned the following:</p><blockquote> If we can’t find the referenced entity instance in the first level cache, what should we do? We obviously can’t load it automatically because that could in turn cause referenced entities’ references to be loaded automatically when they are hydrated. Which in turn could cause their referenced entities… Well, i’m sure you get the point. But those properties obviously can’t be set to a null reference either because the column actually does have a valid foreign key value in the database. Explicitly loading referenced properties leads to seriously ugly (and error-prone) code so that’s not an option i’m willing to consider either. The correct way to deal with this is to use lazy loading. To do that in an automated fashion, we need proxy classes. I’m not going to get into these proxy classes and the whole lazy loading thing just yet, since that will be covered in depth in a future post <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></blockquote><p>It's time to go over the implementation of the lazy loading of this DAL.  I honestly expected that this would be the part that would take the most time to get working.  It actually turned out to be the easiest and quickest part of the DAL to develop.</p><p>As i mentioned, we are going to use proxy classes to achieve our goal of lazy loading.</p><p>Consider the following simple entity type:</p><div><pre class="brush: csharp; title: ; notranslate">
    [Table(&quot;Customer&quot;)]
    public class Customer
    {
        [PrimaryKey(&quot;ID&quot;)]
        public int Id { get; set; }
        [Column(&quot;Name&quot;)]
        public string Name { get; set; }
        [Reference(&quot;AddressID&quot;)]
        public Address Address { get; set; }
    }
</pre></div><p>If we want to avoid having to put any lazy loading logic within this class, we could inherit from it and add the lazy loading logic in the derived class.  First, we would have to make the properties of our entity virtual:</p><div><pre class="brush: csharp; title: ; notranslate">
    [Table(&quot;Customer&quot;)]
    public class Customer
    {
        [PrimaryKey(&quot;ID&quot;)]
        public virtual int Id { get; set; }
        [Column(&quot;Name&quot;)]
        public virtual string Name { get; set; }
        [Reference(&quot;AddressID&quot;)]
        public virtual Address Address { get; set; }
    }
</pre></div><p>Now we could create a proxy class like this:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class CustomerProxy : Customer
    {
        private bool needsToBeInitialized = true;
        public Session Session { get; set; }
 
        public override string Name
        {
            get
            {
                InitializeIfNecessary();
                return base.Name;
            }
            set
            {
                InitializeIfNecessary();
                base.Name = value;
            }
        }
 
        public override Address Address
        {
            get
            {
                InitializeIfNecessary();
                return base.Address;
            }
            set
            {
                InitializeIfNecessary();
                base.Address = value;
            }
        }
 
        private void InitializeIfNecessary()
        {
            if (needsToBeInitialized)
            {
                needsToBeInitialized = false;
                Session.InitializeProxy(this, typeof(Customer));
            }
        }
    }
</pre></div><p>This could definitely work.  Whenever we need a proxy to avoid loading an entity, we can simply instantiate a proxy class like the one above, pass it a Session object (again, the Session implementation will be covered in a future post, though i will show the InitializeProxy later on in this post) and once we retrieve any of the overridden properties, the proxy instance will use the Session to initialize itself.  The Session would then use a DatabaseAction and the hydration process to make sure the proxy's properties are filled in with the values of its corresponding properties.  I didn't override the Id property because accessing the primary key of a proxied object should never result in a database call, so there is no reason to override it.</p><p>But we really can't expect people to manually create proxy types like this.  For one, it's repetitive and thus, it is error prone.  Both are issues that we've aimed to avoid with this DAL from the start.  So how can we make this work automagically? The answer is simple: we can use Castle's excellent <a
href="http://castleproject.org/dynamicproxy/index.html">DynamicProxy</a> library to generate the proxy classes at runtime for us.</p><p>DynamicProxy uses a concept known as an Interceptor.  The Interceptor basically intercepts method calls on proxied objects and allows you to add custom logic before and after the original method calls.  For our lazy loading purposes, we simply need the following LazyLoadingInterceptor class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class LazyLoadingInterceptor : IInterceptor
    {
        private TableInfo tableInfo;
        private readonly Session session;
        private bool needsToBeInitialized = true;
 
        public LazyLoadingInterceptor(TableInfo tableInfo, Session session)
        {
            this.tableInfo = tableInfo;
            this.session = session;
        }
 
        public void Intercept(IInvocation invocation)
        {
            if (invocation.Method.Name.Equals(&quot;get_&quot; + tableInfo.PrimaryKey.PropertyInfo.Name) ||
                invocation.Method.Name.Equals(&quot;set_&quot; + tableInfo.PrimaryKey.PropertyInfo.Name))
            {
                invocation.Proceed();
                return;
            }
 
            if (needsToBeInitialized)
            {
                needsToBeInitialized = false;
                session.InitializeProxy(invocation.Proxy, invocation.TargetType);
            }
 
            invocation.Proceed();
        }
</pre></div><p>Ok, so what does this class do? Like i said, an interceptor will intercept all method calls on virtual methods of a proxied object.  So basically, if we create a proxy through DynamicProxy, and tell DynamicProxy to add a new instance of this LazyLoadingInterceptor so we can add the behavior of this interceptor to our proxy object, and we then access the properties of our proxy object, it will actually show the same behavior as the manually created CustomerProxy class listed earlier.  And we get all of this for free, without having to modify our original Customer class, except for marking the properties as virtual obviously.</p><p>Now, when the LazyLoadingInterceptor calls the Session's InitializeProxy method, the session will delegate this call to our new InitializeProxyAction class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class InitializeProxyAction : DatabaseAction
    {
        public InitializeProxyAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public void InitializeProxy(object proxy, Type targetType)
        {
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor(targetType);
                var query = tableInfo.GetSelectStatementForAllFields();
                tableInfo.AddWhereByIdClause(query);
 
                object id = tableInfo.PrimaryKey.PropertyInfo.GetValue(proxy, null);
                command.CommandText = query.ToString();
                command.CreateAndAddInputParameter(tableInfo.PrimaryKey.DbType, tableInfo.GetPrimaryKeyParameterName(), id);
 
                Hydrater.UpdateEntity(targetType, proxy, command);
            }
        }
    }
</pre></div><p>As you can see, this is extremely similar to the GetByIdAction.  In fact, i should probably put the common logic in another base DatabaseAction class that sits between DatabaseAction and GetByIdAction and InitializeProxyAction.  Anyways, when the entity has been retrieved, we ask the EntityHydrater to update this entity instance through its newly added UpdateEntity method:</p><div><pre class="brush: csharp; title: ; notranslate">
        public void UpdateEntity(Type type, object entity, SqlCommand command)
        {
            using (var reader = command.ExecuteReader())
            {
                reader.Read();
                var tableInfo = metaDataStore.GetTableInfoFor(type);
                Hydrate(tableInfo, entity, GetValuesFromCurrentRow(reader));
            }
        }
</pre></div><p>The Hydrate method is already shown in the post that covers the process of entity hydration, so there's no need to go over that again.</p><p>And now there's only one more missing piece in our lazy loading puzzle, which is the actual creation of the proxy in the EntityHydrater's CreateProxy method:</p><div><pre class="brush: csharp; title: ; notranslate">
        private object CreateProxy(TableInfo tableInfo, ReferenceInfo referenceInfo, object foreignKeyValue)
        {
            var proxy = proxyGenerator.CreateClassProxy(referenceInfo.ReferenceType,
                new[] { new LazyLoadingInterceptor(tableInfo, session) });
            var referencePrimaryKey = metaDataStore.GetTableInfoFor(referenceInfo.ReferenceType).PrimaryKey;
            referencePrimaryKey.PropertyInfo.SetValue(proxy, foreignKeyValue, null);
            return proxy;
        }
</pre></div><p>When using an approach like this, it's best to make everything in your entity classes virtual... NHibernate has a similar restriction and i've tried to explain the reasons behind this in <a
href="http://davybrion.com/blog/2009/03/must-everything-be-virtual-with-nhibernate/">this post</a>.</p><p>I'm not sure if i succeeded in explaining this topic in a simple and clear manner, but this technique really is pretty easy.  If you have any questions, i'd be glad to anser them in the comments <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-lazy-loading%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/"  data-text="Build Your Own Data Access Layer: Lazy Loading" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/feed/</wfw:commentRss> <slash:comments>16</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Session Level Cache</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/#comments</comments> <pubDate>Wed, 26 Aug 2009 15:11:28 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1526</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. In the previous 2 posts of this series, you may have noticed the usage of the SessionLevelCache class. This class is a simple implementation of the Identity Map pattern. It basically offers us two important benefits: It ensures that we [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>In the previous 2 posts of this series, you may have noticed the usage of the SessionLevelCache class. This class is a simple implementation of the <a
href="http://martinfowler.com/eaaCatalog/identityMap.html">Identity Map pattern</a>.  It basically offers us two important benefits:</p><ol><li>It ensures that we never load the same entity from the database twice.</li><li>It ensures that we can't accidentally have two instances which point to the same database record.</li></ol><p>Well actually... there is one situation where you could possibly load the same entity twice (which i'll show momentarily) but even then we can make sure that our users will always have the same instance of the entity.</p><p>The SessionLevelCache class is actually very simple:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class SessionLevelCache
    {
        private readonly Dictionary&lt;Type, Dictionary&lt;string, object&gt;&gt; cache = new Dictionary&lt;Type, Dictionary&lt;string, object&gt;&gt;();
 
        public object TryToFind(Type type, object id)
        {
            if (!cache.ContainsKey(type)) return null;
 
            string idAsString = id.ToString();
            if (!cache[type].ContainsKey(idAsString)) return null;
 
            return cache[type][idAsString];
        }
 
        public void Store(Type type, object id, object entity)
        {
            if (!cache.ContainsKey(type)) cache.Add(type, new Dictionary&lt;string, object&gt;());
 
            cache[type][id.ToString()] = entity;
        }
 
        public void ClearAll()
        {
            cache.Clear();
        }
 
        public void RemoveAllInstancesOf(Type type)
        {
            if (cache.ContainsKey(type))
            {
                cache.Remove(type);
            }
        }
 
        public void Remove(object entity)
        {
            var type = entity.GetType();
 
            if (!cache.ContainsKey(type)) return;
 
            string keyToRemove = null;
 
            foreach (var pair in cache[type])
            {
                if (pair.Value == entity)
                {
                    keyToRemove = pair.Key;
                }
            }
 
            if (keyToRemove != null)
            {
                cache[type].Remove(keyToRemove);
            }
        }
    }
</pre></div><p>It uses a nested dictionary where the outer dictionary uses the entity type as its key value, and stores an inner dictionary for each entity type which uses the primary key value as the key for each entity instance.  I think the code is simple enough so there's not really any point to go over the implementation details.  Instead, let's focus on when and where the SessionLevelCache is used.</p><p>First, let's look back at the Get method of the GetByIdAction class:</p><div><pre class="brush: csharp; title: ; notranslate">
        public TEntity Get&lt;TEntity&gt;(object id)
        {
            var cachedEntity = SessionLevelCache.TryToFind(typeof(TEntity), id);
            if (cachedEntity != null) return (TEntity)cachedEntity;
 
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
                var query = tableInfo.GetSelectStatementForAllFields();
                tableInfo.AddWhereByIdClause(query);
 
                command.CommandText = query.ToString();
                command.CreateAndAddInputParameter(tableInfo.PrimaryKey.DbType, tableInfo.GetPrimaryKeyParameterName(), id);
                return Hydrater.HydrateEntity&lt;TEntity&gt;(command);
            }
        }
</pre></div><p>As you can see, when we enter this method we first check to see whether the entity instance is already present in the cache.  If it is, we obviously don't need to hit the database so we can simply return the cached entity.</p><p>But like i said, there is one situation where we could potentially retrieve the entity from the database even though we already have it in the cache, and that is when we are hydrating a list of entities that have been retrieved from the database.  You might remember the following method from the EntityHydrater class:</p><div><pre class="brush: csharp; title: ; notranslate">
        private TEntity CreateEntityFromValues&lt;TEntity&gt;(IDictionary&lt;string, object&gt; values)
        {
            var tableInfo = metaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
            var cachedEntity = sessionLevelCache.TryToFind(typeof(TEntity), values[tableInfo.PrimaryKey.Name]);
            if (cachedEntity != null) return (TEntity)cachedEntity;
 
            var entity = Activator.CreateInstance&lt;TEntity&gt;();
            Hydrate(tableInfo, entity, values);
            sessionLevelCache.Store(typeof(TEntity), values[tableInfo.PrimaryKey.Name], entity);
            return entity;
        }
</pre></div><p>This method is used to hydrate every single entity that we create.  Obviously, when hydrating a single entity we've actually already performed the cache-look-up in the GetByIdAction.  However, when a custom query is executed, or when all instances are retrieved, there is no way for us to exclude already cached entity instances from the result of the query.  Well, theoretically speaking you could attempt to do this by adding a clause to the WHERE statement of each query that would prevent cached entities from being loaded.  But then you might have to add the cached entity instances to the resulting list of entities anyways if they would otherwise satisfy the other query conditions.  Obviously, trying to get this right is simply put insane and i don't think there's any DAL or ORM that actually does this (even if there was, i can't really imagine any of them getting this right in every corner case that will pop up).</p><p>So a good compromise is to simply check for the existence of a specific instance in the cache before hydrating a new instance.  If it is there, we return it from the cache and we skip the hydration for that database record.  In this way, we avoid having to modify the original query, and while we could potentially return a few records that we already have in memory, at least we will be sure that our users will always have the same reference for any particular database record.</p><p>There is one more scenario that needs to be covered.  If an entity holds a reference through a foreign key to another entity instance, and that referenced entity is already present in the cache, we need to make sure that the entity we are hydrating will refer to the already cached referred-to-instance instead of creating a proxy by default.  After all, if we were to create a proxy object for an entity instance that is already in our cache, we will have failed to achieve our goal of avoiding the possibility of more than one instance representing the same database record.  Therefore, when hydrating the reference properties of an entity, we have the following piece of code:</p><div><pre class="brush: csharp; title: ; notranslate">
                        var referencedEntity = sessionLevelCache.TryToFind(referenceInfo.ReferenceType, foreignKeyValue) ??
                                               CreateProxy(tableInfo, referenceInfo, foreignKeyValue);
 
                        referenceInfo.PropertyInfo.SetValue(entity, referencedEntity, null);
</pre></div><p>This way, we are sure that a proxy object for a referenced entity will only be created if that entity is not already present in the SessionLevelCache.</p><p>Now we're pretty much covered when it comes to retrieving entity instances from the database.  But we obviously also need to update the SessionLevelCache whenever an entity is inserted, and whenever an entity is deleted.  In the InsertAction, you can find the following code at the end of the Insert method:</p><div><pre class="brush: csharp; title: ; notranslate">
                SessionLevelCache.Store(typeof(TEntity), id, entity);
</pre></div><p>And in the DeleteAction we can also spot the following line of code:</p><div><pre class="brush: csharp; title: ; notranslate">
                SessionLevelCache.Remove(entity);
</pre></div><p>There is still one problem however.  When a user executes a custom DELETE statement, there is no way for us to know which entities were actually removed from the database.  But if any of those deleted entities happen to remain in the SessionLevelCache, this could lead to buggy application code whenever a piece of code tries to retrieve a specific entity which has already been removed from the database, but is still present in the SessionLevelCache.  In order to deal with this scenario, the SessionLevelCache has a ClearAll and a RemoveAllInstancesOf method which you can use from your application code to either clear the entire SessionLevelCache, or to remove all instances of a specific entity type from the cache.  Calling these methods would obviously be the responsability of the application code, since we can't possibly take care of this automatically in such a simplistic DAL.  Actually, even for powerful ORM's this can be pretty difficult to get right.</p><p>Another thing i'd like to point out is that the SessionLevelCache is not threadsafe.  A session (which will be covered in a later post) is not threadsafe, so within each of the classes that are used by the session i take no care of thread-safety whatsoever.</p><p>The SessionLevelCache might not seem like much to you, but i do consider its to be an absolute must for any DAL.  Notice that this doesn't even come close to a proper second-level cache like NHibernate offers, but the complexity of implementing such a thing is way beyond the scope of both this series and probably most custom DAL's out there.</p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-session-level-cache%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/"  data-text="Build Your Own Data Access Layer: Session Level Cache" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/feed/</wfw:commentRss> <slash:comments>10</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Hydrating Entities</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/#comments</comments> <pubDate>Tue, 25 Aug 2009 15:42:31 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1524</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. In the previous post of this series, you saw that some of the DatabaseActions use the EntityHydrater to umm.. hydrate the entities with their values from the database. In this post, we'll go over how this actually works. First, i'm [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>In the <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/">previous post</a> of this series, you saw that some of the DatabaseActions use the EntityHydrater to umm.. hydrate the entities with their values from the database.  In this post, we'll go over how this actually works.  First, i'm going to post the code of the entire class and then we'll go over the interesting parts.</p><p>So, here's the entire code of the EntityHydrater class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class EntityHydrater
    {
        private readonly MetaDataStore metaDataStore;
        private readonly SessionLevelCache sessionLevelCache;
 
        public EntityHydrater(MetaDataStore metaDataStore, SessionLevelCache sessionLevelCache)
        {
            this.metaDataStore = metaDataStore;
            this.sessionLevelCache = sessionLevelCache;
        }
 
        public TEntity HydrateEntity&lt;TEntity&gt;(SqlCommand command)
        {
            IDictionary&lt;string, object&gt; values;
 
            using (var reader = command.ExecuteReader())
            {
                if (!reader.HasRows) return default(TEntity);
                reader.Read();
                values = GetValuesFromCurrentRow(reader);
            }
 
            return CreateEntityFromValues&lt;TEntity&gt;(values);
        }
 
        public IEnumerable&lt;TEntity&gt; HydrateEntities&lt;TEntity&gt;(SqlCommand command)
        {
            var rows = new List&lt;IDictionary&lt;string, object&gt;&gt;();
            var entities = new List&lt;TEntity&gt;();
 
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    rows.Add(GetValuesFromCurrentRow(reader));
                }
            }
 
            foreach (var row in rows)
            {
                entities.Add(CreateEntityFromValues&lt;TEntity&gt;(row));
            }
 
            return entities;
        }
 
        private IDictionary&lt;string, object&gt; GetValuesFromCurrentRow(SqlDataReader dataReader)
        {
            var values = new Dictionary&lt;string, object&gt;();
 
            for (int i = 0; i &lt; dataReader.FieldCount; i++)
            {
                values.Add(dataReader.GetName(i), dataReader.GetValue(i));
            }
 
            return values;
        }
 
        private TEntity CreateEntityFromValues&lt;TEntity&gt;(IDictionary&lt;string, object&gt; values)
        {
            var tableInfo = metaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
            var cachedEntity = sessionLevelCache.TryToFind(typeof(TEntity), values[tableInfo.PrimaryKey.Name]);
            if (cachedEntity != null) return (TEntity)cachedEntity;
 
            var entity = Activator.CreateInstance&lt;TEntity&gt;();
            Hydrate(tableInfo, entity, values);
            sessionLevelCache.Store(typeof(TEntity), values[tableInfo.PrimaryKey.Name], entity);
            return entity;
        }
 
        private void Hydrate&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            tableInfo.PrimaryKey.PropertyInfo.SetValue(entity, values[tableInfo.PrimaryKey.Name], null);
            SetRegularColumns(tableInfo, entity, values);
            SetReferenceProperties(tableInfo, entity, values);
        }
 
        private void SetRegularColumns&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            foreach (var columnInfo in tableInfo.Columns)
            {
                if (columnInfo.PropertyInfo.CanWrite)
                {
                    object value = values[columnInfo.Name];
                    if (value is DBNull) value = null;
                    columnInfo.PropertyInfo.SetValue(entity, value, null);
                }
            }
        }
 
        private void SetReferenceProperties&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            foreach (var referenceInfo in tableInfo.References)
            {
                if (referenceInfo.PropertyInfo.CanWrite)
                {
                    object foreignKeyValue = values[referenceInfo.Name];
 
                    if (foreignKeyValue is DBNull)
                    {
                        referenceInfo.PropertyInfo.SetValue(entity, null, null);
                    }
                    else
                    {
                        var referencedEntity = sessionLevelCache.TryToFind(referenceInfo.ReferenceType, foreignKeyValue) ??
                                               CreateProxy(tableInfo, referenceInfo, foreignKeyValue);
 
                        referenceInfo.PropertyInfo.SetValue(entity, referencedEntity, null);
                    }
                }
            }
        }
 
        private object CreateProxy(TableInfo tableInfo, ReferenceInfo referenceInfo, object foreignKeyValue)
        {
            // NOTE: this will be covered in a later post, so i took out the spoiler code 
            return null;
        }
    }
</pre></div><p>As you can see, there are 2 public methods: HydrateEntity and HydrateEntities.  They are both pretty similar, except that the former only hydrates a single entity and the latter a list of entities.  They both retrieve the values from the current position in a DataReader and store them in a Dictionary with the name of the column being the key and the value of the column being the value in the dictionary:</p><div><pre class="brush: csharp; title: ; notranslate">
        private IDictionary&lt;string, object&gt; GetValuesFromCurrentRow(SqlDataReader dataReader)
        {
            var values = new Dictionary&lt;string, object&gt;();
 
            for (int i = 0; i &lt; dataReader.FieldCount; i++)
            {
                values.Add(dataReader.GetName(i), dataReader.GetValue(i));
            }
 
            return values;
        }
</pre></div><p>This dictionary is then passed to the CreateEntityFromValues method:</p><div><pre class="brush: csharp; title: ; notranslate">
        private TEntity CreateEntityFromValues&lt;TEntity&gt;(IDictionary&lt;string, object&gt; values)
        {
            var tableInfo = metaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
            var cachedEntity = sessionLevelCache.TryToFind(typeof(TEntity), values[tableInfo.PrimaryKey.Name]);
            if (cachedEntity != null) return (TEntity)cachedEntity;
 
            var entity = Activator.CreateInstance&lt;TEntity&gt;();
            Hydrate(tableInfo, entity, values);
            sessionLevelCache.Store(typeof(TEntity), values[tableInfo.PrimaryKey.Name], entity);
            return entity;
        }
</pre></div><p>This method will first check to see if an entity instance for the row in the values dictionary already exists.  If it exists already, it simply returns the entity instance and ignores the values in the dictionary.  I'm not sure yet whether this behavior is correct or not.  Theoretically speaking, it's possible when using ReadCommitted isolation level that the values in the dictionary will be more recent than the entity instance in the session level cache.  However, since this simple DAL has no change tracking, i also can't deduce whether the instance in the cache has already had one or more of its properties updated by application code.  Simply overwriting them with newly retrieved values of the database doesn't seem like the right thing to do here.  So the current options are to either ignore this possibility, or to rely on optimistic concurrency.  Oh, and i don't have support for optimistic concurrency either.  A good DAL should however provide some optimistic concurrency strategies here, or be able to track the changes in the entity and throw an exception if it notices that the database contains more recent values AND the local entity has already been modified.  If it hasn't been modified yet, it could overwrite the values of the instances though i'm not sure everyone would agree with this behavior.  Either way, this particular problem is definitely interesting enough to think about for a while <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><p>Anyways, in this implementation, i'm completely ignoring this situation and i either return the already loaded entity of the session level cache, or i hydrate a new entity instance and return that instead.  In the latter case, i also store the entity instance in the session level cache.  Note that the actual implementation of the session level cache will be covered in the next post of this series.</p><p>The actual Hydrate method looks like this:</p><div><pre class="brush: csharp; title: ; notranslate">
        private void Hydrate&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            tableInfo.PrimaryKey.PropertyInfo.SetValue(entity, values[tableInfo.PrimaryKey.Name], null);
            SetRegularColumns(tableInfo, entity, values);
            SetReferenceProperties(tableInfo, entity, values);
        }
</pre></div><p>First the primary key value of the record is set in the primary key property of the new instance, and then we proceed with putting the regular column values in their properties, and after that, the reference properties. Filling the regular column properties is very straightforward:</p><div><pre class="brush: csharp; title: ; notranslate">
        private void SetRegularColumns&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            foreach (var columnInfo in tableInfo.Columns)
            {
                if (columnInfo.PropertyInfo.CanWrite)
                {
                    object value = values[columnInfo.Name];
                    if (value is DBNull) value = null;
                    columnInfo.PropertyInfo.SetValue(entity, value, null);
                }
            }
        }
</pre></div><p>Dealing with the references is a bit more interesting though.</p><div><pre class="brush: csharp; title: ; notranslate">
        private void SetReferenceProperties&lt;TEntity&gt;(TableInfo tableInfo, TEntity entity, IDictionary&lt;string, object&gt; values)
        {
            foreach (var referenceInfo in tableInfo.References)
            {
                if (referenceInfo.PropertyInfo.CanWrite)
                {
                    object foreignKeyValue = values[referenceInfo.Name];
 
                    if (foreignKeyValue is DBNull)
                    {
                        referenceInfo.PropertyInfo.SetValue(entity, null, null);
                    }
                    else
                    {
                        var referencedEntity = sessionLevelCache.TryToFind(referenceInfo.ReferenceType, foreignKeyValue) ??
                                               CreateProxy(tableInfo, referenceInfo, foreignKeyValue);
 
                        referenceInfo.PropertyInfo.SetValue(entity, referencedEntity, null);
                    }
                }
            }
        }
</pre></div><p>If we can't find the referenced entity instance in the first level cache, what should we do? We obviously can't load it automatically because that could in turn cause referenced entities' references to be loaded automatically when they are hydrated.  Which in turn could cause their referenced entities... Well, i'm sure you get the point.  But those properties obviously can't be set to a null reference either because the column actually does have a valid foreign key value in the database.  Explicitly loading referenced properties leads to seriously ugly (and error-prone) code so that's not an option i'm willing to consider either.  The correct way to deal with this is to use lazy loading.  To do that in an automated fashion, we need proxy classes.  I'm not going to get into these proxy classes and the whole lazy loading thing just yet, since that will be covered in depth in a future post <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><p>So that's pretty much it (for now) for our EntityHydrater class.  As you can see, it's still relatively simple but then again, the use cases that it supports are extremely simple as well.  This current implementation is incapable of hydrating entities based on a SQL statement that selects data from more than just the entity's table.  And that is a pretty big shortcoming.  For instance, with NHibernate you can execute queries where you can instruct NHibernate to fetch some (or all) of the entity's references (associations in NHibernate) with just one SQL statement, using the join syntax.  NHibernate can then hydrate the root entity, and populate its reference properties with the other values that were returned by the sql statement.  While it wouldn't be that complex to add this capability to this EntityHydrater class, it wouldn't exactly be completely trivial either.  Again, this is a limitation that many (maybe even most?) custom DAL's have.  This one probably makes it easy enough to still add this feature though <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-hydrating-entities%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/"  data-text="Build Your Own Data Access Layer: Hydrating Entities" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/feed/</wfw:commentRss> <slash:comments>15</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Out Of The Box CRUD Functionality</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/#comments</comments> <pubDate>Mon, 24 Aug 2009 17:29:19 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1522</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. One thing that i consider an absolute must-have in any data access layer is the ability to perform CRUD operations out-of-the-box without having to write any code to enable these operations. Once your data access layer knows about your classes [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>One thing that i consider an absolute must-have in any data access layer is the ability to perform CRUD operations out-of-the-box without having to write any code to enable these operations.  Once your data access layer knows about your classes and your tables, CRUD operations should 'just work'.</p><p>As you've seen in the <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/">previous post</a> of this series, the TableInfo class offers a couple of methods to automatically build the required SQL statements for CRUD actions.  With these statements, we can easily create SqlCommand instances for all CRUD operations.</p><p>First of all, i use the following helper method to easily add a SqlParameter to a SqlCommand:</p><div><pre class="brush: csharp; title: ; notranslate">
        public static void CreateAndAddInputParameter(this SqlCommand command, DbType type, string name, object value)
        {
            var parameter = command.CreateParameter();
            parameter.Direction = ParameterDirection.Input;
            parameter.DbType = type;
            parameter.ParameterName = name;
 
            if (value == null)
            {
                parameter.IsNullable = true;
                parameter.Value = DBNull.Value;
            }
            else
            {
                parameter.Value = value;
            }
 
            command.Parameters.Add(parameter);
        }
</pre></div><p>I also have the following abstract DatabaseAction class which has a few properties that are used by most of the CRUD actions:</p><div><pre class="brush: csharp; title: ; notranslate">
    public abstract class DatabaseAction
    {
        private readonly SqlConnection connection;
        private readonly SqlTransaction transaction;
        protected MetaDataStore MetaDataStore { get; private set; }
        protected EntityHydrater Hydrater { get; private set; }
        protected SessionLevelCache SessionLevelCache { get; private set; }
 
        protected DatabaseAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
                                 EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
        {
            this.connection = connection;
            this.transaction = transaction;
            MetaDataStore = metaDataStore;
            Hydrater = hydrater;
            SessionLevelCache = sessionLevelCache;
        }
 
        protected SqlCommand CreateCommand()
        {
            var command = connection.CreateCommand();
            command.Transaction = transaction;
            return command;
        }
    }
</pre></div><p>Did you notice the EntityHydrater and SessionLevelCache? I'm going to ignore those as much as possible for now, since they will be covered in depth in the following two posts in these series.  The important thing to note is that each derived DatabaseAction will have a reference to the MetaDataStore.</p><p>And now we can easily start implementing our CRUD actions.  Let's start with the GetByIdAction:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class GetByIdAction : DatabaseAction
    {
        public GetByIdAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public TEntity Get&lt;TEntity&gt;(object id)
        {
            var cachedEntity = SessionLevelCache.TryToFind(typeof(TEntity), id);
            if (cachedEntity != null) return (TEntity)cachedEntity;
 
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
                var query = tableInfo.GetSelectStatementForAllFields();
                tableInfo.AddWhereByIdClause(query);
 
                command.CommandText = query.ToString();
                command.CreateAndAddInputParameter(tableInfo.PrimaryKey.DbType, tableInfo.GetPrimaryKeyParameterName(), id);
                return Hydrater.HydrateEntity&lt;TEntity&gt;(command);
            }
        }
    }
</pre></div><p>Pretty simple stuff, right?  This will first check the session level cache to see if this instance has already been retrieved in the current session (i'll discuss the session in a later post) and if so, it will return that instance.  If it's not in the cache, it will create a SqlCommand and fill its CommandText property with a SQL string that is provided by the relevant TableInfo class.   After that, it passes the SqlCommand to the EntityHydrater so it can return an actual entity instance.</p><p>The details of EntityHydration will be fully explored in the next post of this series, so for now you only need to know that it can transform the results from the SqlCommand to an instance of TEntity.</p><p>It's always useful to get a collection of all instances of a certain entity class, so we also have this very simple FindAllAction:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class FindAllAction : DatabaseAction
    {
        public FindAllAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public IEnumerable&lt;TEntity&gt; FindAll&lt;TEntity&gt;()
        {
            using (var command = CreateCommand())
            {
                command.CommandText = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;().GetSelectStatementForAllFields().ToString();
                return Hydrater.HydrateEntities&lt;TEntity&gt;(command);
            }
        }
    }

</pre></div><p>We also need an InsertAction:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class InsertAction : DatabaseAction
    {
        public InsertAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public TEntity Insert&lt;TEntity&gt;(TEntity entity)
        {
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
                command.CommandText = tableInfo.GetInsertStatement();
 
                foreach (var parameterInfo in tableInfo.GetParametersForInsert(entity))
                {
                    command.CreateAndAddInputParameter(parameterInfo.DbType, parameterInfo.Name, parameterInfo.Value);
                }
 
                object id = Convert.ChangeType(command.ExecuteScalar(), tableInfo.PrimaryKey.DotNetType);
                tableInfo.PrimaryKey.PropertyInfo.SetValue(entity, id, null);
                SessionLevelCache.Store(typeof(TEntity), id, entity);
                return entity;
            }
        }
    }
</pre></div><p>There's not a lot to this one either... The actual insert statement is once again retrieved through the TableInfo class, as are the parameter values (including their values for this specific entity).  You can go back to the previous post to look at the implementation of TableInfo's GetParametersForInsert method <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p><p>Keep in mind that there is a limitation here that i only support SQL Server's Identity-style generators.  Again, if you want to support multiple identifier strategies like NHibernate does, you'll have to deal with a lot more complexity in the InsertAction class.</p><p>The UpdateAction is very similar:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class UpdateAction : DatabaseAction
    {
        public UpdateAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public TEntity Update&lt;TEntity&gt;(TEntity entity)
        {
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
 
                command.CommandText = tableInfo.GetUpdateStatement();
 
                foreach (var parameterInfo in tableInfo.GetParametersForUpdate(entity))
                {
                    command.CreateAndAddInputParameter(parameterInfo.DbType, parameterInfo.Name, parameterInfo.Value);
                }
 
                command.ExecuteNonQuery();
                return entity;
            }
        }
    }
</pre></div><p>And finally, we have the DeleteAction:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class DeleteAction : DatabaseAction
    {
        public DeleteAction(SqlConnection connection, SqlTransaction transaction, MetaDataStore metaDataStore,
            EntityHydrater hydrater, SessionLevelCache sessionLevelCache)
            : base(connection, transaction, metaDataStore, hydrater, sessionLevelCache)
        {
        }
 
        public void Delete&lt;TEntity&gt;(TEntity entity)
        {
            using (var command = CreateCommand())
            {
                var tableInfo = MetaDataStore.GetTableInfoFor&lt;TEntity&gt;();
                command.CommandText = tableInfo.GetDeleteStatement();
                object id = tableInfo.PrimaryKey.PropertyInfo.GetValue(entity, null);
                command.CreateAndAddInputParameter(tableInfo.PrimaryKey.DbType, tableInfo.GetPrimaryKeyParameterName(), id);
                command.ExecuteNonQuery();
                SessionLevelCache.Remove(entity);
            }
        }
    }
</pre></div><p>And that's all there is to it.  We now have some classes that will give us out-of-the-box CRUD functionality for all of the mapped entity classes.  Obviously, you will still need some way of actually accessing this functionality from your application code and you certainly don't want to instantiate and use these DatabaseAction classes directly.  All of that will be covered in the "Bringing It All Together" post, so stay tuned <img
src='http://d18sni7re4ly7f.cloudfront.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-out-of-the-box-crud-functionality%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/"  data-text="Build Your Own Data Access Layer: Out Of The Box CRUD Functionality" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/feed/</wfw:commentRss> <slash:comments>12</slash:comments> </item> <item><title>Build Your Own Data Access Layer: Mapping Classes To Tables</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/#comments</comments> <pubDate>Sun, 23 Aug 2009 15:08:47 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1520</guid> <description><![CDATA[Note: This post is part of a series. Be sure to read the introduction here. When you need to populate entity instances with data from a database, you need to know which table the data needs to come from, which columns will map to which property on the entity class, and you'll need to deal [...]]]></description> <content:encoded><![CDATA[<p>Note: This post is part of a series.  Be sure to read the introduction <a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/">here</a>.</p><p>When you need to populate entity instances with data from a database, you need to know which table the data needs to come from, which columns will map to which property on the entity class, and you'll need to deal with a variety of types.  The approach that i've chosen to use tries to make this as simple as possible.  The idea is basically to place an attribute with the name of the table on top of the entity class, and an attribute on each property with the name of the column it maps to.  For foreign keys, i wanted to be able to just use properties of the type of the referenced entity, instead of having foreign keys in my entities.  For these references, we will use an attribute with the name of the foreign key column.</p><p>First, we'll need to define these attributes:</p><div><pre class="brush: csharp; title: ; notranslate">
    [AttributeUsage(AttributeTargets.Class, Inherited = true)]
    public class TableAttribute : Attribute
    {
        public string TableName { get; private set; }
 
        public TableAttribute(string tableName)
        {
            TableName = tableName;
        }
    }

    [AttributeUsage(AttributeTargets.Property, Inherited = true)]
    public class PrimaryKeyAttribute : Attribute
    {
        public string ColumnName { get; private set; }
 
        public PrimaryKeyAttribute(string columnName)
        {
            ColumnName = columnName;
        }
    }

    [AttributeUsage(AttributeTargets.Property, Inherited = true)]
    public class ColumnAttribute : Attribute
    {
        public string ColumnName { get; private set; }
 
        public ColumnAttribute(string columnName)
        {
            ColumnName = columnName;
        }
    }

    [AttributeUsage(AttributeTargets.Property, Inherited = true)]
    public class ReferenceAttribute : Attribute
    {
        public string ColumnName { get; private set; }
 
        public ReferenceAttribute(string columnName)
        {
            ColumnName = columnName;
        }
    }
</pre></div><p>Notice how none of these properties have any indication of types to use.  The .NET type will be inferred automatically, and it will be mapped to a compatible DbType without having to specify these types all over the place.</p><p>We will use the following helper class to map .NET types to their respective DbTypes:</p><div><pre class="brush: csharp; title: ; notranslate">
    public static class TypeConverter
    {
        private static readonly Dictionary&lt;Type, DbType&gt; typeToDbType = new Dictionary&lt;Type, DbType&gt;
        {
            { typeof(string), DbType.String },
            { typeof(DateTime), DbType.DateTime },
            { typeof(DateTime?), DbType.DateTime },
            { typeof(int), DbType.Int32 },
            { typeof(int?), DbType.Int32 },
            { typeof(long), DbType.Int64 },
            { typeof(long?), DbType.Int64 },
            { typeof(bool), DbType.Boolean },
            { typeof(bool?), DbType.Boolean },
            { typeof(byte[]), DbType.Binary },
            { typeof(decimal), DbType.Decimal },
            { typeof(decimal?), DbType.Decimal },
            { typeof(double), DbType.Double },
            { typeof(double?), DbType.Double },
            { typeof(float), DbType.Single },
            { typeof(float?), DbType.Single },
            { typeof(Guid), DbType.Guid },
            { typeof(Guid?), DbType.Guid }
        };
 
        public static DbType ToDbType(Type type)
        {
            if (!typeToDbType.ContainsKey(type))
            {
                throw new InvalidOperationException(string.Format(&quot;Type {0} doesn't have a matching DbType configured&quot;, type.FullName));
            }
 
            return typeToDbType[type];
        }
    }
</pre></div><p>Obviously, more type conversions can be added... these are just the ones i've needed so far.</p><p>Once you've placed all the attributes on top of your entities and properties, we can start building a model of all this metadata.  This will all be stored in a MetaDataStore class that i'll show later on in this post.  Having access to the MetaDataStore makes the implementation of some of these metadata types easier, so i have the following abstract class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public abstract class MetaData
    {
        protected MetaDataStore MetaDataStore { get; private set; }
 
        protected MetaData(MetaDataStore metaDataStore)
        {
            MetaDataStore = metaDataStore;
        }
    }
</pre></div><p>Now we can go over each piece of metadata.  First, the ColumnInfo class:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class ColumnInfo : MetaData
    {
        public string Name { get; private set; }
        public Type DotNetType { get; private set; }
        public DbType DbType { get; private set; }
        public PropertyInfo PropertyInfo { get; private set; }
 
        public ColumnInfo(MetaDataStore store, string name, Type dotNetType, PropertyInfo propertyInfo)
            : this(store, name, dotNetType, TypeConverter.ToDbType(dotNetType), propertyInfo)
        {
        }
 
        public ColumnInfo(MetaDataStore store, string name, Type dotNetType, DbType dbType, PropertyInfo propertyInfo)
            : base(store)
        {
            Name = name;
            DotNetType = dotNetType;
            DbType = dbType;
            PropertyInfo = propertyInfo;
        }
    }
</pre></div><p>As you can see, we have all the information we need to be able to do something with this column.  We have its Name, the .NET type that is used in the mapped class, the DbType and a PropertyInfo reference to its respective property in the mapped class so we can get and set its value.</p><p>For references, we need to know something more:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class ReferenceInfo : ColumnInfo
    {
        public Type ReferenceType { get; private set; }
 
        public ReferenceInfo(MetaDataStore store, string name, Type referenceType, PropertyInfo propertyInfo)
            : base(store, name, store.GetTableInfoFor(referenceType).PrimaryKey.DotNetType,
                    store.GetTableInfoFor(referenceType).PrimaryKey.DbType, propertyInfo)
        {
            ReferenceType = referenceType;
        }
    }
</pre></div><p>For a regular column, it's sufficient to know the .NET type of the property and the DbType.  But for a reference, you need to know the actual type of the referenced entity, as well as the .NET type of it's primary key column.  As you can see in the constructor, we retrieve the TableInfo of the referenced entity, and use the .NET type and the DbType of the primary key of the referenced entity.  The PrimaryKey property of a TableInfo class (which i'll show below) is also a ColumnInfo object.  We obviously also store the actual type of the referenced entity.  And of course, we again store a PropertyInfo so we can get/set the value of the reference.</p><p>The TableInfo class can now hold all of the information that we need.  We know all about its primary key (through the PrimaryKeyAttribute), its regular properties (through the ColumnAttribute) and its referenced properties (through the ReferenceAttribute).  With all of that information, the TableInfo class is able to build your typical default SQL statements for CRUD functionality:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class TableInfo : MetaData
    {
        public string Name { get; private set; }
        public Type EntityType { get; private set; }
        public ColumnInfo PrimaryKey { get; set; }
        public IEnumerable&lt;ReferenceInfo&gt; References { get { return references.Values; } }
        public IEnumerable&lt;ColumnInfo&gt; Columns { get { return columns.Values; } }
 
        private readonly Dictionary&lt;string, ColumnInfo&gt; columns = new Dictionary&lt;string, ColumnInfo&gt;();
        private readonly Dictionary&lt;string, ReferenceInfo&gt; references = new Dictionary&lt;string, ReferenceInfo&gt;();
 
        public TableInfo(MetaDataStore store, string name, Type entityType)
            : base(store)
        {
            Name = name;
            EntityType = entityType;
        }
 
        public void AddColumn(ColumnInfo column)
        {
            if (columns.ContainsKey(column.Name))
            {
                throw new InvalidOperationException(string.Format(&quot;An item with key {0} has already been added&quot;, column.Name));
            }
 
            columns.Add(column.Name, column);
        }
 
        public void AddReference(ReferenceInfo reference)
        {
            if (references.ContainsKey(reference.Name))
            {
                throw new InvalidOperationException(string.Format(&quot;An item with key {0} has already been added&quot;, reference.Name));
            }
 
            references.Add(reference.Name, reference);
        }
 
        public ColumnInfo GetColumn(string columnName)
        {
            if (!columns.ContainsKey(columnName))
            {
                throw new InvalidOperationException(string.Format(&quot;The table '{0}' does not have a '{1}' column&quot;, Name, columnName));
            }
 
            return columns[columnName];
        }
 
        public StringBuilder GetSelectStatementForAllFields()
        {
            StringBuilder builder = new StringBuilder(&quot;SELECT &quot; + Escape(PrimaryKey.Name) + &quot;, &quot;);
 
            AddReferenceColumnNames(builder);
            AddRegularColumnNames(builder);
            RemoveLastCommaAndSpaceIfThereAreAnyColumns(builder);
            builder.Append(&quot; FROM &quot; + Escape(Name));
 
            return builder;
        }
 
        public string GetInsertStatement()
        {
            StringBuilder builder = new StringBuilder(&quot;INSERT INTO &quot; + Escape(Name) + &quot; (&quot;);
 
            AddReferenceColumnNames(builder);
            AddRegularColumnNames(builder);
            RemoveLastCommaAndSpaceIfThereAreAnyColumns(builder);
            builder.Append(&quot;) VALUES (&quot;);
            AddReferenceColumnParameterNames(builder);
            AddRegularColumnParameterNames(builder);
            RemoveLastCommaAndSpaceIfThereAreAnyColumns(builder);
            builder.Append(&quot;); SELECT SCOPE_IDENTITY();&quot;);
 
            return builder.ToString();
        }
 
        public string GetUpdateStatement()
        {
            StringBuilder builder = new StringBuilder(&quot;UPDATE &quot; + Escape(Name) + &quot; SET &quot;);
 
            AddReferenceColumnsNameWithParameterName(builder);
            AddRegularColumnsNameWithParameterName(builder);
            RemoveLastCommaAndSpaceIfThereAreAnyColumns(builder);
            AddWhereByIdClause(builder);
            builder.Append(&quot;;&quot;);
 
            return builder.ToString();
        }
 
        public string GetDeleteStatement()
        {
            StringBuilder builder = new StringBuilder(&quot;DELETE FROM &quot; + Escape(Name) + &quot; &quot;);
 
            AddWhereByIdClause(builder);
            builder.Append(&quot;;&quot;);
 
            return builder.ToString();
        }
 
        public IEnumerable&lt;AdoParameterInfo&gt; GetParametersForInsert(object entity)
        {
            return GetParametersForAllReferenceAndRegularColumns(entity);
        }
 
        public IEnumerable&lt;AdoParameterInfo&gt; GetParametersForUpdate(object entity)
        {
            var parameters = GetParametersForAllReferenceAndRegularColumns(entity);
            parameters.Add(new AdoParameterInfo(PrimaryKey.Name, PrimaryKey.DbType, PrimaryKey.PropertyInfo.GetValue(entity, null)));
            return parameters;
        }
 
        public StringBuilder AddWhereByIdClause(StringBuilder query)
        {
            query.Append(&quot; WHERE &quot; + Escape(PrimaryKey.Name) + &quot; = &quot; + GetPrimaryKeyParameterName());
            return query;
        }
 
        public string GetPrimaryKeyParameterName()
        {
            return &quot;@&quot; + PrimaryKey.Name;
        }
 
        private List&lt;AdoParameterInfo&gt; GetParametersForAllReferenceAndRegularColumns(object entity)
        {
            var parameters = new List&lt;AdoParameterInfo&gt;();
 
            foreach (var referenceInfo in References)
            {
                var referencedEntity = referenceInfo.PropertyInfo.GetValue(entity, null);
                var referencePrimaryKeyProperty = MetaDataStore.GetTableInfoFor(referenceInfo.ReferenceType).PrimaryKey.PropertyInfo;
 
                if (referencedEntity == null)
                {
                    parameters.Add(new AdoParameterInfo(referenceInfo.Name, referenceInfo.DbType, null));
                }
                else
                {
                    parameters.Add(new AdoParameterInfo(referenceInfo.Name, referenceInfo.DbType, referencePrimaryKeyProperty.GetValue(referencedEntity, null)));
                }
            }
 
            foreach (var columnInfo in Columns)
            {
                parameters.Add(new AdoParameterInfo(columnInfo.Name, columnInfo.DbType, columnInfo.PropertyInfo.GetValue(entity, null)));
            }
 
            return parameters;
        }
 
        private void RemoveLastCommaAndSpaceIfThereAreAnyColumns(StringBuilder builder)
        {
            if ((References.Count() + Columns.Count()) &gt; 0)
            {
                RemoveLastCharacters(builder, 2);
            }
        }
 
        private void AddReferenceColumnNames(StringBuilder builder)
        {
            foreach (var referenceInfo in References)
            {
                builder.Append(Escape(referenceInfo.Name) + &quot;, &quot;);
            }
        }
 
        private void AddReferenceColumnParameterNames(StringBuilder builder)
        {
            foreach (var referenceInfo in References)
            {
                builder.Append(&quot;@&quot; + referenceInfo.Name + &quot;, &quot;);
            }
        }
 
        private void AddReferenceColumnsNameWithParameterName(StringBuilder builder)
        {
            foreach (var referenceInfo in References)
            {
                builder.Append(Escape(referenceInfo.Name) + &quot; = @&quot; + referenceInfo.Name + &quot;, &quot;);
            }
        }
 
        private void AddRegularColumnNames(StringBuilder builder)
        {
            foreach (var columnInfo in Columns)
            {
                builder.Append(Escape(columnInfo.Name) + &quot;, &quot;);
            }
        }
 
        private void AddRegularColumnParameterNames(StringBuilder builder)
        {
            foreach (var columnInfo in Columns)
            {
                builder.Append(&quot;@&quot; + columnInfo.Name + &quot;, &quot;);
            }
        }
 
        private void AddRegularColumnsNameWithParameterName(StringBuilder builder)
        {
            foreach (var columnInfo in Columns)
            {
                builder.Append(Escape(columnInfo.Name) + &quot; = @&quot; + columnInfo.Name + &quot;, &quot;);
            }
        }
 
        private string Escape(string name)
        {
            return &quot;[&quot; + name + &quot;]&quot;;
        }
 
        private void RemoveLastCharacters(StringBuilder stringBuilder, int numberOfCharacters)
        {
            stringBuilder.Remove(stringBuilder.Length - numberOfCharacters, numberOfCharacters);
        }
    }
</pre></div><p>This is actually the biggest class in this DAL. I probably should move the building of the SQL statements and providing parameter info into some kind of helper class because this is a bit of a Single Responsability Principle violation.  Speaking of parameter info, i'm using the following helper class to store this information:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class AdoParameterInfo
    {
        public DbType DbType { get; private set; }
        public string Name { get; private set; }
        public object Value { get; private set; }
 
        public AdoParameterInfo(string name, DbType dbType, object value)
        {
            Name = name;
            DbType = dbType;
            Value = value;
        }
    }
</pre></div><p>One thing that you may have noticed is that the generated INSERT statement assumes that SQL Server identity-style generators are being used for primary key values.  Not only that, i'm not even trying to target any other database then SQL Server with this DAL.  Those are 2 rather significant shortcomings of this DAL.  First of all, dealing with multiple identifier strategies can become pretty complex pretty fast.  For this DAL, SQL Server Identity primary keys are sufficient but in a lot of cases you will probably want support for assigned identifier strategies, for GUIDs (preferably locally generated with a sequential GUID algorithm), HiLo and maybe even other ones.  If you really want to, you can do all of this yourself, but you'll quickly spend an entire week (or more) to properly implement all of these identifier strategies.</p><p>As for only targeting SQL Server, that is sufficient in our scenario but a proper DAL should be able to deal with multiple databases.  Of course, this has a direct impact on a lot of implementation details.  For starters, you'd never be able to just construct a SQL statement directly in your code and you will need something to make sure the correct statements are generated for your specific database.  NHibernate does a pretty nice job of this by providing a strategy-like implementation through its Dialect class and its derivatives.  Also, some of your identifier strategies will be different for each database that you need to support.  If you got a headache just from reading these last 2 paragraphs, just imagine implementing this and getting it all 'right' in a maintainable matter.</p><p>Anyways, back to the topic at hand.  We now have the classes we need to build up our metadata model of all of the tables we need to provide data access functionality for.  Well, we still need something to hold all of this information and to actually build up this model:</p><div><pre class="brush: csharp; title: ; notranslate">
    public class MetaDataStore
    {
        private readonly Dictionary&lt;Type, TableInfo&gt; typeToTableInfo = new Dictionary&lt;Type, TableInfo&gt;();
 
        public TableInfo GetTableInfoFor&lt;TEntity&gt;()
        {
            return GetTableInfoFor(typeof(TEntity));
        }
 
        public TableInfo GetTableInfoFor(Type entityType)
        {
            if (!typeToTableInfo.ContainsKey(entityType))
            {
                return null;
            }
 
            return typeToTableInfo[entityType];
        }
 
        public void BuildMetaDataFor(Assembly assembly)
        {
            BuildMapOfEntityTypesWithTheirTableInfo(assembly);
 
            foreach (KeyValuePair&lt;Type, TableInfo&gt; pair in typeToTableInfo)
            {
                // we need this info for each entity before we can deal with references to other entities
                LoopThroughPropertiesWith&lt;PrimaryKeyAttribute&gt;(pair.Key, pair.Value, SetPrimaryKeyInfo);
            }
 
            foreach (KeyValuePair&lt;Type, TableInfo&gt; pair in typeToTableInfo)
            {
                LoopThroughPropertiesWith&lt;ReferenceAttribute&gt;(pair.Key, pair.Value, AddReferenceInfo);
                LoopThroughPropertiesWith&lt;ColumnAttribute&gt;(pair.Key, pair.Value, AddColumnInfo);
            }
        }
 
        private void BuildMapOfEntityTypesWithTheirTableInfo(Assembly assembly)
        {
            foreach (var type in assembly.GetTypes())
            {
                var typeAttributes = Attribute.GetCustomAttributes(type, typeof(TableAttribute));
 
                if (typeAttributes.Length &gt; 0)
                {
                    var tableAttribute = (TableAttribute)typeAttributes[0];
                    var tableInfo = new TableInfo(this, tableAttribute.TableName, type);
                    typeToTableInfo.Add(type, tableInfo);
                }
            }
        }
 
        private void LoopThroughPropertiesWith&lt;TAttribute&gt;(Type entityType, TableInfo tableInfo,
            Action&lt;TableInfo, PropertyInfo, TAttribute&gt; andExecuteFollowingCode)
            where TAttribute : Attribute
        {
            foreach (var propertyInfo in entityType.GetProperties())
            {
                var attribute = GetAttribute&lt;TAttribute&gt;(propertyInfo);
 
                if (attribute != null)
                {
                    andExecuteFollowingCode(tableInfo, propertyInfo, attribute);
                }
            }
        }
 
        private void SetPrimaryKeyInfo(TableInfo tableInfo, PropertyInfo propertyInfo, PrimaryKeyAttribute primaryKeyAttribute)
        {
            tableInfo.PrimaryKey = new ColumnInfo(this, primaryKeyAttribute.ColumnName, propertyInfo.PropertyType, propertyInfo);
        }
 
        private void AddColumnInfo(TableInfo tableInfo, PropertyInfo propertyInfo, ColumnAttribute columnAttribute)
        {
            tableInfo.AddColumn(new ColumnInfo(this, columnAttribute.ColumnName, propertyInfo.PropertyType, propertyInfo));
        }
 
        private void AddReferenceInfo(TableInfo tableInfo, PropertyInfo propertyInfo, ReferenceAttribute referenceAttribute)
        {
            tableInfo.AddReference(new ReferenceInfo(this, referenceAttribute.ColumnName, propertyInfo.PropertyType, propertyInfo));
        }
 
        private TAttribute GetAttribute&lt;TAttribute&gt;(PropertyInfo propertyInfo) where TAttribute : Attribute
        {
            var attributes = Attribute.GetCustomAttributes(propertyInfo, typeof(TAttribute));
            if (attributes.Length == 0) return null;
            return (TAttribute)attributes[0];
        }
    }
</pre></div><p>This class gives you the ability to retrieve the TableInfo class for a specfic entity type.  It also allows you to build the metadata model by passing in an assembly.  It will then loop through all of the types in the assembly to discover the types that have a TableAttribute, and it will then build the TableInfo objects with all of the information we need.</p><p>And that's all we need to create mappings between tables and our entities.  This wasn't hard, but it's not very powerful either.  We can't define custom user types that our DAL needs to be able to deal with, nor can we define any database inheritance strategies.  Our attributes are all inheritable, so you can use some inheritance with your entities, but you are essentially limited to the Table Per Class inheritance strategy.  Implementing support for the other inheritance strategies would obviously introduce a lot more complexity in the whole mapping aspect.</p><p>In the next post, i'll show you how this DAL will use TableInfo's methods to create CRUD statements to offer out-of-the-box CRUD functionality for each mapped entity.</p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-mapping-classes-to-tables%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/"  data-text="Build Your Own Data Access Layer: Mapping Classes To Tables" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/feed/</wfw:commentRss> <slash:comments>11</slash:comments> </item> <item><title>Build Your Own Data Access Layer Series</title><link>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/</link> <comments>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/#comments</comments> <pubDate>Sun, 23 Aug 2009 13:38:43 +0000</pubDate> <dc:creator>Davy Brion</dc:creator> <category><![CDATA[Build Your Own DAL]]></category><guid
isPermaLink="false">http://davybrion.com/blog/?p=1515</guid> <description><![CDATA[I'm definitely not a fan of building your own Data Access Layer (DAL), since there are plenty of powerful and mature options already available. However, we have 2 customers at work who simply don't let us use any existing libraries/tools as a DAL and want us to just use straight ADO.NET. I don't want to [...]]]></description> <content:encoded><![CDATA[<p>I'm definitely not a fan of building your own Data Access Layer (DAL), since there are plenty of powerful and mature options already available.  However, we have 2 customers at work who simply don't let us use any existing libraries/tools as a DAL and want us to just use straight ADO.NET.  I don't want to get into their reasons for this, but the reality of the situation is that whenever we have to develop projects for them, we need to use a custom built DAL.  I've never seen a custom built DAL that i found acceptable, let alone one that i actually wanted to use.</p><p>A lot of people typically go the code generation route when faced with this situation, which is exactly what we have done in the past.  Been there, done that, hated it with a passion for various reasons.  One of my coworkers recently started a new project for one of these customers, and he started implementing a new DAL.  I had to review this, and while it had some good ideas there was a large amount of repetitive and error-prone code that still needed to be written by developers for every table.  So i set out to come up with something better.  If we did have to use a custom DAL for these customers, i wanted to make sure that it would at least avoid having us write repetitive, error-prone code for every table that we needed to use.  Oh, and without having to resort to code generation.  Since we are all NHibernate users (when customers don't have a problem with us using it, that is) i wanted something that was somewhat similar in ease-of-use though it could obviously never match its feature set, power and maturity.</p><p>I spent about 24 working hours (in total) on this, and i believe i came up with something that is acceptable for most simple forms-over-data applications. This DAL allows you to write your entity classes as POCO's, offers 'out-of-the-box' CRUD functionality for every mapped table, and has lazy loading for reference properties (so you don't need to pollute your entity classes with foreign key properties).  There is also a simple session-level cache, and there is some functionality to ease the pain of using simple, custom queries (with that i mean: every query that is not a select all or select by id and that doesn't join other tables).</p><p>Compared to a real ORM, it is missing a lot: there is no Unit Of Work implementation, no automated change tracking of entities, no dirty checks, no collection support, no advanced querying possibilities, no statement batching, no serious caching functionality, no transitive persistence, and a whole host of features that something like NHibernate gives you for free.   Each and every one of those features comes with a great cost of complexity and development time to get 'right' so it truly doesn't make a lot of sense to do all of this yourself.</p><p>In this series, we're going to go over the entire implementation of this DAL and throughout the series i will point out its shortcomings and try to explain the complexity that would be required to make it truly powerful.  The purpose of this series is basically to:</p><ul><li>Show you that you really don't need to resort to code generation to build your own custom DAL</li><li>Show you what kind of complexity is involved with the implementation of a good DAL</li><li>Convince you that you typically are better off with simply using something that is already available as a mature, powerful and proven solution</li></ul><p>These are the posts that this series consists of:</p><ol><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-mapping-classes-to-tables/">Mapping Classes To Tables</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-out-of-the-box-crud-functionality/">Out Of The Box CRUD Functionality</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-hydrating-entities/">Hydrating Entities</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-session-level-cache/">Session Level Cache</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-lazy-loading/">Lazy Loading</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-executing-custom-queries/">Executing Custom Queries</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-bringing-it-all-together/">Bringing It All Together</a></li><li><a
href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-conclusions/">Conclusions</a></li><li><a
href="http://davybrion.com/blog/2009/10/build-your-own-data-access-layer-enabling-bulk-inserts/">Enabling Bulk Inserts</a></li></ol><p>I will update the list above with actual links to the posts as soon as they are published.</p><p>Note: the code of this series can be found <a
href="https://github.com/davybrion/BuildYourOwnDal">here</a></p><div
class="bottomcontainerBox" style=""><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <iframe
src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdavybrion.com%2Fblog%2F2009%2F08%2Fbuild-your-own-data-access-layer-series%2F&amp;layout=button_count&amp;show_faces=false&amp;width=85&amp;action=like&amp;font=verdana&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width=85px; height:21px;" allowTransparency="true"></iframe></div><div
style="float:left; width:80px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <g:plusone size="medium" href="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/"></g:plusone></div><div
style="float:left; width:95px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"> <a
href="http://twitter.com/share" class="twitter-share-button" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/"  data-text="Build Your Own Data Access Layer Series" data-count="horizontal" data-via="davybrion">Tweet</a></div><div
style="float:left; width:105px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script type="in/share" data-url="http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/" data-counter="right"></script></div><div
style="float:left; width:85px;padding-right:10px; margin:4px 4px 4px 4px;height:30px;"><script src="http://www.stumbleupon.com/hostedbadge.php?s=1&amp;r=http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/"></script></div></div><div
style="clear:both"></div><div
style="padding-bottom:4px;"></div>]]></content:encoded> <wfw:commentRss>http://davybrion.com/blog/2009/08/build-your-own-data-access-layer-series/feed/</wfw:commentRss> <slash:comments>19</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced
Database Caching 2/51 queries in 0.028 seconds using disk: basic
Object Caching 1010/1111 objects using disk: basic
Content Delivery Network via Amazon Web Services: CloudFront: d18sni7re4ly7f.cloudfront.net

Served from: davybrion.com @ 2012-02-08 05:29:31 -->
