Home > database >  Making a method generic
Making a method generic

Time:01-08

I have the following object:

public class Store : BaseEntity
{
  public virtual List<ConsumerClubDto>? RelatedConsumerClubs { get; set; }
  public virtual List<GiftDto>? RelatedGifts { get; set; }
}

These two lists implements from the same interface.

I have the following extension method running on each list. At this point I have duplicate this method - one is running on the ConsumerClubDto and the other is GiftDto.

public static async Task UpdateList(this List<ConsumerClubDto>? clubsDto, Store storeFromRepo, AppDbContext dbContext)
  {
    if (clubsDto?.Count > 0)
    {
      foreach (ConsumerClubDto clubDto in clubsDto)
      {
        if ((storeFromRepo.RelatedConsumerClubs?.FindIndex(x => x.Id == clubDto.Id) == -1))
        {
          ConsumerClub t = await dbContext.ConsumerClubs.FindAsync(clubDto.Id);
          storeFromRepo.RelatedConsumerClubs.Add(t);
        }
      }
      List<ConsumerClub> clubsToRemove = new List<ConsumerClub>();
      foreach (ConsumerClub club in storeFromRepo.RelatedConsumerClubs)
      {
        if (clubsDto.FindIndex(x => x.Id == club.Id) == -1)
          clubsToRemove.Add(club);
      }
      if (clubsToRemove.Count > 0)
      {
        foreach (ConsumerClub club in clubsToRemove)
          storeFromRepo.RelatedConsumerClubs.Remove(club);
      }
    }
}

I am sure I can write a generic method and void duplicate the code. I am not sure how.

Any pointers are welcome.

CodePudding user response:

There are maybe better solutions depending on your class hierarchy, base classes and implemented interfaces. But to simply avoid duplicate code over and over and without changing your existing classes or without having common base classes and interfaces this might be a good start.

In the first run, we need to decouple the method from the Store and the AppDbContext. This can be achieved by using the functional programming paradigm:

public static async Task UpdateList(this List<ConsumerClubDto>? clubsDto, List<ConsumerClubDto>? listInStoreFromRepo, Func<ConsumerClubDto, Task<ConsumerClub>> lookupAsync, Func<ConsumerClubDto, ConsumerClubDto, bool> match)
{
    if (clubsDto?.Count > 0)
    {
        foreach (ConsumerClubDto clubDto in clubsDto)
        {
            if ((listInStoreFromRepo?.FindIndex(x => match(x, clubDto)) == -1))
            {
                ConsumerClub t = await lookupAsync(clubDto);
                listInStoreFromRepo.Add(t);
            }
        }
        List<ConsumerClub> clubsToRemove = new List<ConsumerClub>();
        foreach (ConsumerClub club in listInStoreFromRepo)
        {
            if (clubsDto.FindIndex(x => match(x, club)) == -1)
                clubsToRemove.Add(club);
        }
        if (clubsToRemove.Count > 0)
        {
            foreach (ConsumerClub club in clubsToRemove)
                listInStoreFromRepo.Remove(club);
        }
    }
}

In the second run, we can introduce generic type arguments to replace ConsumerClubDto and ConsumerClub. It seems that ConsumerClub is inheriting from ConsumerClubDto. Based on this, we can refactor the code into the following form:

public static async Task UpdateList<TDto, T>(this List<TDto>? dtoList, List<TDto>? listInStoreFromRepo, Func<TDto, Task<T>> lookupAsync, Func<TDto, TDto, bool> match) where T : TDto
{
    if (dtoList?.Count > 0)
    {
        foreach (TDto dto in dtoList)
        {
            if ((listInStoreFromRepo?.FindIndex(x => match(x, dto)) == -1))
            {
                T t = await lookupAsync(dto);
                listInStoreFromRepo.Add(t);
            }
        }
        List<T> clubsToRemove = new List<T>();
        foreach (T o in listInStoreFromRepo)
        {
            if (dtoList.FindIndex(x => match(x, o)) == -1)
                clubsToRemove.Add(o);
        }
        if (clubsToRemove.Count > 0)
        {
            foreach (T club in clubsToRemove)
                listInStoreFromRepo.Remove(club);
        }
    }
}

Finally you can change your existing code to directly call this generic method or you can change your existing methods to make use of the generic method to avoid duplicate code.

public static async Task UpdateList(this List<ConsumerClubDto>? clubsDto, Store storeFromRepo, AppDbContext dbContext)
{
    await clubsDto.UpdateList(
        storeFromRepo.RelatedConsumerClubs,
        dto => dbContext.ConsumerClubs.FindAsync(dto.Id),
        (x, y) => x.Id == y.Id);
}
  •  Tags:  
  • Related