Note that there are some explanatory texts on larger screens.

plurals
  1. POC# - Object Composition - Removing Boilerplate Code
    primarykey
    data
    text
    <h1>Context / Question</h1> <p>I've worked on numerous .NET projects that have been required to persist data and have usually ended up using a <a href="http://www.martinfowler.com/eaaCatalog/repository.html" rel="nofollow noreferrer">Repository</a> pattern. Does anyone know of a good strategy for removing as much boilerplate code without sacrificing code base scalability?</p> <h1>Inheritance Strategy</h1> <p>Because so much of the Repository code is boiler plate and needs to be repeated I normally create a base class to cover the basics like exception handling, logging and transaction support as well as a few basic CRUD methods:</p> <pre><code>public abstract class BaseRepository&lt;T&gt; where T : IEntity { protected void ExecuteQuery(Action query) { //Do Transaction Support / Error Handling / Logging query(); } //CRUD Methods: public virtual T GetByID(int id){} public virtual IEnumerable&lt;T&gt; GetAll(int id){} public virtual void Add (T Entity){} public virtual void Update(T Entity){} public virtual void Delete(T Entity){} } </code></pre> <p>So this works well when I have a simple domain, I can quickly create a DRY repository class for each entity. However, this starts to break down when the domain gets more complex. Lets say a new entity is introduced that does not allow updates. I can break up base classes and move the Update method into a different class:</p> <pre><code>public abstract class BaseRepositorySimple&lt;T&gt; where T : IEntity { protected void ExecuteQuery(Action query); public virtual T GetByID(int id){} public virtual IEnumerable&lt;T&gt; GetAll(int id){} public virtual void Add (T entity){} public void Delete(T entity){} } public abstract class BaseRepositoryWithUpdate&lt;T&gt; : BaseRepositorySimple&lt;T&gt; where T : IEntity { public virtual void Update(T entity){} } </code></pre> <p>This solution does not scale well. Let's say I have several Entities that have a common method: public virtual void Archive(T entity){}</p> <p>but some Entities that can be Archived can also be Updated while others can't. So my Inheritance solution breaks down, I'd have to create two new base classes to deal with this scenario.</p> <h1>Compoisition Strategy</h1> <p>I've explored the Compositon pattern, but this seems to leave a lot of boiler plate code:</p> <pre><code>public class MyEntityRepository : IGetByID&lt;MyEntity&gt;, IArchive&lt;MyEntity&gt; { private Archiver&lt;MyEntity&gt; _archiveWrapper; private GetByIDRetriever&lt;MyEntity&gt; _getByIDWrapper; public MyEntityRepository() { //initialize wrappers (or pull them in //using Constructor Injection and DI) } public MyEntity GetByID(int id) { return _getByIDWrapper(id).GetByID(id); } public void Archive(MyEntity entity) { _archiveWrapper.Archive(entity)' } } </code></pre> <p>The MyEntityRepository is now loaded with boilerplate code. Is there a tool / pattern that I can use to automatically generate this?</p> <p>If I could turn the MyEntityRepository into something like this, I think that would by far be ideal:</p> <pre><code>[Implement(Interface=typeof(IGetByID&lt;MyEntity&gt;), Using = GetByIDRetriever&lt;MyEntity&gt;)] [Implement(Interface=typeof(IArchive&lt;MyEntity&gt;), Using = Archiver&lt;MyEntity&gt;) public class MyEntityRepository { public MyEntityRepository() { //initialize wrappers (or pull them in //using Constructor Injection and DI) } } </code></pre> <h1>Aspect Oriented Programming</h1> <p>I looked into using an AOP framework for this, specifically <a href="http://www.postsharp.net/" rel="nofollow noreferrer">PostSharp</a> and their <a href="http://doc.postsharp.net/postsharp-2.1/Default.aspx##PostSharp-2.1.chm/html/20c81e23-af91-4688-a672-b0f3cce793a9.htm" rel="nofollow noreferrer">Composition Aspect</a>, which looks like it should do the trick, but in order to use a Repository I'll have to call Post.Cast&lt;>(), which adds a very odd smell to the code. Anyone know if there's a better way to use AOP to help get rid of the compositor boilerplate code?</p> <h1>Custom Code Generator</h1> <p>If all else fails, I suppose I could work at creating a Custom Code Generator Visual Studio plug in that could generate the boiler plate code into a partial code file. Is there already a tool out there that would do this?</p> <pre><code>[Implement(Interface=typeof(IGetByID&lt;MyEntity&gt;), Using = GetByIDRetriever&lt;MyEntity&gt;)] [Implement(Interface=typeof(IArchive&lt;MyEntity&gt;), Using = Archiver&lt;MyEntity&gt;) public partial class MyEntityRepository { public MyEntityRepository() { //initialize wrappers (or pull them in //using Constructor Injection and DI) } } //Generated Class file public partial class MyEntityRepository : IGetByID&lt;MyEntity&gt;, IArchive&lt;MyEntity&gt; { private Archiver&lt;MyEntity&gt; _archiveWrapper; private GetByIDRetriever&lt;MyEntity&gt; _getByIDWrapper; public MyEntity GetByID(int id) { return _getByIDWrapper(id).GetByID(id); } public void Archive(MyEntity entity) { _archiveWrapper.Archive(entity)' } } </code></pre> <h1>Extension Methods</h1> <p>Forgot to add this when I initially wrote the question (sorry). I also tried experimenting with extension methods:</p> <pre><code>public static class GetByIDExtenions { public T GetByID&lt;T&gt;(this IGetByID&lt;T&gt; repository, int id){ } } </code></pre> <p>However, this has two problems, a) I'd have to remember the namespace of the extension methods class and add it everywhere and b) the extension methods can't satisfy interface dependencies:</p> <pre><code>public interface IMyEntityRepository : IGetByID&lt;MyEntity&gt;{} public class MyEntityRepository : IMyEntityRepository{} </code></pre> <p><em>Update</em>: Would <a href="http://msdn.microsoft.com/en-us/library/vstudio/bb126445.aspx" rel="nofollow noreferrer">T4 Templates</a> be a possible solution?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload