Mapster.DI IRegister class with autofac Injected interfaces
Hello,
I'm struggling to register a mapster configuration using .Scan function from Mapster DI. I have a specific date time parser that I want to assure that all strings are parsed from a specific format:
public class DateTimeParser : IDateTimeParser
{
public static readonly string DateTimeFormat = "yyyy-MM-dd";
public DateTime Parse(string date)
{
if (!DateTime.TryParseExact(date, DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None,
out var parsedDate))
{
throw new ArgumentException(
$"Incorrect format for: '{nameof(date)}': '{date}'.",
nameof(date));
}
return parsedDate;
}
}
As you can see I have interface so that I register Interface using autofac via assembly scan:
private static void RegisterWithAssemblyScan(ContainerBuilder builder)
{
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Parser"))
.AsImplementedInterfaces();
}
And that works fine, I can use this IDateTimeParser anywhere when I want, except the place that I have my map:
public class SomeClassMaps : IRegister
{
private readonly IDateTimeParser _dateTimeParser;
// TODO: Figure out how to get rid of this tight coupling when registering mapster maps.
public SomeClassMaps()
{
_dateTimeParser = new DateTimeParser();
}
public SomeClassMaps(IDateTimeParser dateTimeParser)
{
_dateTimeParser = dateTimeParser;
}
public void Register(TypeAdapterConfig config)
{
config.ForType<SomeClassToMapFrom, SomeClassToMapTo>()
.Map(dest => dest.TargetDateTime, src => _dateTimeParser.Parse(src.TargetDateString));
}
}
As you can see for now I have to expose default constructor and use tight coupling to be able to use my parser. How can I get rid of tight coupling in such case?
If I will comment out that TODO ctor I get an error that stops starting application:
[10:48:43 FTL] x backend Application terminated unexpectedly
System.MissingMethodException: Cannot dynamically create an instance of type 'x.Application.Maps.UpdateCaseMaps'. Reason: No parameterless constructor defined.
at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type)
at Mapster.TypeAdapterConfig.<>c.<Scan>b__87_3(Type registerType)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at System.Collections.Generic.List`1.AddRange(IEnumerable`1 collection)
at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToList()
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Mapster.TypeAdapterConfig.Scan(Assembly[] assemblies)
at x.Application.Container.ApplicationContainer.RegisterMapster(ContainerBuilder containerBuilder) in D:\work\x\src\x\x.Application\Container\Ap
plicationContainer.cs:line 28
Registration of maps looks like this:
private static void RegisterMapster(ContainerBuilder containerBuilder)
{
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
var assembly = Assembly.GetExecutingAssembly();
typeAdapterConfig.Scan(assembly);
containerBuilder.RegisterInstance(typeAdapterConfig);
containerBuilder.RegisterType<ServiceMapper>()
.As<IMapper>()
.InstancePerLifetimeScope();
}
Hello @anotowski As far as I understand while working on open Issue. The Mapster is a Type Orientet mapper, not a dependency injector. Therefore, in order to successfully build a matching function, all dependencies must be resolved before transferring to Mapster. Otherwise there is simply nothing to analyze) If you need validation, then make a decorator or a special class that does two steps:
- Performs your verification of the source. If successful step 2
- Calls the mapper for the Source and Destination Target types
Or You must explicitly register SomeClassMaps implementation to the standard interface in the builder container.