0

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

pampua84
  • 696
  • 10
  • 33
  • 1
    You could add a `Func>` parameter and let the caller pass the adding logic – Biesi Sep 10 '20 at 08:08
  • 1
    First thing first. `return await Task.FromResult(id);` this is redundant just return the Id – TheGeneral Sep 10 '20 at 08:08
  • 1
    Secondly, just be aware the more you go down this path, the more abstractions you create, the more specialized the methods, the more you are going to need to hack around things in all but the most trivial of cases, the more of a maintenance nightmare it will become. Don't get me wrong, DRY your self to death, but dont be afraid of declarative code that is easy to understand and debug – TheGeneral Sep 10 '20 at 08:12

1 Answers1

2

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.

Sebastian Schumann
  • 3,204
  • 19
  • 37