So richten Sie Automapper in ASP.NET Core ein

255

Ich bin relativ neu bei .NET und habe mich für .NET Core entschieden, anstatt die "alten Methoden" zu lernen. Ich habe hier einen ausführlichen Artikel zum Einrichten von AutoMapper für .NET Core gefunden. Gibt es jedoch eine einfachere Anleitung für Neulinge ?

theutz
quelle
5
Siehe dotnetcoretutorials.com/2017/09/23/…
Michael Freidgeim
Für neuere Versionen von Core (> v1) lesen Sie @ Saineshwars Antwort stackoverflow.com/a/53455699/833878
Robbie
1
Eine vollständige Antwort mit einem Beispiel klicken Sie auf diesen Link
Iman Bahrampour

Antworten:

554

Ich habe es herausgefunden! Hier sind die Details:

  1. Fügen Sie Ihrer Lösung das Haupt-AutoMapper-Paket über NuGet hinzu .
  2. Fügen Sie Ihrer Lösung das AutoMapper Dependency Injection Package über NuGet hinzu .

  3. Erstellen Sie eine neue Klasse für ein Zuordnungsprofil. (Ich habe eine Klasse im Hauptlösungsverzeichnis namens erstellt MappingProfile.csund den folgenden Code hinzugefügt.) Ich verwende ein Userund UserDto-Objekt als Beispiel.

    public class MappingProfile : Profile {
        public MappingProfile() {
            // Add as many of these lines as you need to map your objects
            CreateMap<User, UserDto>();
            CreateMap<UserDto, User>();
        }
    }
  4. Fügen Sie dann die AutoMapperConfiguration Startup.cswie folgt hinzu :

    public void ConfigureServices(IServiceCollection services) {
        // .... Ignore code before this
    
       // Auto Mapper Configurations
        var mappingConfig = new MapperConfiguration(mc =>
        {
            mc.AddProfile(new MappingProfile());
        });
    
        IMapper mapper = mappingConfig.CreateMapper();
        services.AddSingleton(mapper);
    
        services.AddMvc();
    
    }
  5. Gehen Sie wie folgt vor, um das zugeordnete Objekt im Code aufzurufen:

    public class UserController : Controller {
    
        // Create a field to store the mapper object
        private readonly IMapper _mapper;
    
        // Assign the object in the constructor for dependency injection
        public UserController(IMapper mapper) {
            _mapper = mapper;
        }
    
        public async Task<IActionResult> Edit(string id) {
    
            // Instantiate source object
            // (Get it from the database or whatever your code calls for)
            var user = await _context.Users
                .SingleOrDefaultAsync(u => u.Id == id);
    
            // Instantiate the mapped data transfer object
            // using the mapper you stored in the private field.
            // The type of the source object is the first type argument
            // and the type of the destination is the second.
            // Pass the source object you just instantiated above
            // as the argument to the _mapper.Map<>() method.
            var model = _mapper.Map<UserDto>(user);
    
            // .... Do whatever you want after that!
        }
    }

Ich hoffe, dies hilft jemandem, neu mit ASP.NET Core zu beginnen! Ich freue mich über Feedback oder Kritik, da ich noch neu in der .NET-Welt bin!

theutz
quelle
3
Der ausführliche Artikel verlinkt, lostechies.com/jimmybogard/2016/07/20/… , erklärt, wie ProfileKlassen angeordnet sind
Kieren Johnstone
22
@theutz Sie können diese beiden CreateMap-Zeilen mit einer .ReverseMap () am Ende von entweder zusammenführen. Vielleicht kommentieren, aber ich finde es intuitiver.
Astravagrant
6
In Schritt 3 kann es hilfreich sein, das Hinzufügen eines "using AutoMapper" zu erwähnen. oben, damit die Erweiterungsmethode importiert wird.
Rocklan
8
Dies funktionierte gut mit .net Core 1.1, nicht mehr, nachdem ich auf .net Core 2.0 aktualisiert hatte. Ich denke, ich muss die Assembly der Logikprofilklasse explizit angeben. Ich recherchiere immer noch, wie ich das erreichen kann. Update: Ah, die Antwort liegt in Ihrem Kommentar. Ich muss die Art der Klasse übergeben, die mein Profil ist. // services.AddAutoMapper (typeof (Startup)); // <- neuere Automapper-Version verwendet diese Signatur
Esen
3
In AutoMapper v8 und dem Add-On Dependency Injection v5 werden nur die services.AddAutoMapper () benötigt. Zeile in der ConfigureServices-Methode der Startup-Klasse. Für mich war es sogar möglich, Profilklassen in abhängigen Klassenbibliotheksprojekten zu finden.
Stricq
68

Schritt So verwenden Sie AutoMapper mit ASP.NET Core.

Schritt 1. Installieren von AutoMapper.Extensions.Microsoft.DependencyInjection aus dem NuGet-Paket.

Geben Sie hier die Bildbeschreibung ein

Schritt 2. Erstellen Sie einen Ordner in Lösung, um Zuordnungen mit dem Namen "Zuordnungen" beizubehalten.

Geben Sie hier die Bildbeschreibung ein

Schritt 3. Nach dem Hinzufügen des Mapping-Ordners haben wir eine Klasse mit dem Namen " MappingProfile " hinzugefügt. Dieser Name kann alles Einzigartige und Gute verstehen.

In dieser Klasse werden wir alle Zuordnungen pflegen.

Geben Sie hier die Bildbeschreibung ein

Schritt 4. Initialisierung von Mapper beim Start "ConfigureServices"

In der Startklasse müssen wir das von uns erstellte Profil initialisieren und den AutoMapper-Dienst registrieren.

  Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());

  services.AddAutoMapper();

Code-Snippet, um die ConfigureServices-Methode anzuzeigen, bei der AutoMapper initialisiert und registriert werden muss.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });


        // Start Registering and Initializing AutoMapper

        Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
        services.AddAutoMapper();

        // End Registering and Initializing AutoMapper

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }}

Schritt 5. Ausgabe abrufen.

Um das zugeordnete Ergebnis zu erhalten, müssen wir AutoMapper.Mapper.Map aufrufen und das richtige Ziel und die richtige Quelle übergeben.

AutoMapper.Mapper.Map<Destination>(source);

Code-Auszug

    [HttpPost]
    public void Post([FromBody] SchemeMasterViewModel schemeMaster)
    {
        if (ModelState.IsValid)
        {
            var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
        }
    }
Saineshwar
quelle
13
Ich erhalte folgende Fehlermeldung : 'Mapper' does not contain a definition for 'initialize'. Ich benutze AutoMapper.Extensions.Microsoft.DependencyInjectionVersion 7.0.0
Kimbaudi
Super detaillierte Antwort. Danke mein Herr.
Rod Hartzell
1
Wenn Sie ASP.NET CORE 3.0 verwenden, lesen Sie dieses Tutorial. So richten Sie AutoMapper in ASP.NET Core 3.0 ein: tutexchange.com/how-to-set-up-automapper-in-asp-net-core-3-0
Saineshwar
44

Ich möchte die Antworten von @ theutz erweitern - nämlich diese Zeile:

// services.AddAutoMapper(typeof(Startup));  // <-- newer automapper version uses this signature.

Es gibt ( wahrscheinlich ) einen Fehler in AutoMapper.Extensions.Microsoft.DependencyInjection Version 3.2.0. (Ich verwende .NET Core 2.0)

Dies wird in dieser GitHub-Ausgabe behandelt. Wenn Ihre Klassen, die die Profilklasse von AutoMapper erben, außerhalb der Assembly existieren, in der sich Ihre Startup-Klasse befindet, werden sie wahrscheinlich nicht registriert, wenn Ihre AutoMapper-Injektion folgendermaßen aussieht:

services.AddAutoMapper();

es sei denn, Sie geben explizit an, nach welchen Assemblys AutoMapper-Profile durchsucht werden sollen.

Dies kann in Ihrem Startup.ConfigureServices folgendermaßen erfolgen:

services.AddAutoMapper(<assembies> or <type_in_assemblies>);

Dabei verweisen "Assemblys" und "type_in_assemblies" auf die Assembly, in der Profilklassen in Ihrer Anwendung angegeben sind. Z.B:

services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));

Ich nehme an (und ich betone dieses Wort), dass aufgrund der folgenden Implementierung einer parameterlosen Überladung (Quellcode von GitHub ):

public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
     return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}

Wir verlassen uns darauf, dass CLR bereits eine JIT-Assembly hat, die AutoMapper-Profile enthält, die möglicherweise zutreffen oder nicht, da sie nur bei Bedarf angepasst werden (weitere Details in dieser StackOverflow-Frage).

GrayCat
quelle
5
Das ist die richtige Antwort für die neueste Version von AutoMapper und AspNetCore
Joshit
1
Dies war die Antwort, nach der ich für AutoMapper 8.1 (neueste Version)
gesucht habe
30

Die Antwort von theutz hier ist sehr gut, ich möchte nur Folgendes hinzufügen:

Wenn Sie Ihr Mapping-Profil von MapperConfigurationExpressionanstatt erben lassen Profile, können Sie ganz einfach einen Test hinzufügen, um Ihr Mapping-Setup zu überprüfen. Dies ist immer praktisch:

[Fact]
public void MappingProfile_VerifyMappings()
{
    var mappingProfile = new MappingProfile();

    var config = new MapperConfiguration(mappingProfile);
    var mapper = new Mapper(config);

    (mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}
Arve Systad
quelle
Ich erhalte eine Fehlermeldung: "Die Injektion der AutoMapper-Erweiterungsabhängigkeit ist nicht mit asp.net Core 1.1 kompatibel." Bitte helfen Sie!
Rohit Arora
Es scheint, dass die Definition von "verifizieren" zur Debatte steht. Dies wird ausgelöst, wenn bestimmte Eigenschaften vom Design ausgeschlossen werden, um eine Zuordnung zu verhindern.
Jeremy Holovacs
2
Wenn Sie nicht möchten, dass eine Eigenschaft zugeordnet wird, richten Sie sie mit .Ignore () ein. Auf diese Weise müssen Sie aktiv über die Bearbeitung jedes Einzelfalls nachdenken und sicherstellen, dass Sie bei Änderungen nichts verpassen. Eigentlich super praktisch. Ja, der Verifikationstest ist ein größeres Sicherheitsnetz, als viele Menschen glauben. Es ist nicht kinderleicht, aber es kümmert sich um die ersten 90%.
Arve Systad
18

Ich habe es auf diese Weise gelöst (ähnlich wie oben, aber ich denke, es ist eine sauberere Lösung) für .NET Core 2.2 / Automapper 8.1.1 mit Extensions.DI 6.1.1.

Erstellen Sie die MappingProfile.cs-Klasse und füllen Sie den Konstruktor mit Maps (ich plane, eine einzelne Klasse zu verwenden, um alle meine Zuordnungen zu speichern).

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Source, Dest>().ReverseMap();
        }
    }

Fügen Sie in Startup.cs unten hinzu, um DI hinzuzufügen (das Assembly-Argument bezieht sich auf die Klasse, die Ihre Mapping-Konfigurationen enthält, in meinem Fall auf die MappingProfile-Klasse).

//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));

Verwenden Sie es in Controller wie jedes andere DI-Objekt

    [Route("api/[controller]")]
    [ApiController]
    public class AnyController : ControllerBase
    {
        private readonly IMapper _mapper;

        public AnyController(IMapper mapper)
        {
            _mapper = mapper;
        }

        public IActionResult Get(int id)
        {
            var entity = repository.Get(id);
            var dto = _mapper.Map<Dest>(entity);

            return Ok(dto);
        }
    }

Coy Meeks
quelle
2
Ich mag deine Antwort. Ich denke, das Umwickeln MappingProfilesmit, new Type[]{}wie in dieser Antwort gezeigt , ist unnötig.
Too Fat Man No Neck
10

In meiner Startup.cs (Core 2.2, Automapper 8.1.1)

services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });            

In meinem Datenzugriffsprojekt

namespace DAL
{
    public class MapperProfile : Profile
    {
        // place holder for AddAutoMapper (to bring in the DAL assembly)
    }
}

In meiner Modelldefinition

namespace DAL.Models
{
    public class PositionProfile : Profile
    {
        public PositionProfile()
        {
            CreateMap<Position, PositionDto_v1>();
        }
    }

    public class Position
    {
        ...
    }
Brian Rice
quelle
Warum benutzt du nicht einfach services.AddAutoMapper( typeof(DAL.MapperProfile) ); statt services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });?
Too Fat Man No Neck
8

Ich mag viele Antworten, besonders die von @saineshwar. Ich verwende .net Core 3.0 mit AutoMapper 9.0, daher ist es an der Zeit, die Antwort zu aktualisieren.

Was für mich funktioniert hat, war in Startup.ConfigureServices (...), das den Dienst folgendermaßen registriert:

    services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(), 
                               AppDomain.CurrentDomain.GetAssemblies());

Ich denke, dass der Rest der @ Saineshwar-Antwort perfekt bleibt. Aber wenn jemand interessiert ist, ist mein Controller-Code:

[HttpGet("{id}")]
public async Task<ActionResult> GetIic(int id)
{
    // _context is a DB provider
    var Iic = await _context.Find(id).ConfigureAwait(false);

    if (Iic == null)
    {
        return NotFound();
    }

    var map = _mapper.Map<IicVM>(Iic);

    return Ok(map);
}

Und meine Mapping-Klasse:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Iic, IicVM>()
            .ForMember(dest => dest.DepartmentName, o => o.MapFrom(src => src.Department.Name))
            .ForMember(dest => dest.PortfolioTypeName, o => o.MapFrom(src => src.PortfolioType.Name));
            //.ReverseMap();
    }
}

----- BEARBEITEN -----

Nachdem ich die Dokumente gelesen habe, die in den Kommentaren von Lucian Bargaoanu verlinkt sind, denke ich, dass es besser ist, diese Antwort ein wenig zu ändern.

Die parameterlos services.AddAutoMapper() (mit der Antwort @saineshwar) funktioniert nicht mehr (zumindest für mich). Wenn Sie jedoch die NuGet-Assembly AutoMapper.Extensions.Microsoft.DependencyInjection verwenden, kann das Framework alle Klassen überprüfen, die AutoMapper.Profile erweitern (wie meine, MappingProfile).

In meinem Fall, in dem die Klasse zu derselben ausführenden Assembly gehört, kann die Dienstregistrierung auf verkürzt werden services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Ein eleganterer Ansatz könnte eine parameterlose Erweiterung mit dieser Codierung sein).

Danke, Lucian!

Vic
quelle
6

Ich verwende AutoMapper 6.1.1 und asp.net Core 1.1.2.

Definieren Sie zunächst Profilklassen, die von der Profilklasse von Automapper geerbt wurden. Ich habe eine leere IProfile-Schnittstelle erstellt. Der Zweck besteht nur darin, die Klassen dieses Typs zu finden.

 public class UserProfile : Profile, IProfile
    {
        public UserProfile()
        {
            CreateMap<User, UserModel>();
            CreateMap<UserModel, User>();
        }
    }

Erstellen Sie nun eine separate Klasse, z. B. Zuordnungen

 public class Mappings
    {
     public static void RegisterMappings()
     {            
       var all =
       Assembly
          .GetEntryAssembly()
          .GetReferencedAssemblies()
          .Select(Assembly.Load)
          .SelectMany(x => x.DefinedTypes)
          .Where(type => typeof(IProfile).GetTypeInfo().IsAssignableFrom(type.AsType()));

            foreach (var ti in all)
            {
                var t = ti.AsType();
                if (t.Equals(typeof(IProfile)))
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfiles(t); // Initialise each Profile classe
                    });
                }
            }         
        }

    }

Rufen Sie jetzt im MVC Core-Webprojekt in der Datei Startup.cs im Konstruktor die Mapping-Klasse auf, die alle Zuordnungen zum Zeitpunkt des Ladens der Anwendung initialisiert.

Mappings.RegisterMappings();
Aamir
quelle
Sie können einfach eine Unterklasse aus der Profilklasse erstellen und wenn das Programm services.AddAutoMapper () ausführt; Codezeile Der Automapper kennt sie automatisch.
Isaeid
Ich denke nicht, dass dies notwendig ist, wenn Sie AutoMapper.Extensions.Microsoft.DependancyInjection verwenden, das in Nuget verfügbar ist.
Greg Gum
5

Wenn Sie für ASP.NET Core (getestet mit 2.0+ und 3.0) lieber die Quelldokumentation lesen möchten: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

Andernfalls funktionieren die folgenden 4 Schritte:

  1. Installieren Sie AutoMapper.Extensions.Microsoft.DependancyInjection von Nuget.

  2. Fügen Sie einfach einige Profilklassen hinzu.

  3. Fügen Sie dann unten Ihre Startup.cs-Klasse hinzu. services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. Dann injizieren Sie einfach IMapper in Ihre Controller oder wo immer Sie ihn brauchen:

public class EmployeesController {

    private readonly IMapper _mapper;

    public EmployeesController(IMapper mapper){

        _mapper = mapper;
    }

Und wenn Sie ProjectTo jetzt verwenden möchten, ist es einfach:

var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()
Dalcam
quelle
4

Für AutoMapper 9.0.0:

public static IEnumerable<Type> GetAutoMapperProfilesFromAllAssemblies()
    {
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var aType in assembly.GetTypes())
            {
                if (aType.IsClass && !aType.IsAbstract && aType.IsSubclassOf(typeof(Profile)))
                    yield return aType;
            }
        }
    }

MapperProfile:

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    CreateMap<Foo, FooDto>();
    // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
  }
}

In Ihrem Startup:

services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
            .ToArray());

In Controller oder Service: Inject Mapper:

private readonly IMapper _mapper;

Verwendung:

var obj = _mapper.Map<TDest>(sourceObject);
Nicolae Lupei
quelle
4

In den neuesten Versionen von asp.net core sollten Sie die folgende Initialisierung verwenden:

services.AddAutoMapper(typeof(YourMappingProfileClass));
Martcs
quelle
2

Asp.Net Core 2.2 mit AutoMapper.Extensions.Microsoft.DependencyInjection.

public class MappingProfile : Profile
{
  public MappingProfile()
  {
      CreateMap<Domain, DomainDto>();
  }
}

In Startup.cs

services.AddAutoMapper(typeof(List.Handler));
Sras
quelle
1

Hinzufügen zu dem, was Arve Systad zum Testen erwähnt hat. Wenn Sie aus irgendeinem Grund wie ich sind und die in der Zutz-Lösung bereitgestellte Vererbungsstruktur beibehalten möchten, können Sie die MapperConfiguration folgendermaßen einrichten:

var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);

Ich habe das in NUnit gemacht.

Landhaie
quelle
1

services.AddAutoMapper (); hat bei mir nicht funktioniert. (Ich verwende Asp.Net Core 2.0)

Nach der Konfiguration wie unten

   var config = new AutoMapper.MapperConfiguration(cfg =>
   {                 
       cfg.CreateMap<ClientCustomer, Models.Customer>();
   });

Mapper initialisieren IMapper mapper = config.CreateMapper ();

und fügen Sie das Mapper-Objekt als Singleton-Dienste zu Diensten hinzu.AddSingleton (Mapper);

Auf diese Weise kann ich dem Controller einen DI hinzufügen

  private IMapper autoMapper = null;

  public VerifyController(IMapper mapper)
  {              
   autoMapper = mapper;  
  }

und ich habe wie folgt in meinen Aktionsmethoden verwendet

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);
Venkat pv
quelle
Hallo @venkat, Sie mussten wahrscheinlich nur das Paket AutoMapper.Extensions.Microsoft.DependancyInjection zu Ihrem Projekt
hinzufügen
-1

Über die Antwort von theutz muss der IMapper-Mapper- Parrameter nicht im Controller-Konstruktor angegeben werden.

Sie können den Mapper verwenden, da er an jeder Stelle des Codes ein statisches Element ist.

public class UserController : Controller {
   public someMethod()
   {
      Mapper.Map<User, UserDto>(user);
   }
}
Yaronmil
quelle
11
Aber Statik ist ein bisschen anti-testbar, oder?
Scott Fraley
3
Ja. Dies funktioniert in vielen Fällen, aber wenn Sie beim Aufrufen dieser Methode in einem Test keine konfigurierte Zuordnung haben, wird eine Ausnahme ausgelöst (und der Test wird daher aus dem falschen Grund nicht bestanden). Mit einem injizierten können IMapperSie das verspotten und zum Beispiel einfach null zurückgeben, wenn es für den gegebenen Test irrelevant ist.
Arve Systad