SoFunction
Updated on 2025-03-01

Summary of how to use C# AutoMapper

This article is based on AutoMapper 9.0.0

AutoMapper is an object-object mapper that maps one object to another.

Official website address:/

Official Documentation:/en/latest/

1 Introduction Example

public class Foo
{
 public int ID { get; set; }

 public string Name { get; set; }
}

public class FooDto
{
 public int ID { get; set; }

 public string Name { get; set; }
}

public void Map()
{
 var config = new MapperConfiguration(cfg => <Foo, FooDto>());

 var mapper = ();

 Foo foo = new Foo { ID = 1, Name = "Tom" };

 FooDto dto = <FooDto>(foo);
}

2 Register

In use MapBefore the method, you must first tell AutoMapper what class can be mapped to which class.

var config = new MapperConfiguration(cfg => <Foo, FooDto>());

Each AppDomain can only be configured once. This means that the best place to place configuration code is in application startup, such as the application's file.

Starting from 9.0 The method is not available.

2.1 Profile

Profile is another way to organize mapping. Create a new class, inherit Profile, and configure the mapping in the constructor.

public class EmployeeProfile : Profile
{
 public EmployeeProfile()
 {
  CreateMap<Employee, EmployeeDto>();
 }
}

var config = new MapperConfiguration(cfg =>
{
 <EmployeeProfile>();
});

Profile Internal configuration only applies toProfile Internal mapping. The configuration applied to the root configuration applies to all created maps.

AutoMapper can also scan from the specified assembly ProfileInherited class and add it to the configuration.

var config = new MapperConfiguration(cfg =&gt;
{
 // Scan the current assembly (());
 
 // You can also pass the assembly name (dll name) ("LibCoreTest");
});

3 Configuration

3.1 Naming Convention

By default, AutoMapper is based on the same field name map and is case-insensitive.

But sometimes, we need to deal with some special situations.

  • SourceMemberNamingConventionRepresents source type naming rules
  • DestinationMemberNamingConvention Represents the target type naming rules

LowerUnderscoreNamingConvention andPascalCaseNamingConvention are two naming rules provided by AutoMapper. The former is named in lowercase and contains underscores, while the latter is the Pascal naming rules (the initial letter of each word is capitalized).

My understanding is that if the source type and target type use serpentine nomenclature and camel nomenclature respectively, then you need to specify naming rules so that they can map correctly.

public class Foo
{
 public int Id { get; set; }

 public string MyName { get; set; }
}

public class FooDto
{
 public int ID { get; set; }

 public string My_Name { get; set; }
}

public void Map()
{
 var config = new MapperConfiguration(cfg =>
 {
  <Foo, FooDto>();

   = new PascalCaseNamingConvention();
   = new LowerUnderscoreNamingConvention();
 });

 var mapper = ();

 Foo foo = new Foo { Id = 2, MyName = "Tom" };

 FooDto dto = <FooDto>(foo);
}

3.2 Configuring Visibility

By default, AutoMapper only maps public Member, but it can actually be mapped to private attributes.

var config = new MapperConfiguration(cfg =>
{
  = p =>  || ;
 <Source, Destination>();
});

It should be noted that the attribute must be added hereprivate set, omitted set That's not possible.

3.3 Global attribute/field filtering

By default, AutoMapper tries to map each public property/field. The following configuration ignores field mapping.

var config = new MapperConfiguration(cfg =>
{
	 = fi => false;
});

3.4 Identify prefixes and suffixes

var config = new MapperConfiguration(cfg =>
{
 ("My");
 ("My");
}

3.5 Replace characters

var config = new MapperConfiguration(cfg =>
{
 ("Ä", "A");
});

We basically don't use this function.

4 Call the constructor

Some classes, attributesset The method is private.

public class Commodity
{
 public string Name { get; set; }

 public int Price { get; set; }
}

public class CommodityDto
{
 public string Name { get; }

 public int Price { get; }

 public CommodityDto(string name, int price)
 {
  Name = name;
  Price = price * 2;
 }
}

AutoMapper will automatically find the corresponding constructor call. If some changes are made to the parameters in the constructor, the changes will be reflected in the mapping result. As shown in the above example, after mapping Price Will multiply 2.

Disable constructor mapping:

public class Commodity
{
 public string Name { get; set; }

 public int Price { get; set; }
}

public class CommodityDto
{
 public string Name { get; }

 public int Price { get; }

 public CommodityDto(string name, int price)
 {
  Name = name;
  Price = price * 2;
 }
}

AutoMapper will automatically find the corresponding constructor call. If some changes are made to the parameters in the constructor, the changes will be reflected in the mapping result. As in the above example, Price will be multiplied by 2 after mapping.

Disable constructor mapping:

var config = new MapperConfiguration(cfg => ());

If constructor mapping is disabled, the target class must have a parameterless constructor.

5 Array and List Mapping

The mapping of arrays and lists is relatively simple. You only need to configure the element type and define the simple type as follows:

public class Source
{
 public int Value { get; set; }
}

public class Destination
{
 public int Value { get; set; }
}

Mapping:

var config = new MapperConfiguration(cfg =>
{
 <Source, Destination>();
});
IMapper mapper = ();

var sources = new[]
{
 new Source { Value = 5 },
 new Source { Value = 6 },
 new Source { Value = 7 }
};

IEnumerable<Destination> ienumerableDest = <Source[], IEnumerable<Destination>>(sources);
ICollection<Destination> icollectionDest = <Source[], ICollection<Destination>>(sources);
IList<Destination> ilistDest = <Source[], IList<Destination>>(sources);
List<Destination> listDest = <Source[], List<Destination>>(sources);
Destination[] arrayDest = <Source[], Destination[]>(sources);

Specifically, supported source collection types include:

  • IEnumerable
  • IEnumerable
  • ICollection
  • ICollection
  • IList
  • IList
  • List
  • Arrays

When mapped to an existing collection, the target collection is first cleared. If this is not what you want, check it out.

5.1 Processing empty collections

When mapping collection attributes, if the source value isnull, then AutoMapper maps the target field to an empty collection, instead ofnull. This is consistent with the behavior of Entity Framework and Framework Design Guidelines, which argues that C# references, arrays, List, Collection, Dictionary, and IEnumerables should never be null

5.2 Polymorphism in a collection

This official documentation is not easy to understand. Let me give you another example. The entity class is as follows:

public class Employee
{
 public int ID { get; set; }

 public string Name { get; set; }
}

public class Employee2 : Employee
{
 public string DeptName { get; set; }
}

public class EmployeeDto
{
 public int ID { get; set; }

 public string Name { get; set; }
}

public class EmployeeDto2 : EmployeeDto
{
 public string DeptName { get; set; }
}

The array mapping code is as follows:

var config = new MapperConfiguration(cfg =>
{
 <Employee, EmployeeDto>().Include<Employee2, EmployeeDto2>();
 <Employee2, EmployeeDto2>();
});
IMapper mapper = ();

var employees = new[]
{
 new Employee { ID = 1, Name = "Tom" },
 new Employee2 { ID = 2, Name = "Jerry", DeptName = "R & D" }
};

var dto = <Employee[], EmployeeDto[]>(employees);

It can be seen that after mapping, the types of two elements in dto are EmployeeDto and EmployeeDto2, which implements the mapping of the parent class to the parent class and the mapping of the child class to the child class.

If the Include method is removed, the types of both elements in the mapped dto are EmployeeDto.

6 Method to attribute mapping

AutoMapper can not only implement attribute-to-property mapping, but also implement method-to-property mapping, and does not require any configuration. The method name can be the same as the attribute name, or it can also be accompanied byGet Prefix.

For example, the following example() Methods that can be mapped to property.

public class Employee
{
 public int ID { get; set; }

 public string FirstName { get; set; }

 public string LastName { get; set; }

 public string GetFullName()
 {
  return $"{FirstName} {LastName}";
 }
}

public class EmployeeDto
{
 public int ID { get; set; }

 public string FirstName { get; set; }

 public string LastName { get; set; }

 public string FullName { get; set; }
}

7 Custom Mapping

Custom mapping can be used when the source type does not match the target type name, or when some conversions are required to make the source data.

public class Employee
{
 public int ID { get; set; }

 public string Name { get; set; }

 public DateTime JoinTime { get; set; }
}

public class EmployeeDto
{
 public int EmployeeID { get; set; }

 public string EmployeeName { get; set; }

 public int JoinYear { get; set; }
}

As in the above example,IDandEmployeeIDDifferent attribute names,JoinTimeand JoinYearNot only are the attribute names different, but the attribute types are also different.

var config = new MapperConfiguration(cfg =>
{
 <Employee, EmployeeDto>()
  .ForMember("EmployeeID", opt => (src => ))
  .ForMember(dest => , opt => (src => ))
  .ForMember(dest => , opt => (src => ));
});

8 Flat Mapping

One of the common uses of object-object mapping is to flatten complex object models into simpler models.

public class Employee
{
 public int ID { get; set; }

 public string Name { get; set; }

 public Department Department { get; set; }
}

public class Department
{
 public int ID { get; set; }

 public string Name { get; set; }
}

public class EmployeeDto
{
 public int ID { get; set; }

 public string Name { get; set; }

 public int DepartmentID { get; set; }

 public string DepartmentName { get; set; }
}

If the attributes on the target type do not correspond to the attributes and methods of the source type, AutoMapper will disassemble the target member name into a single word according to the camel method and then match it. For example, in the above example, it corresponds to .

8.1 IncludeMembers

If the attribute naming does not comply with the above rules, it is like this:

public class Employee
{
 public int ID { get; set; }

 public string Name { get; set; }

 public Department Department { get; set; }
}

public class Department
{
 public int DepartmentID { get; set; }

 public string DepartmentName { get; set; }
}

public class EmployeeDto
{
 public int ID { get; set; }

 public string Name { get; set; }

 public int DepartmentID { get; set; }

 public string DepartmentName { get; set; }
}

Department The attribute name in the class is directly followed by EmployeeDto If the attribute names in the class are the same, you can use it IncludeMembers Method Specifies.

9 Nested Mapping

Sometimes, we may not need to flatten. See the following example:

public class Employee
{
 public int ID { get; set; }

 public string Name { get; set; }

 public int Age { get; set; }

 public Department Department { get; set; }
}

public class Department
{
 public int ID { get; set; }

 public string Name { get; set; }

 public string Heads { get; set; }
}

public class EmployeeDto
{
 public int ID { get; set; }

 public string Name { get; set; }

 public DepartmentDto Department { get; set; }
}

public class DepartmentDto
{
 public int ID { get; set; }

 public string Name { get; set; }
}

We want toEmployeeMap to EmployeeDto, and will Department Map to DepartmentDto

var config = new MapperConfiguration(cfg =>
{
 <Employee, EmployeeDto>();
 <Department, DepartmentDto>();
});

The above is the detailed content of the usage method of C# AutoMapper. For more information on the usage of C# AutoMapper, please pay attention to my other related articles!