# AutoMapper Madness - Nuances in Self-Mapping

> AutoMapper lets you map a type to itself without creating a map, but it returns the same instance instead of a copy. Here's how to force a real copy and test it.

- URL: https://blog.nimblepros.com/blogs/automapper-madness-part-3/
- Author: [Scott DePouw](https://blog.nimblepros.com/authors/scott/)
- Published: 2022-09-13
- Updated: 2026-11-06
- Series: [Automapper Madness](https://blog.nimblepros.com/series/automapper-madness/)
- Categories: [Software Development](https://blog.nimblepros.com/categories/software-development/)
- Publisher: [NimblePros](https://blog.nimblepros.com/)

---

*This is the third part of my blog post series on AutoMapper. [First part here!](https://blog.nimblepros.com/blogs/automapper-madness-part-1) [Second part here!](https://blog.nimblepros.com/blogs/automapper-madness-part-2/)*

## AutoMapper Allows Self-Mappings Without Map Creation
Typically in AutoMapper, if you don't set up a mapping via `CreateMap<Foo, Bar>()` in your Profile class, AutoMapper will throw an exception when you try to call `_mapper.Map<Bar>(myFoo)`, complaining that you didn't create a mapping between those two types. However, let's say you wanted to get a new instance of the *same* type. AutoMapper allows this without any mappings created! You can call `Foo newFoo = _mapper.Map<Foo>(oldFoo)` and AutoMapper will happily return a shiny new `Foo` object for you.

But is it really a new instance?

### Nope!

You're actually getting the *same* instance back, as AutoMapper is copying the references over to your new object!

Let's say we have the following classes:

```csharp
namespace MyApp.Core;

public class ExampleClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ExampleSubClass Sub { get; set; }
}

public class ExampleSubClass
{
    public int Id { get; set; }
    public string Name { get; set; }
}
```

Without configuring AutoMapper, the following unit test would pass:

```csharp
using AutoMapper;
using MyApp.Core;
using MyApp.Core.AutoMapper;
using Xunit;

namespace MyApp.UnitTests.AutoMapper;

public class MapExampleClassToItselfShould
{
    private readonly IMapper _mapper = AutoMapperConfig.Initialize();
    
    [Fact]
    public void CopyByReferenceForUnmappedSelfMapping()
    {
        ExampleClass foo = new ExampleClass
        {
            Id = 7,
            Name = "Foo",
            Sub = new ExampleSubClass { Id = 17, Name = "SubFoo" }
        };

        ExampleClass bar = _mapper.Map<ExampleClass>(foo);
        
        // The class itself, as well as all its reference properties, are the same referene.
        Assert.Same(foo, bar);
        Assert.Same(foo.Name, bar.Name);
        Assert.Same(foo.Sub, bar.Sub);

        // Changing value type properties in bar reflect back to foo.
        bar.Id = 5;
        bar.Sub.Id = 15;

        Assert.Equal(5, foo.Id);
        Assert.Equal(15, foo.Sub.Id);
    }
}
```

XUnit's `Assert.Same()` will fail if the two objects given are two different instances. (Its counterpart, `Assert.NotSame()`, does the opposite.) As you can see, both `foo` and `bar` in this example are the same instance. This applies recursively for all mapped properties (strings, `ExampleSubClass Sub`, etc.), as all reference types AutoMapper creates are just referencing the original object. How lazy of AutoMapper!

### Unexpected Behavior Can Lead to Bugs

Since AutoMapper usually won't do un-mapped mapping calls, this behavior can lead to subtle bugs in your code. You may expect AutoMapper to fail for *any* undeclared mappings. You may assume that you're getting a new instance and thus can make changes without impacting the original object. (Consider having an EntityFramework object from your database, and subtly making changes to the original, and calling `SaveChanges()`: If you're lucky, you'll get an error (can't insert object with duplicate Id, re-adding already-tracked entity, etc.) If not so lucky, you could end up just updating the existing record instead of making a new one!)

There are likely other scenarios where this behavior can cause bugs. So, why does AutoMapper break apparent convention here and allow it? Simple: The creator himself Jimmy Bogard calls AutoMapper ["A lousy solution for cloning"](https://github.com/AutoMapper/AutoMapper/issues/340#issuecomment-18343567). Why? Because it's the ["most obvious and performant behavior"](https://github.com/AutoMapper/AutoMapper/issues/787#issuecomment-121945356) is to simply assign same-type objects to each other. As such...

#### Aside: Need Deep Copies? Don't Use AutoMapper!

This is out of scope for this post, but AutoMapper is not optimal for deep copying. A pattern of serializing/deserializing your object is the most effective way to perform a deep copy. [BinaryFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-6.0) was a go-to for a while, but [it is obsolete](https://github.com/dotnet/designs/blob/main/accepted/2020/better-obsoletion/binaryformatter-obsoletion.md). You can use JSON (de)serialization to accomplish a similar goal. Whatever you attempt, isolate your methodology behind an interface (e.g. `IDeepCopier`) to make your code's intentions clear, and make sure all those unit tests against your `DeepCopier` implementation pass!

## Force AutoMapper to Make a Copy

If you don't have a super complex object and want to use AutoMapper anyway, you can make it behave the way you expect it to! There's no trickery involved here: Just call CreateMap() referencing the same type for both the source and destination:

`CreateMap<ExampleClass, ExampleClass>();`

For any sub-types that you also want to fully map, those need declared as well:

`CreateMap<ExampleSubClass, ExampleSubClass>();`

If we do that within our AutoMapper Profile class, then the unit test needs changes in order to keep passing!

```csharp
[Fact]
public void SelfMappingWithExplicitSelfMap()
{
    ExampleClass foo = new ExampleClass
    {
        Id = 7,
        Name = "Foo",
        Sub = new ExampleSubClass { Id = 17, Name = "SubFoo" }
    };

    ExampleClass bar = _mapper.Map<ExampleClass>(foo);

    // These references are no longer the same instance.
    Assert.NotSame(foo, bar);
    Assert.NotSame(foo.Sub, bar.Sub);

    // Strings are still the same reference since it's an unmapped reference type.
    Assert.Same(foo.Name, bar.Name);

    // Changing value/explicitly-mapped properties no longer reflects back to foo!
    bar.Id = 5;
    bar.Sub.Id = 15;
    Assert.Equal(7, foo.Id);
    Assert.Equal(17, foo.Sub.Id);
}
```

I've had success with this simple solution, since in my scenarios deep copying wasn't the goal. I just needed a new instance of a flat object. Of course, I wouldn't want to provide some implementation without unit tests proving that AutoMapper's giving me a new instance! How'd I go about doing that?

## Another Base Class for AutoMapper Unit Testing

They're starting to pile up now, but this is another example of abstracting common testing behavior for AutoMapper into a base class. We want to define the type we're self-mapping, and assure that when making calls to AutoMapper we're getting a fresh instance and *not* the same one back. We'll lean on `Assert.NotSame()`, and otherwise copy the general idea from the base class we made in [my previous post](https://blog.nimblepros.com/blogs/automapper-madness-part-2/).

```csharp
using AutoMapper;
using MyApp.Core.AutoMapper;
using Xunit;

namespace MyApp.UnitTests.AutoMapper;

public abstract class AutoMapperSelfMappingTestBase<TSelfMappingType>
    : AutoMapperTestBase<TSelfMappingType, TSelfMappingType>
    where TSelfMappingType : new()
{
    [Fact]
    public void CreateNewInstance()
    {
        TSelfMappingType originalInstance = new();

        TSelfMappingType newInstance = _mapper.Map<TSelfMappingType>(originalInstance);

        Assert.NotSame(originalInstance, newInstance);
    }
}

// Has two unit tests out of the box:
// - MapExampleClassToItselfShould.MapWithoutThrowing
// - MapExampleClassToItselfShould.CreateNewInstance
public class MapExampleClassToItselfShould : AutoMapperSelfMappingTestBase<ExampleClass>
{ }
```

`MapExampleClassToItselfShould` is now configured to run the same `MapWithoutThrowing` test we defined in `AutoMapperTestBase` previously. Inserting another base class in between, `AutoMapperSelfMappingTestBase`, unlocks another test: Verify that a call to AutoMapper will give us a new instance. Prior to `CreateMap<ExampleClass, ExampleClass>()` being added to our Profile, `MapWithoutThrowing` will pass, but `CreateNewInstance` will fail. After adding the explicit mapping, both tests pass!

But what about `ExampleSubClass Sub { get; set; }`? In this particular scenario, we want to verify this sub class is being mapped as well. I don't need to extend `MapExampleClassToItselfShould` with another test, however: As it's another mapping I want to self-map, it would get its own class. I would then repeat this process for any other properties I cared about. (Reminder: If you want a true deep copy and would otherwise have to make a bunch of self-mappings: Don't use AutoMapper!)

```csharp
public class MapExampleSubClassToItselfShould : AutoMapperSelfMappingTestBase<ExampleSubClass>
{ }
```

In the scenarios where I care about self-mappings creating a new instance, I write these test classes ASAP, so that my test suite instantly fails until I make the code do what I want it to do. Since AutoMapper has this little quirk of not failing on a `Map()` call for self-mappings (unlike non-self-mappings), this gives me a form of automation to not let potential bugs seep in!

## Wrap-Up
That'll do it for my series of AutoMapper blog posts! In this third and final part, we explored a potential gotcha when using AutoMapper for self-mappings, its impacts, and how to make it behave in a more expected fashion. We learned why AutoMapper does this, why you *shouldn't use AutoMapper* for deep copy scenarios, and how to unit test our code to assure we're getting new instances when expected.
