如何使用AutoMapper创建复杂的映射?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用AutoMapper创建复杂的映射?相关的知识,希望对你有一定的参考价值。

我有以下实体模型

public class Blog 
{
    public int Id { get; set;}
    public string Title { get; set; }
    public string Body { get; set; }

    [ForeignKey("Category")]
    public int? CategoryId { get; set; }
    public virtual Category Category { get; set; }
    public virtual ICollection<Comment> Comments { get; set; }
}

public class Category
{
    public int Id { get; set;}

    public string Name { get; set; }
}

public class Comment
{
    public int Id { get; set;}

    public string Title { get; set; }
    public string Body { get; set; }
    [ForeignKey("Blog")]
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}

然后我有以下视图模型,其中我想告诉AutoMapper将Blog对象映射到BlogViewModel注意CategoryName属性需要来自Blog.Category.Name并且Comment中的每个Blog.Comments需要使用有机约定转换为CommentViewModel

我目前在运行时使用反射为任何实现ICustomMap接口的类设置映射。请通过Transfer(IMapper mapper)方法阅读代码中的注释。

public class BlogViewModel : ICustomMapFrom 
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public string MyCatName { get; set; }
    public IEnumerable<CommentViewModel> Comments { get; set; }

    // **IMPORTANT NOTE**
    // This method is called using reflection when the on Application_Start() method.
    // If IMapper is the wrong Interface to pass, I can change
    // the implementation of ICustomMap
    // I assumed that `IMapper` is what is needed to add configuration at runtime.
    public void Transfer(IConfigurationProvider config)
    {
        // How to I do the custom mapping for my MyCatName and Comments?
        // I need to use the config to create the complex mapping
        // AutoMapper.Mapper.Map(typeof(Blog), typeof(BlogViewModel));
    }
}

最后这是我的CommentViewModel

public class CommentViewModel : IMapFrom<Comment>
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
}

如何告诉AutoMapper如何映射CategoryName和Comments?

更新

这是我将如何创建映射。我会有以下3个接口

public interface IMap
{
}

public interface IMapFrom<T> : IMap
{
}

public interface ICustomMapFrom : IMap
{
    void Map(IConfigurationProvider config);
}

然后在Global.cs文件中

我会在启动时执行Run方法。基本上这个方法将扫描程序集并注册我想要使用接口注册的类。

public class ConfigureAutoMapper 
{
    public void Run()
    {
        var types = AssemblyHelpers.GetInternalAssemblies()
                                   .SelectMany(x => x.GetTypes())
                                   .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && typeof(IMap).IsAssignableFrom(x))
                                   .ToList();


        RegisterStandardMappings(types);
        RegisterCustomMappings(types);
    }

    private static void RegisterStandardMappings(IEnumerable<Type> types)
    {
        foreach (Type type in types)
        {
            if(type.IsGenericType && typeof(IMapFrom<>).IsAssignableFrom(type))
            {
                AutoMapper.Mapper.Map(type.GetGenericArguments()[0], type);
            }
        }
    }

    private static void RegisterCustomMappings(IEnumerable<Type> types)
    {
        foreach (Type type in types)
        {
            if (typeof(ICustomMapFrom).IsAssignableFrom(type))
            {
                ICustomMapFrom map = (ICustomMapFrom)Activator.CreateInstance(type);
                var t = AutoMapper.Mapper.Configuration;

                map.Map(Mapper.Configuration);
            }
        }
    }
}
答案

我写了一个NUnit测试,用你的类设置AutoMapper。 AutoMapper支持开箱即用的CategoryName映射。

[TestFixture]
public class TestClass
{
    [Test]
    public void Test1()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Blog, BlogViewModel>();
        });

        config.AssertConfigurationIsValid();

        var blog = new Blog()
        {
            Body = "Blog body",
            Category = new Category { Name = "My Category" },
            Comments = new List<Comment>() {
                new Comment { Body = "Comment body 1" },
                new Comment { Body = "Comment body 2" }
            }
        };

        var mapper = config.CreateMapper();
        var result = mapper.Map<Blog, BlogViewModel>(blog);

        Assert.AreEqual(blog.Body, "Blog body");
        Assert.AreEqual(blog.Category.Name, result.CategoryName);
        List<CommentViewModel> comments = result.Comments.ToList();
        Assert.That(comments.Any(c => c.Body == "Comment body 1"));
        Assert.That(comments.Any(c => c.Body == "Comment body 2"));
    }
}

以上是关于如何使用AutoMapper创建复杂的映射?的主要内容,如果未能解决你的问题,请参考以下文章

对象到对象映射-AutoMapper

使用链接地图/传递映射/链式地图的 AutoMapper 地图

如何调试到 AutoMapper 代码?

如何使用Automapper将更改映射到现有集合?

[使用Automapper时,我是否也应该展平/映射视图模型的内部objetc?

如何使用 AutoMapper 将父引用分配给子属性