2
\$\begingroup\$

I've been using this variation of the Repository pattern for over a year now:

 public interface IReadOnlyRepository<T, in TId>
     where T : AbstractEntity<TId>
    {
        T Get( TId id );
        IEnumerable<T> GetAll();
    }

/// <summary>
/// Defines a generic repository interface for
/// classes solely in charge of getting and processing data from a data source
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TId">The type of the id.</typeparam>
public interface IRepository<T, in TId> : IReadOnlyRepository<T, TId> where T : AbstractEntity<TId>
{
    /// <summary>
    /// Determines whether the specified entity has duplicates.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns>
    ///   <c>true</c> if the specified entity has duplicates; otherwise, <c>false</c>.
    /// </returns>
    bool HasDuplicates(T entity);

    /// <summary>
    /// Inserts the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    void Save( T entity );
    /// <summary>
    /// Inserts the entity or updates it if it already exists.
    /// </summary>
    /// <param name="entity">The entity.</param>
    T SaveOrUpdate( T entity );

    /// <summary>
    /// Updates the specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns></returns>
    T Update(T entity);

    /// <summary>
    /// Deletes the specified entity from the data source.
    /// </summary>
    /// <param name="entity">The entity.</param>
    void Delete(T entity);

    /// <summary>
    /// Deletes the entity with the specified id.
    /// </summary>
    /// <param name="id">The id.</param>
    void Delete(TId id);
}

but recently, after rereading some books on Design Patterns, I've had this seemingly amazing idea to apply some patterns to my repositories.

public interface IRepository<T, in TId> : IReadOnlyRepository<T, TId> where T : AbstractEntity<TId>
{
    void Execute(IRepositoryCommand command);
    void Execute(IBatchRepositoryCommand command);
}

public interface IRepositoryCommand<T>
{
    void Execute(T entity);
}

public interface IBatchRepositoryCommand<T>
{
    void Execute(IEnumerable<T> entities);
}

public SaveCommand<T> : IRepositoryCommand<T> 
{
    public void Execute(T entity)
    {
        // Logic for saving goes here
    }
}

public BatchSaveCommand<T> : IRepositoryCommand<T>
{
    public void Execute(IEnumerable<T> entities)
    {
        // Logic for batch saves go here
    }
}

which would then be called like this:

_myRepository.Execute(new SaveCommand());

My reasoning is that placing logic for the common data access operations (e.g. saving, deleting) gets to be so repetitive that right now I'm relying on a T4 template to recreate those everytime I have a new entity enter the playing field. This way I just define the most commonly used Data Access operations and then have any of my callers execute whatever action they need to execute.

Can you critique my work? I do have the tendency to overthink and overengineer things.

\$\endgroup\$

3 Answers 3

1
\$\begingroup\$

Although the command pattern you brought in makes it easy to create a 'flexible' Execute method. I wonder if it is really transparant to the ones who read your code.

Perhaps you can combine your two ideas, by implementing a certain ReadOnlyRepository using the command objects. So that the user of your interace keeps using:

repository->Update(SomeEntity);

While the repository implementation does:

repository.Execute(new UpdateCommand());

Consider, if you had to make a change to your later interface. Where would you need to update that in your code? In my suggestion it simply requires you to change it at one place. (since all the other code still uses the 'old' interface).

I hope my post makes sense :)

\$\endgroup\$
0
\$\begingroup\$

I think it be useful to introduce explicit units of work that can be used in areas where needed such as Save(). I'd also suggest a generic Query() interface that will take a lambda to improve flexibility in retrieval. You might also consider asynchronous scenarios.

\$\endgroup\$
5
  • \$\begingroup\$ I excluded details on Unit of Work, but the presumption is that every Command implementation will have a reference to it. A generic query interface huh? I can't picture it atm. How would I go about implementing it? I've encountered a LOT of variations of queries and I couldn't find a good place to introduce this level of flexibility. \$\endgroup\$
    – Jonn
    Jul 2, 2011 at 7:31
  • \$\begingroup\$ Having a generic Query interface makes sense if there is a proper IQueryable implementation which would understand the "lambda" (an expression tree, actually) and create the db queries accordingly. In other words, there should be Linq-to-Sql, Linq-to-NHibernate or something similar under the hood. Basically, your repo interface has a Query method which (if you are using NHibernate) calls Session.Linq<T>() and returns an IQueryable. This keeps the repo interface simple (because you can do complex queries), but it's hard to implement if your repo only uses ADO.NET underneath. \$\endgroup\$
    – Groo
    Jul 3, 2011 at 21:02
  • \$\begingroup\$ I use QueryOver and sadly, it doesn't return IQueryables. I'm still trying to think of a way to get around that limitation. \$\endgroup\$
    – Jonn
    Jul 4, 2011 at 3:18
  • 1
    \$\begingroup\$ @John: that would basically be NHibernate's custom way of adding query support without actually implementing IQueryable. In other words, you may expose it, but be prepared to use NHibernate-specific constructs in your business layer. The query object pattern itself is tempting to use because it lets you write queries in the business layer, but there are IMHO more cases where strongly typed generic repo interfaces are a better solution (check this link). \$\endgroup\$
    – Groo
    Jul 4, 2011 at 10:17
  • 1
    \$\begingroup\$ @John: IQueryable or QueryOver seem like a silver bullet at first, but eventually you may need to do some "hand-made" queries yourself (for performance or other reasons), and then it will turn out that being too generic is not such a good thing after all. I am mostly writing this because I started with Linq-to-NHibernate which was implemented rather poorly in NH2.0, and some of more complex queries could not be resolved, which made me realize that query object pattern relies on a contract which cannot easily be abstracted (I don't like the idea of having to implement IQueryable myself). \$\endgroup\$
    – Groo
    Jul 4, 2011 at 10:24
0
\$\begingroup\$

Most of my saving and deleting etc are all handled in a base repository class, so I don't really repeat anything.

so for a lot of things you only really need....

IBlahRepository inherits IRepository

ConcreteBlahRepository inherits IBlahRepository and Repository

which is all generic on an Entity

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.