Automapper 没有传输我的 collection 项

Automapper is not transferring my collection items

我正在使用 AutoMapper 4.x。

我有几个类如下:

/// <summary>
///     All service outputs need to descend from this class.
/// </summary>
public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
{
    private readonly OzCpResultErrors _OzCpResultErrors;

    public OzCpAppServiceOutputBase()
    {
        _OzCpResultErrors = new OzCpResultErrors();
    }

    public OzCpResultErrors ResultErrors
    {
        get { return _OzCpResultErrors; }
    }

    public bool ResultSuccess
    {
        get { return _OzCpResultErrors.Messages.Count == 0; }
    }
}

/// <summary>
///     Return from the booking service when a simple booking is made.
/// </summary>
public class OzCpSimpleManualCruiseBookingOutput : OzCpAppServiceOutputBase
{
    public int OzBookingId { get; set; }
    }  
}


public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
{
}

当我调用 AutoMapper 在 OzCpSimpleManualCruiseBookingOutputSimpleManualCruiseBookingOutput 之间进行转换时,我的问题是结果错误已被清除。

public SimpleManualCruiseBookingOutput SimpleManualCruiseBooking(SimpleManualCruiseBookingInput aParams)
{
    OzCpSimpleManualCruiseBookingOutput result = _PlatformBookingService.SimpleManualBooking(Mapper.Map<OzCpSimpleManualCruiseBookingInput>(aParams));

    //**TESTING
    result.ResultErrors.AddFatalError(1, "Oh Dear!!!!");

    //**As soon as I perform the mapping the ResultErrros collection loses the item I have added above
    return Mapper.Map<SimpleManualCruiseBookingOutput>(result);
}

我猜是因为它是只读的 属性,但我不知道如何让它传输 collection。

非常感谢任何帮助。

编辑

我自己也尝试在 collection 中添加项目,因此将我的映射从:

Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>();

使用after map函数如下:

Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>()
                .AfterMap((src, dst) => dst.ResultErrors.Messages.AddRange(src.ResultErrors.Messages));

但这会导致目的地在列表中有 两个 个项目而不是 1 个项目,即:

与 "Oh Dear!!!!" 的条目相同 解决方案

使用 DavidL 建议的私有 setter 方法(以及对 Automapper 4.x 的升级)意味着我得到了所需的行为。所以这就是我最终得到的:

  /// <summary>
    ///     Defines the contract for all output DTO's to platform
    ///     application services.
    /// </summary>
    /// <seealso cref="OzCpAppServiceOutputBase" />
    public interface IOzCpAppServiceOutputBase : IOutputDto
    {
        /// <summary>
        ///     Contains a list of errors should a call to an application service fail.
        /// </summary>
        OzCpResultErrors ResultErrors{ get; }

        /// <summary>
        ///     When TRUE the underlying call to the application service was successful, FALSE
        ///     otherwise. When FALSE see ResultErrors for more information on the error condition.
        /// </summary>
        bool ResultSuccess { get; }
    }

  public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
    {
        public OzCpAppServiceOutputBase()
        {
            ResultErrors = new OzCpResultErrors();
        }

        /// <remarks>The private setter is here so that AutoMapper works.</remarks>
        public OzCpResultErrors ResultErrors { get; private set; }

        public bool ResultSuccess
        {
            get { return ResultErrors.Messages.Count == 0; }
        }
    }

因此,虽然需要添加私有 setter "just for" AutoMapper,但只需付出很小的代价就可以完成这项工作,并且无需使用复杂的映射来处理该问题。

使用当前的继承结构,AutoMapper 将无法完成您希望它完成的工作。由于您的目标结构与源结构具有相同的属性,因此这些属性也是只读的。 AutoMapper 不会映射到没有声明 setter 的只读属性。

您有几个选择:

  • 使 属性 setter 明确私有。 This 答案表明更高版本的 AutoMapper 支持此功能。在这种情况下,它适用于 4.x.
  • 将 属性 setter 设为内部,这样只有该程序集的成员才能设置它。由于最新版本的 AutoMapper 将映射到私有 setters,因此它们也应该映射到内部 setters.
  • 使 属性 可设置。
  • 向下转换对象而不是映射(您已经提到您不想这样做,因为您的对象结构最终会发散)。
  • 用 public setter 在目标对象上遮蔽 属性。丑陋且是奇怪错误的良好来源。

    public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
    {
        public new OzCpResultErrors ResultErrors { get; set; }
    }
    
  • 创建一个帮助程序,通过反射映射您的只读属性。 不要这样做!

    PropertyInfo nameProperty = aParams.GetType().GetProperty ("ResultErrors");
    FieldInfo nameField = nameProperty.GetBackingField ();
    nameField.SetValue (person, aParams.ResultErrors);