0
votes

I'm sure I am missing something simple. First, I'll show all the code I have written to wire up the plumbing, then I'll show the exception message. Then, I'll set out what I have tried to fix it.

LicenceTrackerProfile

public class LicenceTrackerProfile : Profile
{
    const string LicenceTrackerProfileName = "LicenceTrackerProfile";

    public override string ProfileName
    {
        get { return LicenceTrackerProfileName; }
    }

    protected override void Configure()
    {
        // initialize mappings here
        new ViewModelMappings(this).Initialize();
    }

}

MapperBootstrapper

public class MapperBootstrapper
{
    public void Configure()
    {
        var profile = new LicenceTrackerProfile();
        AutoMapper.Mapper.Initialize(p => p.AddProfile(profile));
    }

}

MappingBase

public abstract class MappingBase
{
    private readonly Profile _profile;

    protected MappingBase(Profile profile)
    {
        _profile = profile;
        _profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
        _profile.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
    }

    public Profile Profile
    {
        get { return _profile; }
    }
}

UniversalMapper

public class UniversalMapper : IUniversalMapper
{
    private readonly IMappingEngine _mappingEngine;

    public UniversalMapper(IMappingEngine mappingEngine)
    {
        _mappingEngine = mappingEngine;
    }

    public virtual TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
    {
        return _mappingEngine.Map(source, destination);
    }

}

ViewModelMappings

public class ViewModelMappings : MappingBase, IMappingInitializer
{
    private readonly Profile _profile;

    public ViewModelMappings(Profile profile) : base(profile)
    {
        _profile = profile;
        _profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
        _profile.DestinationMemberNamingConvention = new PascalCaseNamingConvention();

    }

    public void Initialize()
    {
        //  data to domain mappings
        Profile.CreateMap<EFDTO.Enums.FileTypes, Domain.FileTypes>();
        Profile.CreateMap<EFDTO.Licence, Domain.Licence>();
        Profile.CreateMap<EFDTO.LicenceAllocation, Domain.LicenceAllocation>();
        Profile.CreateMap<EFDTO.Person, Domain.Person>();
        Profile.CreateMap<EFDTO.Software, Domain.Software>();
        Profile.CreateMap<EFDTO.SoftwareFile, Domain.SoftwareFile>();
        Profile.CreateMap<EFDTO.SoftwareType, Domain.SoftwareType>();
    }
}

Note, the initialize method and Configure method are being called, so they're not being "missed".

Exception

Missing type map configuration or unsupported mapping.

Mapping types: Software -> Software LicenceTracker.Entities.Software -> LicenceTracker.DomainEntities.Software

Destination path: Software

Source value: LicenceTracker.Entities.Software

Troubleshooting Ignoring columns. I planned to ignore columns, starting with all and then eliminating them by un-ignoring them 1 by 1 until I found the problem columns. However, to my surprise, the error occurs when I ignore all columns:

Profile.CreateMap<EFDTO.Software, Domain.Software>()
    .ForMember(software => software.Licences, e => e.Ignore())
    .ForMember(software => software.Name, e => e.Ignore())
    .ForMember(software => software.SoftwareFiles, e => e.Ignore())
    .ForMember(software => software.Type, e => e.Ignore())
    .ForMember(software => software.Description, e => e.Ignore())
    .ForMember(software => software.Id, e => e.Ignore())
    .ForMember(software => software.TypeId, e => e.Ignore()
    .ForMember(software => software.ObjectState, e => e.Ignore());

The Domain entities have [DataContract] (at class level) and [DataMember] (at method level) attributes. I added each of those attributes to the EF entities as well.

Other than that, I am out of ideas. It all seems to be wired up correctly.

What did I miss?

1
Any bites? I've spent so many hours trying to figure this out. I'm using the static Mapper.Engine and initializing the profile using the static Mapper class. The mapping is there, so why does it claim to be unsupported?onefootswill

1 Answers

0
votes

I'm back to heroically answer my question.

The problem was in the Service which created the UniversalMapper object (forgive the sloppy code, it is not final yet):

public class LicenceTrackerService : ILicenceTrackerService, IDisposable
{
    LicenceTrackerContext context = new LicenceTrackerContext();
    private MapperBootstrapper mapperBootstrapper;
    private IUniversalMapper mapper = new UniversalMapper(Mapper.Engine);
    private IUnitOfWork unitOfWork;

    public LicenceTrackerService()
    {            
        unitOfWork = new UnitOfWork(context, new RepositoryProvider(new RepositoryFactories()));
        mapperBootstrapper  = new MapperBootstrapper();
        mapperBootstrapper.Configure();

        Database.SetInitializer(new LicenceTrackerInitializer());
        context.Database.Initialize(true);
    }

    public int GetNumber()
    {
        return 42;
    }

    public List<LicenceTracker.DomainEntities.Software> GetSoftwareProducts()
    {
        var productsRepo = unitOfWork.Repository<Software>();

        var list = productsRepo.Query().Select().ToList();

        var softwareList = new List<LicenceTracker.DomainEntities.Software>();

        foreach (var software in list)
        {
            var softwareProduct = new LicenceTracker.DomainEntities.Software();
            softwareList.Add(Mapper.Map(software, softwareProduct));
        }

        return softwareList;
    }

    public void Dispose()
    {
        unitOfWork.Dispose();
    }
}

I'm still not sure why, but initializing the mapper outside of the constructor (default value style) was not happy. By moving that instantiation into the constructor of the service, it worked:

    private IUniversalMapper mapper;

    public LicenceTrackerService()
    {
        mapper = new UniversalMapper(Mapper.Engine);
        ...
    }

There's obviously something about static properties (Mapper.Engine) and default instantiations that I'm not understanding.

Anyway, no big deal as I was planning to inject the UniversalMapper into the service anyway.

Edit I've actually figured out the problem for real now. It is an ordering thing. With Automapper, I had to initialize the mapper with the Profile before inserting the Mapper.Engine into the UniversalMapper.

Obviously, the Get aspect of the Mapper.Engine property is not just a memory reference to an object. And yes, a quick glance at the code inside Automapper confirms that.

So, assigning the result of the Get property to the _mappingEngine field of the UniversalMapper must happen after that engine has been configured.