amindomeniko
amindomeniko

Reputation: 429

How to implement a correct repository pattern in .NET

This article explains how to create a generic HttpClient class with all the basic CRUD operations. Then it creates a type specific repository for a specific POCO object (Called Member).

What I fail to understand is the need for the MemberReposity class that is returning exactly what the GenericHttpClient class would return for a specific type? Can't I simply rename the GenericHttpClient to GenericReposity and then use this for all of my POCO objects CRUD operations? In another word, why would I need a unique repository for every object if they all only require basic CRUD operations?

I could simply instantiate them on my form like this:

private GenericReposity<Customer, int> _customerClient = new GenericReposity<Customer, string>(url, "api/Customers/");

and then get a customer this way:

_customerClient.GetAsync(5);

Am I missing a point here?


UPDATE

After reading about Anti-Pattern I designed a base repository interface as following:

internal interface IGenericRepository<T, in TResourceIdentifier>
{
    Task<IEnumerable<T>> GetManyAsync();
    Task<T> GetAsync(TResourceIdentifier id);
    Task PutAsync(T model);
    Task<T> PostAsync(T model);
    Task DeleteAsync(TResourceIdentifier id);
}

Then I implemented it:

public class GenericRepository<T, TResourceIdentifier> : IDisposable, IGenericRepository<T, TResourceIdentifier> 
    where T : class
{
    private bool _disposed;
    protected HttpClientHelper<T, TResourceIdentifier> Client;

    protected GenericRepository(string addressSuffix)
    {
        Client = new HttpClientHelper<T, TResourceIdentifier>(Properties.Settings.Url, addressSuffix);
    }

    public async Task<IEnumerable<T>> GetManyAsync()
    {
        return await Client.GetManyAsync();
    }

    // All other CRUD methods and dispose
}

Then I created custom repository interface for each of my entities. For example:

internal interface IOrderRepository : IGenericRepository<Order, int>
{
    Task<IEnumerable<Order>> GetOrderBySomeConditionAsync(string condition );

}

And finally, I implemented the custom repository:

public class OrderRepository : GenericRepository<Order, int>, IOrderRepository
{
    public OrderRepository(string addressSuffix) : base(addressSuffix)
    {
    }

    public async Task<IEnumerable<Order>> GetOrderBySomeConditionAsync(string condition)
    {
        //get all the orders (GetManyAsync()) and then returns the ones meeting the condition
    }
}

This way I can get all the simple CRUD operations without having to implement them one by one for each entity as well as any custom ones. Is this a proper approach? Or is this considered anti-pattern?

Upvotes: 2

Views: 496

Answers (2)

Amit Joshi
Amit Joshi

Reputation: 16389

Your new approach (after update) looks great.

With this, you bypass all the drawbacks of generic repository; still taking advantage of code reuse from base generic repository.

Not big deal but consider making all methods in base repository protected so that those should be only accessible in concrete repository. Of-course this suggestion can be neglected if all concrete repositories implement that method.

Upvotes: 1

Leo Bartkus
Leo Bartkus

Reputation: 1947

Some day you might want to add things that would be unique per entity type like data validation, or encrypting personal information when the data is at rest.

Upvotes: 1

Related Questions