webentwicklung-frage-antwort-db.com.de

Verwenden von AutoMapper zum Deaktivieren eines DTO

Ich habe versucht, AutoMapper zu verwenden, um Zeit zu sparen, die von meinen DTOs zu meinen Domänenobjekten führt. Ich habe jedoch Probleme, die Karte so zu konfigurieren, dass sie funktioniert, und ich frage mich, ob AutoMapper das falsche Werkzeug ist die Arbeit.

Betrachten Sie dieses Beispiel von Domänenobjekten (eine Entität und einen Wert):

public class Person
{
    public string Name { get; set; }
    public StreetAddress Address { get; set; }
}

public class StreetAddress
{
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

Mein DTO (von einem Linq-to-SQL-Objekt) sieht ungefähr so ​​aus:

public class PersonDTO
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

Ich möchte dies in meinem Repository tun können:

return Mapper.Map<PersonDTO, Person>(result);

Ich habe versucht, AutoMapper so zu konfigurieren, wie ich es herausfinden kann, aber ich bekomme immer die generische Fehlende Typzuordnungskonfiguration oder nicht unterstützte Zuordnung Fehler, ohne dass mir Details mitgeteilt werden, wo ich versagen kann.

Ich habe verschiedene Konfigurationen ausprobiert, aber hier einige:

Mapper.CreateMap<PersonDTO, Person>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));

und

Mapper.CreateMap<Person, Domain.Person>()
    .ForMember(dest => dest.Address.Address1, opt => opt.MapFrom(src => src.Address))
    .ForMember(dest => dest.Address.City, opt => opt.MapFrom(src => src.City))
    .ForMember(dest => dest.Address.State, opt => opt.MapFrom(src => src.State));

Ich habe gelesen, dass Abflachung Objekte mit AutoMapper einfach sind, aber Unflattening sie ist nicht einfach ... oder sogar möglich. Kann mir jemand sagen, ob ich versuche, das Unmögliche zu tun, und wenn nicht, was ich falsch mache?

Beachten Sie, dass meine eigentlichen Objekte etwas komplizierter sind. Es kann also sein, dass ich die Informationen auslasse, die der Schlüssel zum Fehler sind. Wenn das, was ich tue, richtig aussieht, kann ich weitere Informationen bereitstellen oder meine Objekte zum Testen vereinfachen .

38
Josh Anderson

verwenden Sie https://github.com/omuleanu/ValueInjecter , es erfolgt Abflachung und Unflattening, und alles andere, was Sie brauchen, ist ein asp.net mvc-Beispielanwendung im Download wo alle Funktionen demonstriert werden (auch Unit-Tests)

8
Omu

Das scheint auch für mich zu funktionieren:

Mapper.CreateMap<PersonDto, Address>();
Mapper.CreateMap<PersonDto, Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));

Erstellen Sie grundsätzlich eine Zuordnung vom dto zu beiden Objekten und verwenden Sie sie dann als Quelle für das untergeordnete Objekt.

62
sydneyos

Neben der Antwort von Sydney und gemäß dem Kommentar von Trevor de Koekkoek ist eine Zweiwegemapping auf diese Weise möglich

public class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

public class PersonViewModel
{
    public string Name { get; set; }
    public string AddressStreet { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
}

Automapper-Zuordnungen

Mapper.Initialize(cfg => cfg.RecognizePrefixes("Address"));
Mapper.CreateMap<Person, PersonViewModel>();
Mapper.CreateMap<PersonViewModel, Address>();
Mapper.CreateMap<PersonViewModel, Person>()
    .ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));

Wenn Sie NameOf class implementieren, können Sie die Präfix-Zeichenfolge entfernen

Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof<Person>.Property(x => x.Address)));

BEARBEITEN: In C # 6

Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
9
sanjuro

Kann keinen Kommentar posten, also eine Antwort posten. Ich denke, es gab einige Änderungen in der AutoMapper-Implementierung, so dass die von HansoS vorgeschlagene Antwort https://stackoverflow.com/a/5154321/2164198 nicht mehr kompilierbar ist. Es gibt zwar eine andere Methode, die in solchen Szenarien verwendet werden kann - ResolveUsing:

Mapper.CreateMap<Person, Domain.Person>()
    .ForMember(dest => dest.Address, opt => opt.ResolveUsing( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
9
Ivan Samygin

Dies ist möglicherweise zu spät, aber Sie können das Problem lösen, indem Sie das Objekt mit Lambda-Ausdrücken wie folgt erstellen:

Mapper.CreateMap<Person, Domain.Person>()
        .ForMember(dest => dest.Address, opt => opt.MapFrom( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))
5
HansoS

Ich benutze das

public static void Unflatten<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt)
{
    var prefix = opt.DestinationMember.Name;
    var memberProps = typeof(TMember).GetProperties();
    var props = typeof(TSource).GetProperties().Where(p => p.Name.StartsWith(prefix))
        .Select(sourceProp => new
        {
            SourceProp = sourceProp,
            MemberProp = memberProps.FirstOrDefault(memberProp => prefix + memberProp.Name == sourceProp.Name)
        })
        .Where(x => x.MemberProp != null);
    var parameter = Expression.Parameter(typeof(TSource));

    var bindings = props.Select(prop => Expression.Bind(prop.MemberProp, Expression.Property(parameter, prop.SourceProp)));
    var resolver = Expression.Lambda<Func<TSource, TMember>>(
        Expression.MemberInit(Expression.New(typeof(TMember)), bindings),
        parameter);

    opt.ResolveUsing(resolver.Compile());
}

Aufbau

new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Person, PersonDTO>();
    cfg.CreateMap<PersonDTO, Person>().ForMember(x => x.HomeAddress, opt => opt.Unflatten());
});

Modelle

public class Person
{
    public string Name { get; set; }
    public Address HomeAddress { get; set; }
}

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string ZipCode { get; set; }
}

Befolgen der AutoMapper-Reduzierungskonventionen

public class PersonDTO
{
    public string Name { get; set; }
    public string HomeAddressLine1 { get; set; }
    public string HomeAddressLine2 { get; set; }
    public string HomeAddressCity { get; set; }
    public string HomeAddressState { get; set; }
    public string HomeAddressZipCode { get; set; }
}

Benötigt wahrscheinlich viele Verbesserungen, aber es funktioniert ...

1
andres.chort

Ich habe eine andere Lösung. Die Hauptidee ist, dass AutoMapper know wie verschachtelte Objekte abgeflacht werden, wenn Sie Eigenschaften in einem abgeflachten Objekt richtig benennen: Hinzufügen des geschachtelten Objekteigenschaftennamens als Präfix. Für Ihren Fall ist Adresse das Präfix:

public class PersonDTO
{
    public string Name { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
    ...
}

Wenn Sie ein vertrautes Mapping erstellen, das verschachtelt ist und anschließend mit und dann mit der ReverseMap-Methode erstellt wird, kann AutomMapper verstehen, wie das geschachtelte Objekt deaktiviert wird.

CreateMap<Person, PersonDTO>()
   .ReverseMap();

Das ist alles!

0
Andrei