Memory cache service I am repeating this code at lot of places:

public class ProductTypeService : IProductTypeService
{
    public async Task&lt;IList&lt;ProductType&gt;&gt; GetAllAsync(int sectionId = -1)
    {
        string key = "ProductTypeService_" + "GetAllAsync" + sectionId;
        var list = _memoryCacheService.Get&lt;IList&lt;ProductType&gt;&gt;(key);
        if (list == null)
        {
            list = await _repository.GetAllAsync(sectionId);
            _memoryCacheService.Save(key, list);
        }
        return list;
    }

public class VendorService : IVendorService
{
    public async Task&lt;IList&lt;Vendor&gt;&gt; GetAllAsync()
    {
        string key = "VendorService_" + "GetAllAsync";
        var list = _memoryCacheService.Get&lt;IList&lt;Vendor&gt;&gt;(key);
        if (list == null)
        {
            list = await _repository.GetAllAsync();
            _memoryCacheService.Save(key, list);
        }
        return list;
    }


The key is the combination of class + method + parameter. The only change is _repository.XX(XX, XX, ...........). How can I make it DRY?
First we don't review:


ProductType
Vendor
IProductTypeService (just a little)
IVendorService
IRepository (just a little)
what is _memorxCahceService? I will use ObjectCache.


What we need to discuss?

A generic service implementation which will contain the common methods and implementation.

BaseService

We will need a class which can be the bridge to the repository and the cache so define it aas BaseService. T means we will use it in different repository+cache combose int a generic way. No black magic here, move on.

First we need to extract the common things from the two service implementation in you question:


get a cache key
try load from cache the data




public abstract class BaseService&lt;T&gt;
{
    private ObjectCache ObjectCache { get; set; }
    private IRepository&lt;T&gt; Repository { get; set; }

    protected BaseService(IRepository&lt;T&gt; repository)
        : this(MemoryCache.Default, repository)
    {
    }

    protected BaseService(ObjectCache objectCache, IRepository&lt;T&gt; repository)
    {
        ObjectCache = objectCache;
        Repository = repository;
    }

    protected virtual string GetCacheKey(string suffix, int sectionId = -1)
    {
        return GetType().Name + "_" + suffix + sectionId;
    }

    protected virtual async Task&lt;ICollection&lt;T&gt;&gt; GetAllAsyncValueFactory()
    {
        return await Repository.GetAllAsync();
    }

    public virtual async Task&lt;ICollection&lt;T&gt;&gt; GetAllAsync(int sectionId = -1)
    {
        var key = GetCacheKey("GetAllAsync", sectionId);

        var list = ObjectCache.Get(key) as ICollection&lt;T&gt;;

        if (list == null)
        {
            return ObjectCache.AddOrGetExisting(key, await GetAllAsyncValueFactory(), new CacheItemPolicy
            {
                //TODO: define cache policy
            }) as ICollection&lt;T&gt;;
        }

        return list;
    }
    }


What do we here is that we defining in the constructor what we will need: a repository and an ObjectCache but we could you a default MemoryCache instance if we don't get anything other.

The main thing is i have extracted all the logic you are using commonly in your code but with a generic implementation (hint: don't use IList is a dirty interface). Every method is virtual so they can be easily override in derived classes you you can define different cache keys or you can tell how you would want to get the data from the current repository.

Interface implementation

Because the generic base class we only need to define the constructors in our interface implementation classes as you can se:

public class ProductTypeService : BaseService&lt;ProductType&gt;, IProductTypeService
{
    public ProductTypeService(ObjectCache objectCache, IRepository&lt;ProductType&gt; repository)
        : base(objectCache, repository)
    {
    }

    public ProductTypeService(IRepository&lt;ProductType&gt; repository)
        : base(repository)
    {
    }
}


The constructor implementation will be different based on your unknown needs.