Search code examples
c#datamapperdryioc

DryIoc and ExpressMapper


I'm using DryIoc and ExpressMapper.

I wrap ExpressMapper inside another class and at some point it should be use to map to a type with a parametrized constructor. Actually it's when I map a view model to a business model. The parameter is an instance of a repository class.

Using TDD, my Mapper class cended up like this (note the constructor accepting a Func).

public class Mapper<T1, T2> : IMapper<T1, T2>
{
    private readonly MappingServiceProvider _mapper;

    public Mapper()
    {
        _mapper = new MappingServiceProvider();
        _mapper.Register<T1, T2>();
    }

    public Mapper(Func<T2> func)
    {
        _mapper = new MappingServiceProvider();
        _mapper.Register<T1, T2>().Instantiate((t1) => func());
    }

    public T2 Map(T1 t)
    {
        return _mapper.Map<T1, T2>(t);
    }
}

Thus I tried to use the Ioc like this :

        Func<IActivitiesModel>factoryActivitiesModel = () => container.Resolve<IActivitiesModel>();
        container.Register(Made.Of(() => factoryActivitiesModel));
        // () => new ActivitiesModel(container.Resolve<IActivityRepository>(IfUnresolved.Throw)
        container.Register<IMapper<ActivitiesViewModel, IActivitiesModel>, Mapper<ActivitiesViewModel, IActivitiesModel>>(Reuse.Singleton, Made.Of(
            () => new Mapper<ActivitiesViewModel, IActivitiesModel>(Arg.Of<Func<IActivitiesModel>>())
        ));

But it didn't do.

Unable to use null factory object with factory method ActivitiesMVC.Ioc.<>c__DisplayClass4_0::System.Func`1[ActivitiesLogic.Models.IActivitiesModel] factoryActivitiesModel when resolving: Func<ActivitiesLogic.Models.IActivitiesModel>.

Anyway, I tried different approaches afterward but none seemed to work.

I'd like to avoid to store the container as a static singleton and having the mapper relies on it (I'd like the mapper to remain Ioc agnostic).

How can it be achieved?

EDIt : current solution (with bad static singleton)

public class Ioc
{
    public class Factory<T> : IFactory<T>
    {
        public T Create()
        {
            return Container.Resolve<T>();
        }
    }

    private static Lazy<Container> _container;

    public static Container Container => _container.Value;

    static Ioc()
    {
        _container = new Lazy<Container>(GetContainer);
    }

    static private Container GetContainer()
    {
        var container = new Container(rules => rules
            //.WithoutThrowOnRegisteringDisposableTransient()
            .WithTrackingDisposableTransients());
        container.Register(Made.Of(() => new ActivityController(Arg.Of<IMapper<ActivitiesViewModel, IActivitiesModel>>())));
        container.Register<ILabContext, LabContext>(new SingletonReuse());
        container.Register<IActivityRepository, ActivityRepository>(new SingletonReuse());
        container.Register<IActivitiesModel>(made: Made.Of(() => new ActivitiesModel(Arg.Of<IActivityRepository>())));
        container.Register(typeof(IFactory<>), typeof(Factory<>), new SingletonReuse());
        container.Register<IMapper<ActivitiesViewModel, IActivitiesModel>, Mapper<ActivitiesViewModel, IActivitiesModel>>(Reuse.Singleton, Made.Of(
            () => new Mapper<ActivitiesViewModel, IActivitiesModel>(Arg.Of<IFactory<IActivitiesModel>>())
        ));
        container.Register<IMapper<ActivitiesModel, ActivitiesEntity>, Mapper<ActivitiesModel, ActivitiesEntity>>(new SingletonReuse(), Made.Of(
            () => new Mapper<ActivitiesModel, ActivitiesEntity>()
        ));

        return container;
        ;
    }
}

Solution

  • First, I am not familiar with ExpressMapper, so will try to help based on your sample code alone.

    As I understood you want to register activities model, repository, vm, etc., as well as IMapper / Mapper implementation which relies on Func dependency. Another detail is mapper has two constructors, that why you probably tried to use Made.Of.

    But Made.Of works with Expression>, not with Func delegate. That is the reason for exception I believe.

    Try this setup:

    container.Register(typeof(IMapper<,>), typeof(Mapper<,>), Reuse.Singleton,
        // will select second constructor with Func parameter
        made: FactoryMethod.ConstructorWithResolvableArguments);
    
    // normal model registrations, no need to use Made.Of
    // if implementations have single constructor,
    // othetwise try use the same made as for Mapper.
    container.Register<IActivitiesModel, ActivitiesModel>();
    // ... the same way register repository, vm, etc. 
    

    If the situation with multiple constructors is common in your code (May be it is a requirement of ExpressMapper to have a default ctor?) Then you may configure automatic constructor selection globally per container:

    container = new Container(rules => rules
        .With(FactoryMethod.ConstructorWithResolvableArguments));