How to Define Extension Method for ICollection<T> where T : IMyInterface without Specifying T in the Method Definition

Background: I want to hook in business rules at the point that DTOs are being mapped to entities. I figured encapsulating the mapping into an extension method would be a good route.

IEntityDto is the interface that all DTOs that can be directly mapped to entities implement.

The single instance works fine:

public static TEntity MapTo<TEntity>(this IEntityDto dto)
{
    ... Run Business Rules that don't require db access ...
    return AutoMapper.Mapper.Map<TEntity>(dto);
}

I'd like to also extend ICollection the same way:

public static ICollection<TEntity> MapToCollection<TEntity>(this ICollection<IEntityDto> dtos)
{
    ... Run Business Rules that don't require db access ...
    return AutoMapper.Mapper.Map<ICollection<TEntity>>(dtos);
}

Unfortunately, MapToCollection does not show up on the context menu or compile when applied to an ICollection of IEntityDto.

What am I missing to get this to work? Do I need to just extend ICollection where T is IEntityDto? I'd prefer to not have to include the DTO type when calling the extension method.

public static ICollection<TEntity>MapToCollection<TDto,TEntity>(this ICollection<TDto> dtos) where TDto: IEntityDto
{
    ... Do Work and Return ...
}

The above works, but I was hoping to infer T from the collection.

Thanks!

Jon Skeet
people
quotationmark

You effectively need a method with a signature of

public static ICollection<TEntity> MapToCollection<TEntity, TEntityDto>(
    this ICollection<TEntityDto> dtos)
    where TEntityDto : IEntityDto

... but that would force you to specify both type arguments, which I understand you don't want to do.

What you can do instead is go in two hops, e.g.

public static class DtoExtensions
{
    public static CollectionMapper<TEntityDto> Map(this ICollection<TEntityDto> dtos)
        where TEntityDto : IEntityDto
    {
        return new CollectionMapper<TEntityDto>(dtos);
    }
}

public class CollectionMapper<TEntityDto> where TEntityDto : IEntityDto
{
    private readonly ICollection<TEntityDto> dtos;

    public CollectionMapper(ICollection<TEntityDto> dtos)
    {
        this.dtos = dtos;
    }

    public ICollection<TEntity> To<TEntity>()
    {
        // Business rules...
        return AutoMapper.Mapper.Map<ICollection<TEntity>>(dtos);
    }
}

You can now use:

var result = collection.Map().To<FooEntity>();

The Map call infers TEntityDto, and you specify TEntity in the To call.

people

See more on this question at Stackoverflow