Search code examples
c#.netrefactoringdry

C# Refactor method to respect the DRY principle


I have a question about how to implement a method to be able to use it in other methods without repeating code and respecting the DRY principle. Let me explain better, I have more methods like this:

public async Task<int> AddCustomerPackageAsync(Guid customerId, CustomerPackageDto customerPackageDto)
{
    var customer = await GetCustomerFromRepositoryAsync(customerId);

    var customerPackage = _mapper.Map<CustomerPackage>(customerPackageDto, options =>
        options.AfterMap((o, dest) =>
        {
            dest.customerID = customer.Id;
            dest.Added = DateTime.UtcNow;
        }))

    var id = await _repository.AddCustomerPackageAsync(customerPackage);

    return await Task.FromResult(id);
}

then:

public async Task<int> AddCustomerOrderAsync
public async Task<int> AddCustomerAddressAsync
.....

I thought about writing a generic method to use in the methods above, like this:

private async Task<int> Add<T, TResult>(Guid customerId, T dto) where TResult : CustomerBase
{
    var customer = await GetClientFromRepositoryAsync(customerId);

    var entity = _mapper.Map<TResult>(dto, options =>
    {
        options.AfterMap((o, customerBase) => customerBase.Id = customer.Id);
    });

    // Inject here different _repository methods
    // var id = async _repository.Add....

    return await Task.FromResult(id);
}

Where i want inject different repository methods like:

_repository.AddCustomerOrderAsync(clientOrder);

or

_repository.AddCustomerPackageAsync(customerPackage);

How can I refactor the generic method to get my goal? It's possible to do it? Thank you


Solution

  • What about this implementation?:

    private async Task<int> Add<T, TResult>(Guid customerId, T dto, Func<TResult, Task<int>> addToRepo) where TResult : CustomerBase
    {
        var customer = await GetClientFromRepositoryAsync(customerId);
    
        var entity = _mapper.Map<TResult>(dto, options =>
        {
            options.AfterMap((o, customerBase) => customerBase.Id = customer.Id);
        });
        
        // You can write it in one line - it's just to be clear about returning the id.
        var id = await addToRepo(entity);
        return id;
    }
    

    And invoke it like:

    var orderId = await Add(customerId, dto, this._repository.AddCustomerOrderAsync);
    var packageId = await Add(customerId, dto, this._repository.AddCustomerPackageAsync); 
    

    Maybe you need to specify the generics explicitly.

    I think that is what @Biesi Grr was trying to tell you.