1

How can I convert these two ConvertDtoListToAddresses and ConvertDtoListToDocuments C# methods to a generic? I've tried passing in two generic type variables, but when I get down to 'Add' in the loop I get stuck on various errors. Converting from a DTO to its respective DBO is done in the constructor of the DBO, which I think is part of the problem.

private void ConvertDtoToPerson(PersonDTO dto)
{
    Id = dto.Id;
    Fname = dto.FirstName;
    Mname = dto.MiddleName;
    Lname = dto.LastName;
    Suffix = dto.Suffix;
    Maiden = dto.MaidenName;
    Addresses = ConvertDtoListToAddresses(dto.AddressesDto); // want to convert to generic
    Documents = ConvertDtoListToDocuments(dto.DocumentsDto); // want to convert to generic
}

private static ICollection<Address>? ConvertDtoListToAddresses(ICollection<AddressDTO>? addressesDto)
{
    if (addressesDto is not null && addressesDto.Any())
    {
        ICollection<Address> addys = new List<Address>();

        foreach (AddressDTO dto in addressesDto)
        {
            // Converts from dto in constructor
            addys.Add(new Address(dto));
        }

        return addys;
    }

    return null;
}

private static ICollection<Document>? ConvertDtoListToDocuments(ICollection<DocumentDTO>? documentsDto)
{
    if (documentsDto is not null && documentsDto.Any())
    {
        ICollection<Document> docs = new List<Document>();

        foreach (DocumentDTO dto in documentsDto)
        {
            // Converts from dto in constructor
            docs.Add(new Document(dto));
        }

        return docs;
    }

    return null;
}

Here is what I tried:

Addresses = ConvertDtoListToType<Address, AddressDTO>(dto.AddressesDto);
private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection)
{
    if (dtoCollection is not null && dtoCollection.Any())
    {
        ICollection<T> tList = new List<T>();

        foreach (D dto in dtoCollection)
        {
            tList.Add(new T(dto));  // <-- This is where I'm having trouble
        }

        return tList;
    }

    return null;
}
7
  • 1
    Have you tried AutoMapper Commented Mar 19, 2022 at 3:12
  • 1
    It looks like 90% of your boilerplate would be eliminated by LINQ .Select. Commented Mar 19, 2022 at 3:14
  • 2
    dtoCollection?.Select(x => new Document(x)).ToList() Commented Mar 19, 2022 at 3:16
  • What error are you getting? Commented Mar 19, 2022 at 4:09
  • @Zee Yes, I'm familiar with AM and have used it in the past, but am trying to get away from it. Commented Mar 19, 2022 at 14:34

2 Answers 2

1

Use of a Func<D, T> factory parameter would sort this out.

private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
{
    if (dtoCollection is not null && dtoCollection.Any())
    {
        ICollection<T> tList = new List<T>();
        foreach (D dto in dtoCollection)
        {
            tList.Add(factory(dto));
        }
        return tList;
    }
    return null;
}

Do keep in mind that that is almost the semantic equivalent of this:

private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
    => dtoCollection?.Select(d => factory(d))?.ToList();

I'd question the idea that an empty dtoCollection should return a null final collection anyway. This is probably a better implementation.

So, having said that, your original method offers very little functionality benefit. It's code for code's sake. A simple Select/ToList pair keeps your code simple.

In any case, you can provide a static method off of Address and Document to provide the Func<D, T> that you need.

public class Address
{
    AddressDTO dto;

    public static Address CreateFromDto(AddressDTO dto)
        => new Address(dto);

    public Address(AddressDTO dto)
    {
        this.dto = dto;
    }
}

Now, calling it is like this:

var addresses = ConvertDtoListToType(addressDtos, Address.CreateFromDto);

Or:

var addresses = addressDtos?.Select(Address.CreateFromDto)?.ToList();
Sign up to request clarification or add additional context in comments.

1 Comment

Brilliant! Ended out forgoing all that extra code and just using the Select with CreateFromDto combination. Works perfectly and so much cleaner. Thanks so much!
1

What you need is to be able to provide a constraint on the Type T to say that it has a constructor that takes a parameter of type D. Something like this:

private static ICollection<T>? ConvertDtoListToType<T, D>(
  ICollection<D>? dtoCollection) where T : new(D) 
{}

But this does not exist in C#. The workaround is to provide a factory method to create your type T given a type D. i.e. Something like:

private static ICollection<T>? ConvertDtoListToType<T, D>(
  ICollection<D>? dtoCollection, Func<D, T> factory)
{
    // Use factory(dto), instead of new T(dto)
}

But as @Zee says, you should have a look at Automapper, which can convert between types of collections.

1 Comment

Helpful, thanks! I ended out using the solution Enigmativity suggested.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.