0
votes

I am using Ninject in ASP.NET WEB API with AutoMapper to map Service Models to Entity Model. In this, I am trying to first map 'NewUser' service model to 'User' entity model and soring the 'NewUser' to database and finally mapping the newly stored data to 'User' service model. Here is the NinjectDependencyResolver class:

public sealed class NinjectDipendencyResolver :IDependencyResolver 
    {
        private readonly IKernel _container;

        public NinjectDipendencyResolver(IKernel contanier)
        {
            _container = contanier;
        }

        public IKernel Container
        {
            get { return _container; }
        }
        public object GetService(Type servicetType)
        {
            return _container.TryGet(servicetType);
        }
        public  IEnumerable<object> GetServices(Type ServiceType)
        {
            return _container.GetAll(ServiceType);
        }
        public IDependencyScope BeginScope()
        {
            return this;
        } 
        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }

Here are the NewUser to User enity and User entity to User service model mapping implementations:

Mapper.Initialize(cfg => cfg.CreateMap<Models.NewUser, Data.Entities.User>()
        .ForMember(m => m.UserId, i => i.Ignore())
                .ForMember(m => m.AuthKey, i => i.Ignore())
                .ForMember(m => m.DisabledDate, i => i.Ignore())
                .ForMember(m => m.JoinedDate, i => i.Ignore())
                .ForMember(m => m.LastSeen, i => i.Ignore())
                .ForMember(m => m.Version, i => i.Ignore())
                .ForMember(m => m.Goals, i => i.Ignore()));
Mapper.Initialize(cfg => cfg.CreateMap<Data.Entities.User, Models.User>()
            .ForMember(m => m.Links, i => i.Ignore())
            .ForMember(m => m.Goals, i => i.MapFrom(j => Mapper.Map<ICollection<Data.Entities.Goal>, List<Models.Goal>>(j.Goals)))
            .ReverseMap()
            .ForMember(m => m.Goals, i => i.MapFrom(j => Mapper.Map<List<Models.Goal>, ICollection<Data.Entities.Goal>>(j.Goals)))
            .ForMember(m => m.Version, i => i.Ignore()));

Here is NinjectConfigurator class where I am configuring all the Mappings to single IAutoMapperTypeConfigurator:

    public class NinjectConfigurator
        {
            public void Configure(IKernel container)
            {
                AddBindings(container);
            }
            private void AddBindings(IKernel container)
            {
                ConfigureUnitOfWork(container);
                ConfigureAutoMapper(container);
            }
            private void ConfigureUnitOfWork(IKernel container)
            {
                var unitOfWork = new UnitOfWork(new AppTestDBContext());
                container.Bind<IUnitOfWork>().ToConstant(unitOfWork);
            }
            private void ConfigureAutoMapper(IKernel container)
            {
                container.Bind<IAutoMapper>()
                            .To<AutoMapperAdapter>()
                            .InSingletonScope();
                container.Bind<IAutoMapperTypeConfigurator>()
                            .To<NewUserMapper>()
                            .InSingletonScope();
                container.Bind<IAutoMapperTypeConfigurator>()
                            .To<UserMapper>()
                            .InSingletonScope();
            }
}

And AutoMapperConfigurator where I am configuring the mappings in one go:

public class AutoMapperConfigurator
    {
        public void Configure(IEnumerable<IAutoMapperTypeConfigurator> autoMapperTypeConfigurations)
        {
            autoMapperTypeConfigurations.ToList().ForEach(m => m.Configure());
            Mapper.AssertConfigurationIsValid();
        }
    }

And here my Global.ascx.cs where I am configuring the Mappings on Application_Start:

protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
            new AutoMapperConfigurator().Configure(
                WebContainerManager.GetAll<IAutoMapperTypeConfigurator>());
        }

Controller action:

public async Task<IHttpActionResult> Post(NewUser newUser)
        {
            var createdUser = _iUserMaintenanceProcessor.Add(newUser);
            if (createdUser != null)
            {
                return Ok(createdUser);
            }
            else
                return NotFound();
        }

and

public Models.User Add(NewUser newUser)
        {
            var userEntity = _autoMapper.Map<Data.Entities.User>(newUser);
            _unitOfWork.User.Add(userEntity);
            var user = _autoMapper.Map<Models.User>(userEntity);
            return user;
        }

The problem is whenever I call the Post method of the Api through fiddler it is throwing following exception:

{"Message":"An error has occurred.","ExceptionMessage":"Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nNewUser -> User\r\nTestApp.Web.Api.Models.NewUser -> TestApp.Data.Entities.User","ExceptionType":"AutoMapper.AutoMapperMappingException"}

Please help me to figure out the cause.

UPDATE : I tried to add initialize the mappings on the mathod itself and it worked. Looks like the mappings are getting initialized on Application_Start but as mentioned above i have initialized the mappings. Here is the updated mehthod which is working:

public Models.User Add(NewUser newUser)
        {
            Mapper.Initialize(cfg => cfg.CreateMap<Models.NewUser, Data.Entities.User>()
            .ForMember(m => m.UserId, i => i.Ignore())
                    .ForMember(m => m.AuthKey, i => i.Ignore())
                    .ForMember(m => m.DisabledDate, i => i.Ignore())
                    .ForMember(m => m.JoinedDate, i => i.Ignore())
                    .ForMember(m => m.LastSeen, i => i.Ignore())
                    .ForMember(m => m.Version, i => i.Ignore())
                    .ForMember(m => m.Goals, i => i.Ignore()));
            var userEntity = _autoMapper.Map<Data.Entities.User>(newUser);
            _unitOfWork.User.Add(userEntity);
            Mapper.Initialize(cfg => cfg.CreateMap<Data.Entities.User, Models.User>()
            .ForMember(m => m.Links, i => i.Ignore())
            .ForMember(m => m.Goals, i => i.MapFrom(j => Mapper.Map<ICollection<Data.Entities.Goal>, List<Models.Goal>>(j.Goals)))
            .ReverseMap()
            .ForMember(m => m.Goals, i => i.MapFrom(j => Mapper.Map<List<Models.Goal>, ICollection<Data.Entities.Goal>>(j.Goals)))
            .ForMember(m => m.Version, i => i.Ignore()));
            var user = _autoMapper.Map<Models.User>(userEntity);
            return user;
        }
1

1 Answers

1
votes

You can run Initialize method only once. So what i sugest you to do is to change code as below:

Change IAutoMapperTypeConfigurator interface to take configuration property

public interface IAutoMapperTypeConfigurator
{
    void Configure(IMapperConfigurationExpression configuration);
}

Change mappers to use this configuration property

public void Configure(IMapperConfigurationExpression configuration)
{
    configuration.CreateMap<Models.NewUser, Data.Entities.User>()
    .ForMember(m => m.UserId, i => i.Ignore())
            .ForMember(m => m.AuthKey, i => i.Ignore())
            .ForMember(m => m.DisabledDate, i => i.Ignore())
            .ForMember(m => m.JoinedDate, i => i.Ignore())
            .ForMember(m => m.LastSeen, i => i.Ignore())
            .ForMember(m => m.Version, i => i.Ignore())
            .ForMember(m => m.Goals, i => i.Ignore());
}

In AutoMapperConfiguration change method to run Initialize only once

public void Configure(IEnumerable<IAutoMapperTypeConfigurator> autoMapperTypeConfigurations)
{
    Mapper.Initialize(config => autoMapperTypeConfigurations.ToList().ForEach(m => m.Configure(config)));
    Mapper.AssertConfigurationIsValid();
}

This should work.