【问题标题】:Automapper: How to leverage a custom INamingConvention?Automapper:如何利用自定义 INamingConvention?
【发布时间】:2012-03-14 04:03:45
【问题描述】:

我正在使用一个数据库,设计师似乎真的很喜欢大写字母和下划线键。因为我有一个简单的 ORM,所以我的数据模型也使用这些名称。我需要构建 DTO,并且我更愿意为它们提供标准名称,因为我们通过服务公开它们。 以下代码现已更正!测试通过,因此如果您需要使用多种命名约定,请将此作为参考

    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using AutoMapper;
    using NUnit.Framework;

    namespace AutomapperTest
    {
        public class DATAMODEL
        {
            public Guid ID { get; set; }
            public string FIRST_NAME { get; set; }
            public List<CHILD_DATAMODEL> CHILDREN { get; set; }
        }

        public class CHILD_DATAMODEL
        {
            public Guid ID { get; set; }
            public int ORDER_ID { get; set; }
        }

        public class DataModelDto
        {
            public Guid Id { get; set; }
            public string FirstName { get; set; }
            public List<ChildDataModelDto> Children { get; set; }
        }

        public class ChildDataModelDto
        {
            public Guid Id { get; set; }
            public int OrderId { get; set; }
        }

        public class UpperUnderscoreNamingConvention : INamingConvention
        {
            private readonly Regex _splittingExpression = new Regex(@"[\p{Lu}0-9]+(?=_?)");

            public Regex SplittingExpression { get { return _splittingExpression; } }

            public string SeparatorCharacter { get { return "_"; } }
        }

        public class Profile1 : Profile
        {
            protected override void Configure()
            {
                SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
                DestinationMemberNamingConvention = new PascalCaseNamingConvention();
                CreateMap<DATAMODEL, DataModelDto>();
                CreateMap<CHILD_DATAMODEL, ChildDataModelDto>();
            }
        }
        [TestFixture]
        public class Tests
        {
            [Test]
            public void CanMap()
            {
                //tell automapper to use my convention
                Mapper.Initialize(x => x.AddProfile<Profile1>());
                //make a dummy source object
                var src = new DATAMODEL();
                src.ID = Guid.NewGuid();
                src.FIRST_NAME = "foobar";
                src.CHILDREN = new List<CHILD_DATAMODEL>
                               {
                                   new CHILD_DATAMODEL()
                                       {
                                           ID = Guid.NewGuid(),
                                           ORDER_ID = 999
                                       }
                               };
                //map to destination
                var dest = Mapper.Map<DATAMODEL, DataModelDto>(src);
                Assert.AreEqual(src.ID, dest.Id);
                Assert.AreEqual(src.FIRST_NAME, dest.FirstName);
                Assert.AreEqual(src.CHILDREN.Count, dest.Children.Count);
                Assert.AreEqual(src.CHILDREN[0].ID, dest.Children[0].Id);
                Assert.AreEqual(src.CHILDREN[0].ORDER_ID, dest.Children[0].OrderId);
            }
        }
    }

【问题讨论】:

  • 我有非常相似的问题,无法弄清楚。基本上想将数据库生成的代码例如customer_id映射到CustomerId,但它不起作用。请您发布此测试的完整代码好吗?谢谢

标签: automapper automapper-2


【解决方案1】:

在配置文件中创建您的映射,并根据需要定义 INamingConvention 参数。

我不喜欢全局/静态,所以我更喜欢使用 Initialize 并一起定义我的所有映射。这还有一个额外的好处是允许调用 AssertConfiguration... 这意味着如果我的映射失败了,我会在启动时得到异常,而不是每当我的代码开始使用有问题的映射时。

Mapper.Initialize(configuration =>
{
    configuration.CreateProfile("Profile1", CreateProfile1);
    configuration.CreateProfile("Profile2", CreateProfile2);
});
Mapper.AssertConfigurationIsValid();

在与该初始化方法相同的类中:

public void CreateProfile1(IProfileExpression profile)
{
    // this.CreateMap (not Mapper.CreateMap) statements that do the "normal" thing here
    // equivalent to Mapper.CreateMap( ... ).WithProfile("Profile1");
}

public void CreateProfile2(IProfileExpression profile)
{
    profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
    profile.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();

    // this.CreateMap (not Mapper.CreateMap) statements that need your special conventions here
    // equivalent to Mapper.CreateMap( ... ).WithProfile("Profile2");
}

如果你这样做,并且没有在两个配置文件中定义相同的映射,我认为你不需要任何东西来从原始问题中“填补空白”,它应该已经设置为执行正确的事情。

【讨论】:

  • 您可能想要获取 AutoMapper 源并查看它,因为没有很多好的文档可用。特别是在 INamingConvention.cs 中的 2 个 INamingConvention 实现
  • 好的,所以我听到的是您不能在特定地图上设置命名约定,但您可以在单独的配置文件上设置命名约定。所以上面的解决方案是创建两个单独的配置文件。让我试试看。是的,我很久以前就读过源代码,但现在看来我需要编写自己的 INamingConvention。感谢您的提示
  • DotNet Zebra:根据您的建议,我尝试重构和使用命名约定。我失败了。我用一个独立的代码示例更新了上面的问题,你可以运行它来演示这个问题。
  • 代码中有一个非常微妙但非常重要的错误,您在全局静态而不是配置文件配置方法中创建地图(将 Mapper.CreateMap 调用更改为 CreateMap 或 this.CreateMap ,你应该有一个通过测试和想要的结果)
  • 这确实非常微妙而且非常重要!你修好了,非常感谢你
【解决方案2】:

怎么样

public class DATAMODELProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<DATAMODEL, DATAMODEL>();
        Mapper.CreateMap<DATAMODEL, SOMETHINGELSE>();
        Mapper.CreateMap<DATAMODEL, DataModelDto>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.ID))
            .ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FIRST_NAME))
            .ForMember(dest => dest.ChildDataModels, opt => opt.MapFrom(src => src.CHILD_DATAMODELS));
    }
}

【讨论】:

  • 是的,这就是我要走的路线,但它当然会破坏 automapper 的许多优点。我正在寻找可以应用于特定地图粒度的属性命名约定
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 2015-10-20
  • 1970-01-01
  • 2014-03-27
  • 1970-01-01
相关资源
最近更新 更多