3

I'm new to AutoMapper and trying to map Array class ItemLink[].

public class ViewModel
{
  public ItemLink[] ItemLinks { get; set; }
}

public class ItemLink
{
  public string Description { get; set; }
}

I tried:

Mapper.Map<viewModel.ItemLink>(db.ItemLinks);

Error:

"Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."

Can't it be simple mapping?

EDIT 1

To clarify more, I'm getting similar class structure from database. Example,

public class Db
{
  public ItemLink[] ItemLinks { get; set; }
}

So, I want to map ViewModel.ItemLink[] with Db.ItemLink[].

7
  • Are you trying to map a array to a string? Can you please re-frame the question? Commented Apr 22, 2017 at 15:38
  • 1
    The error suggest that you initialize the configuration before doing a mapping. You can do that by calling AutoMapper.Mapper.Initialize() as explained in the Automapper Wiki here where you have to determine exactly what are you trying to map to what. From your code, it seems you are trying to map an ItemLink[] to an ItemLink which is not a supported mapping. What exactly is it that you want to achieve? Commented Apr 22, 2017 at 16:04
  • @Biswabid: No. Just trying to map db.ItemLink to viewmodel.ItemLink. Commented Apr 22, 2017 at 16:11
  • What are viewModel.ItemLink and db.ItemLinks? What's their content and what is supposed to be their content after the mapping? You spoil your bounty when the question isn't clear. Commented Apr 25, 2017 at 13:21
  • this is strange, you are trying to map a list of objects to a object. it is difficult to understand what you need Commented Apr 26, 2017 at 10:12

5 Answers 5

5
+100

You cannot provide variable to a generic parameter like you do in Mapper.Map<viewModel.ItemLink>(db.ItemLinks);. It is called Type parameter and must be a type.

As @gisek mentioned in his answer you need to configure mapper first. Normally it is done at application startup.

You can consider to fetch full objects from db, but you also have an option to use Queryable Extensions which are there to only fetch data you need for your view model.

The configuration. I assume that you have namespace DB for entity in database, and View namespace for view model.

Mapper.Initialize(cfg => {
    cfg.CreateMap<DB.ItemLink, View.ItemLink>();
});
Mapper.Configuration.AssertConfigurationIsValid();

Fetch full entity from DB and then map it to property:

var viewModel = new View.Item();
viewModel.ItemLinks = Mapper.Map<View.ItemLink[]>(db.ItemLinks.ToArray());

Project entity from DB to view model:

var viewModel = new View.Item();
viewModel.ItemLinks = db.ItemLinks.ProjectTo<View.ItemLink>().ToArray();
Sign up to request clarification or add additional context in comments.

2 Comments

@AshwiniVerma, have you tried the code I posted in my answer? Does it work for you?
Thanks for the answer Andrii. Sorry for the delay in reply. It looks good but I'm yet to test it. I'll confirm soon
2

I assumed you are using .net mvc

Firstly you need Initialize your mapping on Application Start, there is no need to initialize every time in your controller etc.

Following error means you didn't initialize mapper, automapper doesn't know your source and destination object.

Error:

"Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."

For Solution:

Create an AutoMapperConfig object in your App_Start folder.

public class AutoMapperConfig
{
    public static void Register()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<source,destination>(); /*If source and destination object have same propery */
            cfg.CreateMap<source, destination>()
             .ForMember(dest => dest.dId, opt => opt.MapFrom(src => src.sId)); /*If source and destination object haven't same property, you need do define which property refers to source property */ 
         });
    }
}

In your Global.asax.cs

 protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        AutoMapperConfig.Register(); // Call Register method on App_Start
    }

In your controller

   var mappedArray = Mapper.Map<viewmodel.ItemLink[]>(db.ItemLinks);

or

var mappedArray = Mapper.Map<viewmodel.ItemLink[],ItemLinks[]>(db.ItemLinks); //ItemLinks[] refers to your dto.

1 Comment

Up vote for Register() tips. This will reduce code repetition a lot.
0

You need to configure the mapper first.

There are 2 possible approaches, static and non-static. I lean towards non-static as it allows you to create multiple mappers, which can use different mapping strategies.

Non-static example:

using AutoMapper;

namespace Experiments
{
    class Program
    {
        static void Main(string[] args)
        {
            var links = new ItemLink[]
            {
                new ItemLink {Description = "desc 1"},
                new ItemLink {Description = "desc 2"},
            };

            var item = new Item
            {
                ItemLinks = links,
            };

            var config = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<ItemLink, ItemLink>(); // you can extend this part of the configuration here
                cfg.CreateMap<Item, Item>();
                cfg.CreateMap<ItemLink, MyCustomClass>()
                    .ForMember(myCustomClass => myCustomClass.DescriptionWithDifferentName,
                        expression => expression.MapFrom(itemLink => itemLink.Description)); // to map to a different type
                // more configs can do here
                // e.g. cfg.CreateMap<Item, SomeOtherClass>();
            });

            IMapper mapper = new Mapper(config);
            ItemLink linkClone = mapper.Map<ItemLink>(links[0]);
            ItemLink[] linkArrayClone = mapper.Map<ItemLink[]>(item.ItemLinks);
            Item itemClone = mapper.Map<Item>(item);
            MyCustomClass myCustomClassObject = mapper.Map<MyCustomClass>(links[0]);
        }
    }

    public class Item
    {
        public ItemLink[] ItemLinks { get; set; }
    }

    public class ItemLink
    {
        public string Description { get; set; }
    }

    public class MyCustomClass
    {
        public string DescriptionWithDifferentName { get; set; }
    }
}

Static example:

using AutoMapper;

namespace Experiments
{
    class Program
    {
        static void Main(string[] args)
        {
            var links = new ItemLink[]
            {
                new ItemLink {Description = "desc 1"},
                new ItemLink {Description = "desc 2"},
            };

            var item = new Item
            {
                ItemLinks = links,
            };

            Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<ItemLink, ItemLink>(); // you can extend this part of the configuration here
                cfg.CreateMap<Item, Item>();
                cfg.CreateMap<ItemLink, MyCustomClass>()
                    .ForMember(myCustomClass => myCustomClass.DescriptionWithDifferentName,
                        expression => expression.MapFrom(itemLink => itemLink.Description));
                    // to map to a different type
                // more configs can do here
                // e.g. cfg.CreateMap<Item, SomeOtherClass>();
            });

            ItemLink linkClone = Mapper.Map<ItemLink>(links[0]);
            ItemLink[] linkArrayClone = Mapper.Map<ItemLink[]>(item.ItemLinks);
            Item itemClone = Mapper.Map<Item>(item);
            MyCustomClass myCustomClassObject = Mapper.Map<MyCustomClass>(links[0]);
        }

        public class Item
        {
            public ItemLink[] ItemLinks { get; set; }
        }

        public class ItemLink
        {
            public string Description { get; set; }
        }

        public class MyCustomClass
        {
            public string DescriptionWithDifferentName { get; set; }
        }
    }
}

You can also configure Automapper to create missing maps automatically with cfg.CreateMissingTypeMaps = true;

using AutoMapper;

namespace Experiments
{
    class Program
    {
        static void Main(string[] args)
        {
            var links = new ItemLink[]
            {
                    new ItemLink {Description = "desc 1"},
                    new ItemLink {Description = "desc 2"},
            };

            var item = new Item
            {
                ItemLinks = links,
            };

            Mapper.Initialize(cfg =>
            {
                // now AutoMapper will try co create maps on it's own
                cfg.CreateMissingTypeMaps = true; 

                // we can still add custom maps like that
                cfg.CreateMap<ItemLink, MyCustomClass>() 
                    .ForMember(myCustomClass => myCustomClass.DescriptionWithDifferentName,
                        expression => expression.MapFrom(itemLink => itemLink.Description));
            });

            ItemLink linkClone = Mapper.Map<ItemLink>(links[0]);
            ItemLink[] linkArrayClone = Mapper.Map<ItemLink[]>(item.ItemLinks);
            Item itemClone = Mapper.Map<Item>(item);

            // without custom map myCustomClassObject.DescriptionWithDifferentName would be null
            MyCustomClass myCustomClassObject = Mapper.Map<MyCustomClass>(links[0]);
        }

        public class Item
        {
            public ItemLink[] ItemLinks { get; set; }
        }

        public class ItemLink
        {
            public string Description { get; set; }
        }

        public class MyCustomClass
        {
            public string DescriptionWithDifferentName { get; set; }
        }
    }
}

5 Comments

thanks for the answer. what this line says : cfg.CreateMap<ItemLink, ItemLink>(); and where is the source array(db.ItemLinks)?
@AshwiniVerma This line tells Automapper "how to" convert an object of type ItemLink to another object of type ItemLink. In this case, we say just clone it by properties. Automapper by design requires us to provide a configuration even if it's trivial as this one. However, more often than not, we want a more complex transformation (see the updated answer).
@AshwiniVerma I also added a snippet presenting the static approach
Sorry. but didn't make sense to me. can't be simple as it's here : github.com/AutoMapper/AutoMapper.Collection
@AshwiniVerma Your instincts were right :) Honestly, I've never used this approach before, but you made me dig deeper and I found the way to make AutoMapper create missing maps automatically. Take a look at the last snippet.
0

I am not sure that I understood what you need, I guess that you are trying to map from a list of ItemLink to a list of viewModel.ItemLink

so Mapper.Map<viewModel.ItemLink>(db.ItemLinks);

become

var listOfViewModelItemLink = Mapper.Map<List<viewModel.ItemLink>>(db.ItemLinks);

you can call ToArray() on listOfViewModelItemLink then assign to ItemLinks property of Item class

1 Comment

Thanks for the answer but this won't work. I have already tried this.
0

I am not sure but I guess that you are trying to map from an array of ItemLink to an array of viewModel.ItemLink. You should do it like this:

var viewModels = db.ItemLinks
        .ToArray()
        .Select(x=>Mapper.Map<viewModel.ItemLink>(x))
        .ToArray();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.