C# 将多个源属性映射到单个目标属性

C# 将多个源属性映射到单个目标属性,c#,automapper,C#,Automapper,我想知道是否有一种方法可以用一些自定义类型或值解析器来处理这种情况 public class SuperDateTime { public DateTimeOffset Date { get; set; } public string Timezone { get; set; } } public class Entity { public DateTimeOffset CreationDate { get; set; } public string Cr

我想知道是否有一种方法可以用一些自定义类型或值解析器来处理这种情况

public class SuperDateTime
{
    public DateTimeOffset Date { get; set; }

    public string Timezone { get; set; }
}

public class Entity 
{
    public DateTimeOffset CreationDate { get; set; }

    public string CreationDateZone { get; set; }

    public DateTimeOffset EndDate { get; set; }

    public string EndDateZone { get; set; }
}

public class Model
{
    public SuperDateTime CreationDate { get; set; }

    public SuperDateTime EndDate { get; set; }
}
当目标对象中有一个
SuperDateTime
时,我想用源对象中关联的
DateTimeOffset
和时区
字符串来实例化这个对象

当然,我想做的是做一些通用的东西,所以不要去想每个
实体的
CreateMap
中的
MapFrom

我试着用一个自定义的TypeConverter来实现,但它只支持SourceType->DestinationType
在我的例子中,我有一个
字符串
日期时间偏移量
,它必须创建一个
超级日期时间

,您可以使用客户解析程序进行此操作。我使用自定义解析器从int获取一个对象,类似这样的东西

假设您正在创建这样的映射(尽管您没有显示如何创建它):

Mapper.CreateMap()
.FormMember(x=>x.DateTimeOffset,opt=>opt.ResolveUsing(新的DateTimeOffsetResolver(loadRepository)).FromMember(x=>x.timezone));
以下是您的解析器的外观:

public class DateTimeOffsetResolver : ValueResolver<string, DateTimeOffset>
    {
        private DatabaseLoadRepository loadRepository;
        public personIdResolver(DatabaseLoadRepository repo)
        {
            this.loadRepository = repo;
        }
        protected override DateTimeOffset ResolveCore(string timeZone)
        {
            //Your logic for converting string into dateTimeOffset goes here
            return DateTimeOffset; //return the DateTimeOffset instance
        }
    }
公共类DateTimeOffsetResolver:ValueResolver
{
专用数据库loadRepository loadRepository;
公共人员解算器(DatabaseLoadRepository repo)
{
this.loadRepository=repo;
}
受保护的覆盖DateTimeOffset ResolveCore(字符串时区)
{
//将字符串转换为dateTimeOffset的逻辑如下
return DateTimeOffset;//返回DateTimeOffset实例
}
}
如果不需要访问Nhibernate存储库,您可以删除所有与Nhibernate存储库相关的代码。
您可以进一步了解自定义解析程序

问题的简短答案是“否”,没有办法使用自定义值解析程序映射=>SuperDateTime并避免重复的.MapFrom调用。在上面的示例中,这样的值解析器将无法区分哪些字符串和DateTimeOffset在映射期间一起使用

不确定您是否拥有.MapFrom代码,但如果没有,以下是解决问题的最佳方案:

Mapper.CreateMap<Entity, Model>()
      .ForMember(
           dest => dest.CreationDate,
           opt => opt.MapFrom(
               src => new SuperDateTime()
                     {
                           Date = src.CreationDate, 
                           TimeZone = src.CreationDateZone
                     };
            ));
Mapper.CreateMap()
福门博先生(
dest=>dest.CreationDate,
opt=>opt.MapFrom(
src=>newsuperdatetime()
{
日期=src.CreationDate,
时区=src.CreationDateZone
};
));
如果您真的想避免过多的MapFrom声明,请查看这里是否有利用映射继承的方法


编辑:修改SuperDateTime的实例化以匹配提供的源代码。

除了LiamK的建议之外,下一个可能的改进是编写一个帮助器方法来执行
.MapFrom
。根据您的需求,它可以是简单的,也可以是复杂的。我将提供一个简单的方法,可以做很多假设,但是您可以修改和优化它,以满足您可能的需求

static IMappingExpression<TFrom, TTo> MapSuperDateTime<TFrom, TTo>(
    this IMappingExpression<TFrom, TTo> expression, 
    Expression<Func<TTo, object>> dest)
{
    var datePropertyName = ReflectionHelper.FindProperty(dest).Name;
    var timezomePropertyName = datePropertyName + "Zone";
    var fromType = typeof (TFrom);
    var datePropertyGetter = fromType.GetProperty(datePropertyName).ToMemberGetter();
    var timezonePropertGetter = fromType.GetProperty(timezomePropertyName).ToMemberGetter();

    return expression.ForMember(dest, opt => opt.MapFrom(src => new SuperDateTime
    {
        Date = (DateTimeOffset)datePropertyGetter.GetValue(src),
        Timezone = (string)timezonePropertGetter.GetValue(src)         
    }));
}
静态IMappingExpression MapSuperDateTime(
这个IMappingExpression表达式,
表达式(dest)
{
var datePropertyName=ReflectionHelper.FindProperty(dest.Name);
var timezomePropertyName=datePropertyName+“区域”;
var fromType=typeof(TFrom);
var datePropertyGetter=fromType.GetProperty(datePropertyName.ToMemberGetter();
var timezonePropertGetter=fromType.GetProperty(timezomePropertyName.tomembergeter();
返回表达式.ForMember(dest,opt=>opt.MapFrom(src=>newsuperdatetime
{
Date=(DateTimeOffset)datePropertyGetter.GetValue(src),
时区=(字符串)timezonePropertGetter.GetValue(src)
}));
}
然后您可以像这样指定映射:

Mapper.CreateMap<Entity, Model>()
    .MapSuperDateTime(dest => dest.CreationDate)
    .MapSuperDateTime(dest => dest.EndDate);
Mapper.CreateMap<Entity, Model>()
    .ForMemberType((member,src) => new SuperDateTime
            {
                Date = (DateTimeOffset)GetPropertyValue(src, member),
                Timezone = (string)GetPropertyValue(src, member+"Zone")
            });
public object GetPropertyValue(object o, string memberName)
{
    return o.GetType().GetProperty(memberName).ToMemberGetter().GetValue(o);
}
Mapper.CreateMap()
.MapSuperDateTime(dest=>dest.CreationDate)
.MapSuperDateTime(dest=>dest.EndDate);

假设您的实体
DateTimeOffset
被称为bla,那么您对应的实体
string
被称为blaZone,您的模型
SuperDateTime
被称为bla。

在它上面睡觉后,这里有一个感觉更通用的替代方法

假设,您希望执行以下操作:

Mapper.CreateMap<Entity, Model>()
    .MapSuperDateTime(dest => dest.CreationDate)
    .MapSuperDateTime(dest => dest.EndDate);
Mapper.CreateMap<Entity, Model>()
    .ForMemberType((member,src) => new SuperDateTime
            {
                Date = (DateTimeOffset)GetPropertyValue(src, member),
                Timezone = (string)GetPropertyValue(src, member+"Zone")
            });
public object GetPropertyValue(object o, string memberName)
{
    return o.GetType().GetProperty(memberName).ToMemberGetter().GetValue(o);
}
以及
FormMemberType
方法本身,看起来是这样的:

public static IMappingExpression<TSource, TDestination> ForMemberType<TSource, TDestination, TMember>(
    this IMappingExpression<TSource, TDestination> expression,
    Func<string, TSource, TMember> memberMapping
    )
{
    return new TypeInfo(typeof(TDestination))
        .GetPublicReadAccessors()
        .Where(property => property.GetMemberType() == typeof(TMember))
        .Aggregate(expression, (current, property)
            => current.ForMember(property.Name, 
               opt => opt.MapFrom(src => memberMapping(property.Name, src))));
}

OP的问题实际上是针对如何获取=>SuperDateTime的自定义值解析器而提出的。这个解决方案解决了映射string=>DateTimeOffsetIn的完全不同的问题。除了下面我的回答之外,定义一个用于源类型的结构(例如SuperDateTimeDto)是否有任何价值?然后,您可以定义从结构到目标SuperDateTime的自定义值解析程序。根据OP SuperDateTime,它没有带两个参数的构造函数。你建议加一个吗?(如果你是的话,你的回答还不清楚)我的假设是这篇文章没有包括完整的来源。如果确实没有可行的参数化构造函数,那么使用setter实例化属性就足够容易了。.MapFrom函数接受Func参数。OP可以做任何必要的事情来实例化匿名函数中的对象。实例化SuperDateTime对象的细节似乎与问题无关。+1用于将所有内容整合到OP的帮助器方法。这是我能想到的必须重复映射的最简单方法。@LiamK谢谢,我认为我们可以使它更通用。我又补充了一个答案。