Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

new Repository<T>().DoMagic()

#karl_custom_1 code{color:#833;background:#fcfcfc;}
#karl_custom_1 h4{margin:30px 0px 0px 0px;font-color:#fff;font-weight:bold;border-bottom:1px dashed #ccc;font-variant: small-caps}


The purpose behind the repository pattern is to provide a layer of abstraction between your domain and data layer. For smaller projects, this typically isn’t needed. However, larger projects can really benefit from a broker that specifically handles the back and forth between the two layers. With repositories your domain objects aren’t burdened with infrastructure details and can therefore better focus on domain-specific behavior.


Without A Repository


To build this up to our grand finale (a generic repository), let’s look at how you might do things in a small project without a repository. The simplest example might look something like:

public class User
{
private int _id;

public void Delete()
{
Factory.Get<IDataStore>().DeleteUser(_id);
}
}


Of course, our User class will likely need a Save and various Find methods. Also, Delete won’t always be so straightforward – for data integrity/historic reasons you might not actually delete the entity, but rather flag it as inactive and update it:

public void Delete()
{
_isActive = false;
Factory.Get<IDataStore>().Update(this);
}

With non-generic Repositories


The above approach will quickly result in a burdened User class – especially when you look at all the different ways you’ll want to fetch entities. The next step is to create repositories per-entity. So you end up with something like:

public class UserRepository : IUserRepository
{
public void Delete(User user)
{
user.IsActive = false;
Factory.Get<IDataStore>().Update(user);
}
public User FindByCredentials(string username, password)
{
//todo some null checks, maybe encrypt the password
var user = Factory.Get<IDataStore>().FindUser(username, password)
return user ?? User.Null;
}
}

This is a pretty common approach (you don’t have to look too far back to some of my own code samples to see it) and it works pretty well. The main problem with this code is that you end up with a lot of repetitive code and an equal number of repetitive tests. If only there was a way to significantly reduce the repitition…


Generic Repository


Not too long ago I blogged about Generics saying that they were ideal for situations where code was similar but types differed. I provided a few examples, including a quick overview of a generic repository. Lets expand on that a bit. The beauty of a generic repository is that it gives you a powerful, flexible and easy to test class that can satisfy the vast majority of your needs – especially when you combine it with other generic classes (we use a generic controller for our CRUD which interacts with our generic repository which hits a generic DAL which is pretty much a wrapper to NHibernate (which itself is a generic implementation)).


First thing first, we start with some basic stuff:

public class Repository<T> where T : IEntity
{
private T _entity;
public Repository(T entity)
{
if (entity == null)
{
throw new ArgumentNullException(“entity”);
}
_entity = entity;
}
}

The only note-worthy thing is the generic constraint stating that T must be of type IEntity. This is a custom type, which I simply define as:

public interface IEntity
{
int Id{ get; }
}

This not only lets us get an entity’s Id (which is pretty important for some simple operations), but also makes sure someone doesn’t create a repository for a type we don’t control, such as:

new Repository<int>(3); //doh!

Next we can start implementing simple versions of our most common methods:

public virtual void Delete()
{
DataFactory.For<T>().Delete(_entity);
}
public virtual void Save()
{
DataFactory.For<T>().Save(_entity);
}

The real power though comes from being able to re-use more complex patterns. In our current system we have versioned entities (entities that can’t be modified or deleted in order to preserver historical data integrity). Such entities are marked with the IVersioned interface:

public interface IVersioned<T> where T : IEntity
{
void MarkAsNew();
void MarkAsOld();
DateTime Created{get;set;}
T Root{get;set;}
}

Essentially, a versioned entity can be marked as active/inactive, always has a created property and always references its root element (this makes reporting across a single entity transparent from the versioning aspect). By implementing IVersioned<T> within an entity, such as:

public class User : IEntity, IVersioned<User>
{
private int _id;
private bool _isActive;

public int Id{get { return _id; }}
public bool IsActive{get { return _isActive; }}
public User Root{get;set;}
public DateTime Created{get;set;}

public void MarkAsOld()
{
_isActive = false;
}
public void MarkAsNew()
{
_created = DateTime.Now;
_isActive = true;
_id = 0;
}
}


We can now start wirting some really meaningful and reusable code:

public virtual void Delete()
{
if (!(_entity is IVersioned<T>))
{
DataFactory.For<T>().Delete(entity);
return;
}
var entity = (IVersioned<T>)_entity;
entity.MarkAsInactive();
DataFactory.For<T>().Save(entity);
}
public virtual void Save()
{
AssertValidity(); //validates _entity
var store = DataFactory.For<T>();
if (_entity.Id == 0 || !(_entity is IVersioned<T>))
{
store.Save(_entity);
return;
}
var original = store.Get(_entity.Id);
var entity = (IVersioned<T>)_entity;
entity.MarkAsNew();
entity.Root = original.Root ?? original;
original.MarkAsOld();
store.Save(entity, original)
}

This is just a narrow slice of what you can accomplish – with just a few interfaces you can accomplish a lot of work – we currently have IImmutable (can’t be updated/deleted), IDefautable (an entity with IsDefault == true cannot be deleted and cannot be removed as the default), and IUnique which lists some unique constraint that the generic repository can enforce (a unique username for example).


What Is DataFactory<T>()…


Except for references to a data-layer, I left out all details about the underlying DAL that our repository has to interact with. Depending on what you’re currently using, you might not see how the two will play nicely together (for example, I’m under the impression that my above code won’t work all that well with a LINQ-to-SQL implementation). This is just one of the many reasons why you really should look at your DAL from the domain point of view (instead of the data perspective). If you aren’t familiar with a quality O/R mapper, what are you waiting for?


What about Fetching?


I also conveniently left-out how to retrieve records. We can consider this a topic for future posts. However, the same concept applies. There are actually a number of tools at your disposal, including LINQ-to-Nhibernate, NHIbernate detached queries and generic object query. We currently use the latter, though we’re looking into going more of an expression route. Here’s a little sample:

//I dislike the magic string ‘IsActive’
var query = new ModelQuery<User>()
.Where(w => w.AddCriteria(“IsActive”, Operation.Equals, true).Paged(1, 10);
var result = new Repository<User>().Find(query);
int pages = result.TotalPages;
var users = result.Entities;

Special Cases


Your generic repository won’t be able to handle all cases. In such situations, you simply sub-class it and provide the custom logic – overriding existing methods or implementing your own.


Conclusion


I see more and more applications making use of numerous XXXRepository classes, and wonder whether they get tired, like I did, of copy-n-pasting the same base code and tests. Over the last year, I’ve learnt that if you copy existing tests as a starting point, there’s a good chance you can introduce a generic implementation – which almost always results in cleaner and more testable code. Hopefully you also see how generics and interfaces can play off of each other – almost in a symbolic manner. By simply marking your entity with IDefautable (and implementing the sole IsDefault property) all of the necessary infrastructure and tests are already in place to support your systems concept of a default entity

.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

31 Responses to new Repository<T>().DoMagic()

  1. Chris J says:

    Karl, first of all, great article. Thank you.

    I’m really curious how a mapping of an entity in NHibernate would work when the entity inherits from IVersioned and also what the database schema would entail. Before finding this article, I envisioned a versioning construct very similar, however, I’m really struggling to determine how NHibernate plays nicely with this interface from which many entities would inherit. How does DataFactory handle this? Any help would be GREATLY appreciated!

  2. Karl: Thanks for your thoughts – I really enjoyed the article.

    Regards,
    Remi.

  3. karl says:

    Remi:
    I think your approach is better for larger systems – I happened to be working on something with only a few domain objects so that’s the mindset I was in.

    Keeping references to repositories within domain entities can cause all sorts of nasty bugs to creep up – largely causes by caching and such. I can’t remember the details, but I remember spending a few days tracking down an obscure bug which ended up being caused by entities holding on to references to stale repositories. I think it’s simply to simply use a DI framework and ask for the repository whenever its needed. This works well in .NET since the scope of a unit of work is generally tied to an individual request (it’s why the Session-per-request model for Nhibernate works so well too). Not sure how I’d go about doing it for non-web apps though.

    Karl

  4. karl says:

    Remi:
    I think your approach is better for larger systems – I happened to be working on something with only a few domain objects so that’s the mindset I was in.

    Keeping references to repositories within domain entities can cause all sorts of nasty bugs to creep up – largely causes by caching and such. I can’t remember the details, but I remember spending a few days tracking down an obscure bug which ended up being caused by entities holding on to references to stale repositories. I think it’s simply to simply use a DI framework and ask for the repository whenever its needed. This works well in .NET since the scope of a unit of work is generally tied to an individual request (it’s why the Session-per-request model for Nhibernate works so well too). Not sure how I’d go about doing it for non-web apps though.

    Karl

  5. Karl, I notice that your repository contains an instance of the domain class it represents. In my case, when I’ve used repositories – even generic ones – I’ve typically set them up using a repository instance per domain aggregate class (passing the instance in by param), and not a repository per domain aggregate instance (which I believe is the implication here).

    I would be interested in hearing your thoughts on the advantages in your approach.

    Also, how does the repository get called for a loaded aggregate instance? Who keeps a reference to the repository?

    Regards,
    Remi.

  6. Karl, I notice that your repository contains an instance of the domain class it represents. In my case, when I’ve used repositories – even generic ones – I’ve typically set them up using a repository instance per domain aggregate class, and not a repository per domain aggregate instance (which I believe is the implication here).

    I would be interested in hearing your thoughts on the advantages in your approach.

    Also, how does the repository get called for a loaded aggregate instance? Who keeps a reference to the repository?

    Regards,
    Remi.

  7. Robert G says:

    Karl,

    I’d also like to see an implementation of DataFactory. I can’t help but think that you’ll run into massive subtle issues once there is a real implementation.

  8. karl says:

    Fregas:
    Its our own class. Its a pretty straightforward query object…we then use a “Converter” to map it to our DAL. So for our Nhibernate DAL, we’ll have an NhibernateConverter It really isn’t hard to piece together. ModelQuery just contains a list of Creteria, a Pager class (which also does sorting) and you just add to it as you need (our supports joins and only selecting individual properties instead of full objects). Might write up on it some day.

  9. Fregas says:

    Whats that class you’re using “ModelQuery” ? Is that part of NH or something you guys came up with?

  10. @Daniel : I would be cautious in using Linq as a query engine publicly exposed by your Repository.
    The compile time of Linq might be generic but it can crash on you hard at runtime so I would still use things such as Specification objects to encapsulate filtering logic.

    @Troy : I did too take the IRepository route too, it seems it’s an easy way to get compile time safety but the way I see it sometimse Generics are plain annoying because they tend to leak very badly.
    I would say it’d be best to do something like :
    IRepository
    {
    IPrimaryKey Key {get;set;}
    }
    Not the most common way of approaching this I know.

  11. Colin Jack says:

    @Troy Goode
    If you mean me then I *didn’t* say that it wasn’t different….I just said the reuse argument was baloney.

    As for registering, thats just a single convention. You could write another one, look for all inerfaces inheriting from IRepository (such as ICustomerRepository) and then register any classes implementing them (CustomerRepository). Not tried that exact convention, but can’t see a problem.

    Also assuming people “must not be use DI/IoC” isn’t sensible, just because someone disagrees does not mean they are ignorant of the ideas.

  12. karl says:

    @redgreenrefactor:
    I’ve wondered the same thing more than once. It’ll probably get there. Initially I had assumed that _how_ an entity versioned itself, or determined that it had to version itself would be different from type to type. As it turns out, so far, the code is almost identical, and thus a base type might be more suitable. Only have 2 fully versioned entities so far, maybe once I repeat the same code a 3rd time I’ll budge…continuous improvements.

  13. redgreenrefactor says:

    Hi Karl,

    Nice writing. Why is IVersioned not a generic abstract class.

  14. Troy Goode says:

    A few comments:

    1) We do exactly the same thing (even with almost the same naming, except our IRepository methods follow CRUD naming, e.g. Save = Update, Find = Retrieve).

    2) Instead of IEntity, we use IEntity, which allows for non-int keys (primarily for the tables that use Guid).

    3) The people talking about this not being any different than using abstract base classes and such must not be use DI/IoC. Using DI/IoC (Autofac for me) and the generic repository, I can register ALL of my generic repositories with one line:

    container.RegisterGeneric(typeof(NHibernateRepository< ,>)).As(typeof(IRepository< ,>));

    Then, my service classes can just request an instance of IRepository (second arg is id type) and it will automatically create the appropriate instance – no extra code needed.

  15. JP says:

    Good Read! I like the idea of IVersioned.

    It always seem to bother me that we still “write” strategies like this. I’ve written many, and have to say there is a point where they must be believed in for your fellow developers to use it wisely. I’ve been reluctantly softening my walls to ef hoping that it pays off with the next drop. At least v1 is released and they can’t pull an “object spaces”; although we did witness linq2sql getting absorbed.

  16. Daniel says:

    I’d be interested in seeing more of DataFactory.

    I keep trying this and running into issues around queries and data types. I think what we all want is a generic repository that handles POCOs and supports identical LINQ queries across different data stores. This gets tricky fast, though, because there are so many different flavors of LINQ, and so many nuances in the data stores. For example, I’ve seen lots of IRepository examples expose LINQ-to-SQL classes as domain objects. This looks great until you try to implement an EF, XML, or SQL Data Services repository- the types either don’t work in the data store, or the queries are different between the two, or nuances like lazy loading implementations dictate different strategies.

  17. @Chris : I don’t think the IVersioned type check is really a worry, I instead think creating a subtype of the repository that puts another constraint on T as a smell though, who knows what combinations of constraints you will need ?
    I’d rather use a mechanism where you can have a registry of what types are interested for specific persistence events (pre save, post save, pre load, etc…).

  18. @Chris : I don’t think the IVersioned type check is really a worry, I instead think creating a subtype of the repository that puts another constraint on T as a smell though, who knows what combinations of constraints you will need ?
    I’d rather use a mechanism where you can have a registry of what types are interested for specific persistence events (pre save, post save, pre load, etc…).

  19. Lars says:

    Very nice, Karl. Very nice.
    Generic repositories are a big help in a system with lots of data access across types.

    I did the whole Repository thing with LINQ2Sql, and it worked like a charm.
    Actualle, it was a Repository
    >, but that is only because we needed to do our own mapping from the L2Sql data types.

    I also tried out doing a Repository, just using the L2Sql data types throughout the app, but it always feels a bit hackish.
    I was able to create a custom querying class that way though, I might write about it some day 😉

  20. karl says:

    @Chris:
    It does smell a bit – though its still super-easy to test, which I tend to use as a general indication of whether something really has to be refactored or not. You could subclass the repository and have a VersionedRepository and so on. I’d consider it, and I’d certainly consider it more as things got more complicated. Thanks for the suggestion.

  21. Chris says:

    Hi – that was interesting reading

    One question though – isn’t checking whether a parameter is of a particular type (e.g. IVersioned) and doing one thing if it is and one thing if it isnt a code smell?

    I’m not having a pop at you – but I have been reading Refactoring and Bob Martins book recent,y and Im sure that i mentioned as a no-no. (is it a violation of Liskov principle?)

    would love to hear our opinion – even if i is to tell me I am wrong – which is deffinitely possible as I am still getting my head round all of this

  22. Colin Jack says:

    @karl
    Agreed, I’ve just read a few blog posts and heard a few arguments where people said that writing custom repositories led to too much redundnancy and so they moved to generic redundancies. Poppycock is my response to that :)

    On the clean and tight, I’m not so sure. Custom repositories can be incredibly clean and tight, they’re focussed on behavior specific to that aggregate.

    Also whilst custom query code can sometimes be wrapped in specifications eventually you’ll want to use a bit of SQL or do something clever (maybe swap to loading some aggregate or part of it from another source) which is when the real power of the custom repository kicks in.

    Anyhow great blogpost, as always.

  23. karl says:

    @Colin:
    You’re right – the re-use isn’t because its a generic repository, it’s merely because its a base class (which can be extended or not if needed) – the generic aspect merely helps keep it clean and tight. Aside from that, the differences between the approach seems pretty small enough that we can probably agree that we’d understand each others code pretty easily – its in the same genus.

    @Robin:
    The entity is there for non-fetch operations. How you deal with fetches is up to you, I’ve seen a separate read-specific repository, static members, parameterless constructor (have to be careful that someone doesn’t call Save() though) and so on.

  24. Colin Jack says:

    @Bil Simser
    Yeah I use a similiar approach, find it gives me all the advantages with no particular disadvantages (that I’m aware of).

  25. Why do you pass an instance of your IEntity into your generic repository? What if you just need to do a fetch operation, do you pass in a null instance?

  26. Colin Jack says:

    “I see more and more applications making use of numerous XXXRepository classes, and wonder whether they get tired, like I did, of copy-n-pasting the same base code and tests.”

    This whole generic repository and reuse thing leaves me a little puzzled, because leaveraging interfaces/generics to get lots of reuse is what people have been doing since 2.0 came out and has certainly been what I’ve been doing since I started using repositories (same for most people I know of).

    I do disagree with your conclusion that this reuse is caused by you using generic repositories, you can get massive quantities of reuse through careful use of inheritance/composition and still have concrete repositories.

    In our case we quickly extracted all common code and added base classes, allowing inheritance and/or composition (whichever you prefer) for reuse. So you could use RepositoryHelper through composition or you could inherit from ReadWriteRepository. This took me very little time and meant for simple CRUD you got all the code for free (http://tech.groups.yahoo.com/group/domaindrivendesign/message/9053).

    The custom repositories then just contain any custom queries, SQL (for performance) or mapping (maybe from DTO’s or web services).

    Same applied for testing, you had a DeleteTestHelper, a SaveTestHelper and so on. To make this easy my repositories implemented interfaces like IDelete and DeleteTestHelper used those interfaces. Actually we also had base classes like AggregateRootPersistenceTestBase, you had to overload a few methods like CreateRepository/CreateAggregateRoot but other than that it could take care of the vast majority of the work.

    Overall I think this is much preferable to the generic Repository approach, other than (perhaps) for relatively simple scenarios (though even then I don’t particularly rate the generic approach).

  27. Hi Karl,

    Very nice post! I also played around with a single generic repository. One of the main problems I faced was when using special cases. Also, I was caching the results of the method call and only wanted to cache for some cases. This can be accomplished by overriding the method in the concrete repository class.

  28. Paco says:

    I used to use a generic repository, but I removed it and made it an abstract base class, because I always seam to have special cases. I don’t like to expose persistence implementation specific stuff like IQueryable to other parts of the system then the repository classes itself. What about YAGNI? It’s useless to have a public findall method in a repository querying against a millions of record db.

  29. Yoann. B says:

    Great article,

    Thanks.

  30. Bil Simser says:

    I find I do the same, I have an IRepository that is implemented by a generic BaseRepository which handles all of the normal basics (Get, Find, FindAll, Query, Update, Delete, etc.). The specialized repositories (say for a Customer) would have methods specific to features I needed to optimize the Customer UI pipeline (GetAllCustomersMatchingSomeSpecification). The last method could be generic’ized using an ISpecification and ISpecification.Matches but that might be pushing it). In the end I have specialized repository classes with very few methods and an abstract generic one that handles the majority of the world. Must watch viddler to see the debate though as I would rather have just one if it would work for all cases.

  31. Karl,

    We use Linq for NHibernate and enforce a rigid rule of always using the same identity strategy for each entity, so we get away with only having IRepository with generic methods for Find() and Query() to avoid all the rinse and repeat repository coding. Add the overhead of wanting in memory repositories for testing, and I much prefer the single repository class over having individual repository classes.

    Ayende & I had a debate on this subject at KaizenConf at the Opinionated MVC talk that’s on Viddler.