Search code examples
dependency-injectionasp.net-core.net-coreautomappertypeconverter

AutoMapper Dependency Injection with ITypeConverter


I'm trying to use Automapper to convert all my DateTimes from UTC to Local Time but i need to inject an interface into ITypeConverter... I'm receiving this error when I run the application: MissingMethodException: No parameterless constructor defined for this object.

I think that the problem is with the dependency injection code!

Anyone can help me?

UserRepository:

public class UserRepository : IUserRepository
{
    private static readonly MapperConfiguration Config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<User, User>();

        cfg.CreateMap<DateTime?, DateTime?>().ConvertUsing<UtcToLocalConverter>();
    });

    public List<User> GetById(string[] ids)
    {
        var result = BuildQuery().Where(w => ids.Contains(w.UserName)).ToList();

        var mapper = Config.CreateMapper();

        return mapper.Map<List<User>>(result); // Throws error !!!
    }

    #region Helper

    private IQueryable<User> BuildQuery()
    {
        return Settings.EpedDb.User;
    }

    #endregion
}

Converter

public class UtcToLocalConverter : ITypeConverter<DateTime?, DateTime?>
{
    public UtcToLocalConverter(IBaseSettings baseClass) // I tried to inject here!!!
    {
        Settings = baseClass;
    }

    private IBaseSettings Settings { get; }

    public DateTime? Convert(DateTime? source, DateTime? destination, ResolutionContext context)
    {
        if (source == null) return null;

        var tzi = TimeZoneInfo.FindSystemTimeZoneById(Settings.UserData.TimeZone);
        return TimeZoneInfo.ConvertTime(DateTime.SpecifyKind((DateTime)source, DateTimeKind.Utc), tzi);
    }
}

Solution

  • Your hunch is correct: you can't inject any parameters into the constructor when you use CreateUsing<TTypeConverter>(). The type must have a parameterless constructor.

    You could pass a single instance to CreateUsing():

    var converter = new UtcToLocalConverter(mySettings);
    cfg.CreateMap<DateTime?, DateTime?>().ConvertUsing(converter);
    

    But I suspect that won't work because you're using dependency injection to try to handle the user's time zone at runtime.

    I think the real solution to your problem is to not deal with time zones at this layer of your application. The .NET DateTime class is notoriously bad at handling time zones.

    You should:

    • Use DateTimeOffset? instead of DateTime?
    • Always store dates in UTC (offset 0)
    • Don't worry about converting time zones in your application code
    • At the rendering or presentation layer, render the UTC date in the user's local time zone

    This is a much cleaner way of handling dates and time zones.