1
votes

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[].

5
Are you trying to map a array to a string? Can you please re-frame the question? - Biswabid
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? - granit
@Biswabid: No. Just trying to map db.ItemLink to viewmodel.ItemLink. - Ashwini Verma
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. - Gert Arnold
this is strange, you are trying to map a list of objects to a object. it is difficult to understand what you need - Dan Nguyen

5 Answers

4
votes

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();
2
votes

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.
0
votes

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; }
        }
    }
}
0
votes

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

0
votes

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();