I've been iterating on my repository pattern implementation over the course of the past 4-5 months. In my new projects I choose to use this pattern and I try to improve upon what I learned in previous projects. I'm pretty pleased with the state of my current implementation. I feel like I could improve upon my validation logic somehow, though.
First and foremost, my code is on GitHub in its entirety: https://github.com/ryancole/Warcraft
Description of my implementation
I've gone with a layered approach consisting of my POCO entity classes, the repositories and finally services on top of the repositories. I have a service layer because I've chosen to use a generic repository class. My repository class is basic and provides the core CRUD operations. It's also purposefully leaky, because it's intended to sit behind my service layer - so, my repository class returns IQueryable
. My service layer returns ICollection
, on the other hand.
public interface IRepository<T> where T : class
{
#region Properties
/// <summary>
/// Retrieve all entities
/// </summary>
IQueryable<T> All { get; }
#endregion
#region Methods
/// <summary>
/// Count entities matching a predicate
/// </summary>
int Count(Expression<Func<T, bool>> predicate);
/// <summary>
/// Insert an entity
/// </summary>
T Insert(T entity);
/// <summary>
/// Retrieve entities matching a predicate
/// </summary>
IQueryable<T> Where(Expression<Func<T, bool>> predicate);
#endregion
}
I have a unit of work class, which contains my DbContext
instance and provides access to my various services. The unit of work class also exposes a SaveChanges
method. The intention is that users of my libraries will just pass this unit of work class through to their MVC controllers, and therefor do not have to deal with any of the underlying contexts, repositories, etc - only the services with their explicitly crafted methods for specific tasks.
public interface ISession : IDisposable
{
#region Properties
IAccountService Accounts { get; }
ICharacterService Characters { get; }
#endregion
#region Methods
bool SaveChanges();
#endregion
}
public interface ICharacterService
{
#region Methods
/// <summary>
/// Insert a new Character
/// </summary>
Character Insert(Character character);
/// <summary>
/// Retrieve a Character with the specified Name
/// </summary>
Character GetByName(string name);
/// <summary>
/// Retrieve all Characters
/// </summary>
ICollection<Character> GetAll();
/// <summary>
/// Retrieve all Characters for the given Account
/// </summary>
ICollection<Character> GetByAccount(Account account);
#endregion
}
The only thing that I think if slightly leaky is updating an existing entity. Updating an entity is just done by relying on the DbContext
to track the changes, and then calling the unit of work's SaveChanges
method. So, updating entities is not funneled through any service method. And this creates my current situation and question about my implementation.
My question
Where can I perform validation that works for both inserts and updates, with minimal repeating of myself?
My original intention and plan was to perform all validation inside of my service layer. So, when adding a new entity you'd call the Insert
method and I'd just validate in there. But, with how I do updating of existing entities, there is no Update
method, and therefor no place to put that validation in my service layer.
So, what I'm doing now instead is just putting basic validation inside of the POCO entity classes themselves, using DataAnnotations
. Any validation that requires data from the database is done in the DbContext
override method ValidateEntity
. This method works nice, because of how well it integrates with EF for inserts and updates. But, this method kind of sucks because validation is split up into 2 different places and neither are my desired location which would be the service layer!
What do you think of my implementation of the repository pattern? Any improvements I could make?
Do you know of any better way to perform validation that works for both inserts and updates? Any way using dep injection or anything?
Character
one of your POCO classes? \$\endgroup\$IValidatableObject
logic. \$\endgroup\$