简体   繁体   English

使用AutoMapper映射类数组

[英]Mapping class array using AutoMapper

I'm new to AutoMapper and trying to map Array class ItemLink[] . 我是AutoMapper的新手,并尝试映射ArrayItemLink[]

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." “映射器未初始化。使用适当的配置调用Initialize。如果您试图通过容器或其他方式使用Mapper实例,请确保您没有对静态Mapper.Map方法的任何调用,并且如果您使用的是ProjectTo或UseAsDataSource扩展方法,请确保您传入适当的IConfigurationProvider实例。”

Can't it be simple mapping? 难道不是简单的映射?

EDIT 1 编辑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[] . 因此,我想将ViewModel.ItemLink[]Db.ItemLink[]映射。

You cannot provide variable to a generic parameter like you do in Mapper.Map<viewModel.ItemLink>(db.ItemLinks); 您不能像在Mapper.Map<viewModel.ItemLink>(db.ItemLinks);那样为通用参数提供变量Mapper.Map<viewModel.ItemLink>(db.ItemLinks); . It is called Type parameter and must be a type. 它称为Type参数,并且必须是类型。

As @gisek mentioned in his answer you need to configure mapper first. 正如@gisek在他的回答中提到的,您需要首先配置映射器。 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. 您可以考虑从db获取完整的对象,但是您也可以选择使用Queryable Extensions,那里的可查询扩展仅用于获取视图模型所需的数据。

The configuration. 配置。 I assume that you have namespace DB for entity in database, and View namespace for view model. 我假设您具有DB中实体的名称空间DB ,以及视图模型的View命名空间。

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

I assumed you are using .net mvc 我以为你正在使用.net mvc

Firstly you need Initialize your mapping on Application Start, there is no need to initialize every time in your controller etc. 首先,您需要在Application Start上初始化映射,而无需每次都在控制器中进行初始化等。

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." “映射器未初始化。使用适当的配置调用Initialize。如果您试图通过容器或其他方式使用Mapper实例,请确保您没有对静态Mapper.Map方法的任何调用,并且如果您使用的是ProjectTo或UseAsDataSource扩展方法,请确保您传入适当的IConfigurationProvider实例。”

For Solution: 解决方案:

Create an AutoMapperConfig object in your App_Start folder. 在您的App_Start文件夹中创建一个AutoMapperConfig对象。

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 在您的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.

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; 您还可以配置Automapper来使用cfg.CreateMissingTypeMaps = true;自动创建丢失的地图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; }
        }
    }
}

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 我不确定我是否了解您的需求,我想您正在尝试从ItemLink列表映射到viewModel.ItemLink列表

so Mapper.Map<viewModel.ItemLink>(db.ItemLinks); 所以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 您可以在listOfViewModelItemLink上调用ToArray(),然后将其分配给Item类的ItemLinks属性

I am not sure but I guess that you are trying to map from an array of ItemLink to an array of viewModel.ItemLink. 我不确定,但是我想您正在尝试从ItemLink数组映射到viewModel.ItemLink数组。 You should do it like this: 您应该这样做:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM