Solving AutoMapper Errors When Mapping Fields That Don't Exist in the Target Class

When working with AutoMapper, you may encounter errors when trying to map properties from a source object to a target object, especially if the source contains properties that don't exist in the target. This is a common pitfall, but it’s easy to resolve once you understand the root cause.

The Issue:

Consider the scenario where we have a PostNoteRequest class containing two new fields: PublishDateTime and TimezoneId. These fields are necessary for calculating the CreatedAt property of the target Note class, but the Note class doesn’t have PublishDateTime or TimezoneId at all.

Here’s a typical AutoMapper mapping that results in an error:

CreateMap<PostNoteRequest, Note>()
    .ForMember(m => m.CreatedAt, _ => _.MapFrom((src,dst) =>
    {
        // Logic to calculate CreatedAt based on PublishDateTime and TimezoneId
    }));

Even though we're calculating CreatedAt based on the source properties, AutoMapper will try to map PublishDateTime and TimezoneId directly from the source to the target, resulting in an error like this:

Error mapping types.
Mapping types:
PostNoteRequest -> Note
HappyNotes.Models.PostNoteRequest -> HappyNotes.Entities.Note

The issue arises because AutoMapper expects all properties mentioned in the MapFrom expression to exist in both the source and target objects.

The Cause:

AutoMapper doesn't know how to handle fields (PublishDateTime and TimezoneId) that don’t exist in the target class (Note). The moment we reference these fields directly in the delegate passed to MapFrom, AutoMapper assumes they need to be mapped.

The Solution:

To solve this, we have two main approaches:

1. Use a Custom Value Resolver

A custom value resolver allows us to decouple the logic of calculating CreatedAt from the mapping process. It ensures that we don't reference non-existent fields in the target object.

public class CreatedAtResolver : IValueResolver<PostNoteRequest, Note, long>
{
    public long Resolve(PostNoteRequest source, Note destination, long member, ResolutionContext context)
    {
        if (!string.IsNullOrWhiteSpace(source.PublishDateTime) && !string.IsNullOrWhiteSpace(source.TimezoneId))
        {
            return DateTime.UtcNow.ToUnixTimeSeconds();
        }
        
        var dateStr = source.PublishDateTime;
        if (dateStr.Length == 10) dateStr += " 20:00:00";

        DateTime date = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
        TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(source.TimezoneId);

        return TimeZoneInfo.ConvertTime(new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Unspecified), timeZone).ToUnixTimeSeconds();
    }
}

// In your mapping configuration
CreateMap<PostNoteRequest, Note>()
    .ForMember(m => m.CreatedAt, opt => opt.MapFrom<CreatedAtResolver>());

This approach encapsulates the logic for CreatedAt calculation in a resolver, which avoids directly referencing PublishDateTime or TimezoneId in the target object.

2. Use ConstructUsing for Manual Construction

Another way to handle this is by manually constructing the target object in the ConstructUsing method, ensuring CreatedAt is set correctly without relying on AutoMapper’s default property mapping behavior.

CreateMap<PostNoteRequest, Note>()
    .ForMember(m => m.CreatedAt, opt => opt.MapFrom((src, dst) =>
    {
        var dateStr = src.PublishDateTime;
        if (dateStr.Length == 10) dateStr += " 20:00:00";
        
        DateTime date = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
        TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(src.TimezoneId);
        
        return TimeZoneInfo.ConvertTime(new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Unspecified), timeZone).ToUnixTimeSeconds();
    }))
    .AfterMap((src, dst) =>
    {
        dst.Tags = string.Join(" ", dst.TagList);
        dst.Content = dst.IsLong ? src.Content.GetShort() : src.Content;
    });

This method allows you to explicitly handle the CreatedAt logic while avoiding AutoMapper’s attempt to map non-existent properties from the source object.

Conclusion:

AutoMapper is a powerful tool, but it expects the properties referenced in its mapping expressions to exist in both the source and target objects. When you need to perform custom logic (like calculating CreatedAt based on other properties), using custom value resolvers or manual construction can help you avoid errors and maintain clear and maintainable code.

By understanding how AutoMapper expects the mapping to work, you can prevent issues related to missing properties and ensure your mappings are both efficient and correct.

书摘: 人间相亲故事

2024-06-14 18:05:45 毕业后的恋爱里,女友有次幽怨地说,她期待的是回家时有一盏为她亮着的灯。于是,他毅然放弃了深圳的稳定工作来到广州,从零开始打拼。但爱情的甜蜜期很快过去,女友开始天天计算着工资和房价,

2024-06-13 15:37:39 他曾经奋不顾身的浪漫变成了鲁莽、冲动和毫无计划…… 注: 怎么说呢,这话又对又不对。这话有理,但谁说都不违和,可从那个人嘴里说出来就好没理。

2024-06-13 15:39:37 他原本“希望与对方有话题可以聊,价值观至少要匹配”,但一年下来却发现,“很难找到能好好说话的人”。

2024-06-13 20:09:38 小林是他的初恋女友,也是他的情感启蒙老师。29岁的陆昱辰,像新生儿颤巍巍地迈出了第一步——跨进了一个没有爸妈、只有他和小林的平行空间。他们一起度过了将近一年的时光,逛遍了彼此都很熟悉的北京、看遍了新上线的电影,还去丽江等地旅行。他们也吵架,又和好。他们谈论朋友,讨论工作,唯独不谈论未来。

2024-06-14 08:35:31 我远远地看着,看东子和一个刚刚认识两天的姑娘结婚。我远远地看着,看东子和一个刚刚认识两天的姑娘结婚。

2024-06-14 08:43:00 我远远地看着。看东子和一个刚刚认识两天的姑娘,并排站在装饰得极浪漫温馨的礼台中央,表白、宣誓、拥抱、互换婚戒,相视而立,亲友哄笑着把两个人的脸颊贴在一起,他们也各自捆绑住了彼此始料未及的今生。

书摘:毛澤東私人醫生回憶錄

2024-01-14 17:00:37
帝王權勢讓帝王享有最大的奢侈──生活簡單。毛大部分的時間要不在床上,要不在私人游泳池旁休憩。

2024-01-14 17:05:31
毛告訴李醫生,美國對中國的企圖一向具有正面意義。

2024-01-14 17:11:36
毛發展的理想失敗了。但在這個他所毀滅的國家中,他握有絕對權力。

2024-01-14 17:17:13
此書給我們另一種教訓。它描述過度膨脹的權力,如何驅策其擁有者進入一種黑暗的深淵:在深淵中,偉大的夢想只能導致滔天罪行。

2024-01-14 18:20:00
雪泥鴻爪

2024-01-14 18:21:33
一九八九年三月,我點檢行篋,取出了舊記和帶來的全部資料,開始了寫作生活。這一方面是對嫻的永久的紀念。另一方面,身在美國,就可以將這些年的所見所聞,秉筆直書,無需避諱,加以發表。如果讀過這本書以後,讀者能夠更加珍惜自己的理想和所嚮往的幸福的生活,那將是我和嫻多年來的最大願望。

2024-01-14 18:25:33
對蔣介石,雖然終生為敵,但並不持完全否定的態度。他認為蔣有強烈的民族自尊心,不俯首貼耳聽命於美國。

2024-01-14 18:29:26
寮國領袖凱山的合照

…more

书摘:抱歉,我动了你的脑子:一位神经外科医生的悲喜

2024-08-01 07:28:37
神经外科的手术就像是一次独自的徒手攀岩,这是一个人的旅程,当你作为主刀医生开始手术的那一刻起,就注定要独自完成,因为没有任何的退路。手术是这样,人生又何尝不是如此呢?

2024-08-01 07:40:31
我想不起在医学院读过的关于“类风湿性关节炎”的文章,但我记得心神烦忧、手指扭曲、再也无法捏面团做饼干给孙子吃的老祖母

2024-08-01 07:41:09
失败,往往比成功更具有教育意义

2024-08-01 07:41:36
一位病人的死亡比五十次的救援成功更能塑造医生的心理,促进他成长。

2024-08-01 08:30:58
第一条规则:当你的脑壳被打开,跟空气接触过之后,你就再也不是从前的你了。

2024-08-01 08:18:34
设计师只管机器的表现,从没考虑过维修容不容易、方不方便。”

2024-08-01 08:30:41
第二条规则:只有别人操刀的手术,才叫小手术。如果负责动手术的人是你,这就是大手术。永远不要忘记这一点。”

2024-08-01 08:31:19
第三条规则同样适用于要动脑部或椎间盘手术的病人:只要病人还活着,你永远有办法把他弄得更惨。

…more

书摘:永远的费曼:走近费曼丛书合集(套装共8册)

2024-08-25 14:40:22
我终于把它修好了,因为我有韧劲儿,现在仍然有。一旦我遇到个难题,我是不会善罢甘休的。

2024-08-25 14:40:55
我不可能半途而废。我必须最终找到它究竟出了什么毛病。

2024-08-25 14:41:05
那是一种解决难题的冲动。

2024-08-25 15:03:00
达·芬奇的一生

2024-08-27 19:16:21
军队时不时地派个中尉来检查事儿搞得怎么样。我们老板说,我们这是个民营单位,那个中尉比我们任何人地位都高。“什么事儿,也别告诉中尉,”他说,“一旦他明白我们在干什么,就会发号施令,把事情搞乱。”

2024-08-27 20:30:37
因此,我的小伙子们出息了,只需要告诉他们那是什么,就成了。结果,尽管他们以前花费了9个月解决了3个问题,现在我们用3个月解决了9个问题,几乎快了10倍。

2024-08-27 20:34:09
我没有高兴,但也没觉得特别难过,这或许是因为,7年来,我已经知道这事儿是免不了的了。

2024-08-27 20:37:17
冯·诺伊曼给了我一个很有意思的想法:你不必为你身在其中的这个世界负责。由于冯·诺伊曼的这个建议,我形成了对社会强烈的不负责任感。这使我从此以后成了一个很快乐的人。是冯·诺伊曼播下了这颗种子,这种子在我的思想里长成了活跃的不负责任感!

…more