【问题标题】:How to create a reusable mapping profile with Mapster?如何使用 Mapster 创建可重复使用的映射配置文件?
【发布时间】:2021-10-21 12:28:03
【问题描述】:

我有一个 .Net 5 Web Api 项目并想使用

Mapster v7.2.0

避免手动映射对象。以下代码显示了一个示例场景

  • 设置映射配置
  • 来自多个来源的地图
  • 映射到不同名称的字段

.

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    [HttpGet]
    public ActionResult<UsernameWithTodoTitle> Get()
    {
        TypeAdapterConfig<(User, Todo), UsernameWithTodoTitle>
            .NewConfig()
            .Map(dest => dest, src => src.Item1) // map everything from user
            .Map(dest => dest, src => src.Item2) // map everything from todo
            .Map(dest => dest.TodoTitle, src => src.Item2.Title); // map the special fields from todo
        
        var user = new User { Username = "foo", FieldFromUser = "x" };
        var todo = new Todo { Title = "bar", FieldFromTodo = "y" };
        
        var usernameWithTodoTitle = (user, todo).Adapt<(User, Todo), UsernameWithTodoTitle>();
        
        return Ok(usernameWithTodoTitle);
    }
}

public class User
{
    public string Username { get; set; }
    public string FieldFromUser { get; set; }
}

public class Todo
{
    public string Title { get; set; } // !! map this one to the TodoTitle field !!
    public string FieldFromTodo { get; set; }
}

public class UsernameWithTodoTitle
{
    public string Username { get; set; }
    public string TodoTitle { get; set; } // !! this one is special, is has a different name !!
    public string FieldFromUser { get; set; }
    public string FieldFromTodo { get; set; }
}

在运行应用程序时,映射似乎以这种方式工作正常

我必须以这种方式设置配置,其他方式对我不起作用。但是还有3件事需要解决

  • 我觉得配置不对。它映射待办事项中的所有内容并再次映射特殊字段......所以它可能会循环多次?如果有多个名称不同的字段,这可能会变得很昂贵
  • 我在控制器内部创建了配置。如何创建全局注册一次的可重用映射配置文件类?
  • 当有一个映射配置文件时,这条线var usernameWithTodoTitle = (user, todo).Adapt&lt;(User, Todo), UsernameWithTodoTitle&gt;(); 对我来说看起来很混乱。更好的是var usernameWithTodoTitle = UsernameWithTodoTitle.Adapt((user, todo)) /* pass in as a tuple */,因为它会根据参数类型选择正确的映射配置文件

你们对如何创建这样的映射配置文件有任何想法吗?

【问题讨论】:

  • 您可以添加您想要的自动映射器示例吗?
  • @AliZeinali 抱歉,澄清一下 => 我使用的是 Mapster 而不是 Automapper。 Automapper 无法从多个来源进行映射 stackoverflow.com/questions/21413273/…
  • 我添加了一个伪实现...希望对您有所帮助

标签: c# .net-core .net-5 mapster


【解决方案1】:

更新:无法找到使用 Mapster 执行您尝试执行的操作的方法,但这里有一个使用 Automapper 的示例。

using AutoMapper;
using System;

namespace ConsoleApp5
{
    class A { public string FirstName { get; set; } }

    public class B { public string Address1 { get; set; } }

    public class C
    {
        public string FirstName { get; set; }
        public string Address1 { get; set; }
    }

    public class DemoProfile : Profile
    {
        public DemoProfile()
        {
            CreateMap<(A, B), C>()
                .ForMember(dest=> dest.FirstName, opts => opts.MapFrom(src => src.Item1.FirstName))
                .ForMember(dest => dest.Address1, opts => opts.MapFrom(src => src.Item2.Address1));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var config = new MapperConfiguration(cfg => {
                cfg.AddProfile<DemoProfile>();
            });

            var mapper = config.CreateMapper();
            var destination = mapper.Map<C>((new A {  FirstName = "Test" }, new B { Address1 = "Addr" }));

            Console.ReadKey();
        }
    }
}

嘿,直到现在我还没有使用过 Mapster,但这是我收集到的。它对您使用 Tuple&lt;T1,T2&gt; 而不是 (T1,T2) 的元组类型非常具体,但除了那件小事之外,我能够让它运行和映射而没有问题。这是一个小的控制台示例作为示例。

using Mapster;
using System;

namespace ConsoleApp5
{
    class A { public string FirstName { get; set; } }

    public class B { public string Address1 { get; set; } }

    public class C
    {
        public string FirstName { get; set; }
        public string Address1 { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Option 1
            TypeAdapterConfig<Tuple<A, B>, C>.NewConfig()
                .Map(dest => dest.FirstName, src => src.Item1.FirstName)
                .Map(dest => dest.Address1, src => src.Item2.Address1);

            var destObject = new Tuple<A, B>(new A { FirstName = "Test" }, new B { Address1 = "Address 1" })
                .Adapt<Tuple<A, B>, C>();

            // Option 2
            TypeAdapterConfig<(A, B), C>.NewConfig()
                .Map(dest => dest.FirstName, src => src.Item1.FirstName)
                .Map(dest => dest.Address1, src => src.Item2.Address1);

            var destObject2 = (new A { FirstName = "Test" }, new B { Address1 = "Address 1" })
                .Adapt<(A, B), C>();

            Console.ReadKey();
        }
    }
}

【讨论】:

  • 感谢您的帮助。我玩过并更新了我的问题,但不幸的是还有一些问题需要解决......
  • 我在他们的文档中没有找到像 Automapper 这样的配置文件的任何引用,但是您可以创建一个基类或接口并编写一个扩展以在启动时加载它们。
  • 为 Automapper 添加了配置文件和元组的参考,希望对您有所帮助。认为您也许可以使用 Mapster 完成此操作,但可能需要您做更多的工作。
  • 不完全是我要找的,但没关系 :) 我根据您的回答 stackoverflow.com/a/68973616/7764329 发布了给定问题的解决方案
【解决方案2】:

基于@Felipe Ramos 的回答,我无法使用 Mapster 解决它,而是使用 Automapper。为了完整起见,这是我的解决方案。 如果 Mapster 有解决方案,请告诉我!

我安装了软件包

AutoMapper v10.1.1

AutoMapper.Extensions.Microsoft.DependencyInjection v8.1.1

在方法Startup.ConfigureServices里面我添加了services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());这一行

整个代码看起来像

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly IMapper _mapper;
    
    public MyController(IMapper mapper)
    {
        _mapper = mapper;
    }
    
    [HttpGet]
    public ActionResult<UsernameWithTodoTitle> Get()
    {
        var user = new User { Username = "foo", FieldFromUser = "x" };
        var todo = new Todo { Title = "bar", FieldFromTodo = "y" };
        var usernameWithTodoTitle = _mapper.Map<UsernameWithTodoTitle>((user, todo));
    
        return Ok(usernameWithTodoTitle);
    }
}

public class User
{
    public string Username { get; set; }
    public string FieldFromUser { get; set; }
}

public class Todo
{
    public string Title { get; set; } // !! map this one to the TodoTitle field !!
    public string FieldFromTodo { get; set; }
}

public class UsernameWithTodoTitle
{
    public string Username { get; set; }
    public string TodoTitle { get; set; } // !! this one is special, is has a different name !!
    public string FieldFromUser { get; set; }
    public string FieldFromTodo { get; set; }
}

public class UsernameWithTodoTitleMappingProfile : Profile
{
    public UsernameWithTodoTitleMappingProfile()
    {
        CreateMap<(User, Todo), UsernameWithTodoTitle>()
            .ForMember(
                destination => destination.Username,
                memberOptions => memberOptions.MapFrom(source => source.Item1.Username))
            .ForMember(
                destination => destination.TodoTitle,
                memberOptions => memberOptions.MapFrom(source => source.Item2.Title))
            .ForMember(
                destination => destination.FieldFromUser,
                memberOptions => memberOptions.MapFrom(source => source.Item1.FieldFromUser))
            .ForMember(
                destination => destination.FieldFromTodo,
                memberOptions => memberOptions.MapFrom(source => source.Item2.FieldFromTodo));
    }
}

【讨论】:

    【解决方案3】:

    你可以使用下一个:

        var config = new TypeAdapterConfig()
        {
            RequireExplicitMapping = true,
            RequireDestinationMemberSource = true,
            Compiler = exp => exp.CompileFast()
        };
    
        config.Scan("Your assembly");
    
        services.AddSingleton(config);
        services.AddTransient<IMapper, ServiceMapper>();
    
        public class RegisterConfig : IRegister
        {
            public void Register(TypeAdapterConfig config)
            {
                config.NewConfig<TSource, TDestination>();
            }
        }
    

    IServiceCollection 在哪里服务

    【讨论】:

      猜你喜欢
      • 2018-02-04
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 2014-04-07
      • 2019-09-11
      • 1970-01-01
      • 2019-01-21
      • 1970-01-01
      相关资源
      最近更新 更多