Automapper - Map objects with IEnumerable<anotherType> - c#

SOLVED: I had a property defined incorrectly!
I'm getting this error...
Missing type map configuration or unsupported mapping.
Mapping types:
HouseDomain -> RoomDomain
{namespace}.HouseDomain -> {namespace}.RoomDomain
Destination path:
City.Houses.Houses.Houses0[0]
So for example, I have
public class CityDomain
{
public IEnumerable<HouseDomain> DomainHouses {get;set;}
}
public class HouseDomain
{
public IEnumerable<RoomDomain> DomainRooms {get;set;}
}
public class RoomDomain
{
//whatever
}
and
public class CityDto
{
public IEnumerable<HouseDto> DtoHouses {get;set;}
}
public class HouseDto
{
public IEnumerable<RoomDto> DtoRooms {get;set;}
}
public class RoomDto
{
//whatever
}
So I want to map CityDomain to CityDto. I have...
Mapper.CreateMap<CityDomain , CityDto>();
Is there an issue going 2 levels deep like this? Any help? Thanks!

This is all mappings you need (Automapper is smart enough to map lists of objects if mapping for appropriate object types was created):
Mapper.CreateMap<RoomDomain, RoomDto>();
Mapper.CreateMap<HouseDomain, HouseDto>()
.ForMember(d => d.DtoRooms, m => m.MapFrom(s => s.DomainRooms));
Just remove your second mapping for member DtoRooms. E.g. if house has id and room has name, then for sample domain house mapping works just fine:
HouseDomain domainHouse = new HouseDomain
{
Id = 42,
DomainRooms = new List<RoomDomain>
{
new RoomDomain { Name = "foo" },
new RoomDomain { Name = "bar" }
}
};
var dtoHouse = Mapper.Map<HouseDto>(domainHouse);
Produces:
{
Id: 42,
DtoRooms: [ { Name: "foo" }, { Name: "bar" } ]
}
Last note - make sure you create maps before you are doing mapping. Usually all maps are created on application startup.

You can keep your mappings simplier if you make names of collections the same (e.g. Rooms) for both HouseDomain and HouseDto types.
Mappings:
Mapper.CreateMap<RoomDomain, RoomDto>();
Mapper.CreateMap<HouseDomain, HouseDto>();
Types:
public class HouseDomain
{
public IEnumerable<RoomDomain> Rooms {get;set;}
}
public class HouseDto
{
public IEnumerable<RoomDto> Rooms {get;set;}
}
I answered the similar question AutoMapping nested models a while ago.

Related

Order property of derived class first in Swagger schema

I use, among others, Json.NET and NSwag and generate a swagger document. I have classes, Animal and Dog : Animal (inherits from Animal).
public class Animal
{
//properties
}
public class Dog : Animal
{
[JsonProperty(Order = -2)]
public string Name { get; set; }
}
I have property in Dog, which is Name. I want to sort Name before all of Dog's properties in the generated Swagger request schema.
Current result:
{
propertyOne: value1, //property of Animal
propertyTwo: value2, //property of Animal
Name: value3 //property of Dog
}
Desired result:
{
Name: value3, //property of Dog is sorted before everything else
propertyOne: value1,
propertyTwo: value2
}
I have done similar sorting but in Swashbuckle, might be similar functionality available in Nswag
Used SchemaFilter to sort properties.
services.AddSwaggerGen(swaggerGenOptions =>
{
.....
swaggerGenOptions.SchemaFilter<SwaggerSchemaFilter>();
}
In schemaFilter, I have used order logic to show all the basic datatype properties in their current oreder and properties which are custom classes in the end. But you can order by name or any other logic.
internal class SwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
schema.Properties = schema.Properties
.OrderBy(x => string.IsNullOrWhiteSpace(x.Value.Type) ? "z" : "a")
.ToDictionary(p2 => p2.Key, p2 => p2.Value);
}
}
For custom classes the type was null,
Also, you can get type from (SchemaFilterContext) context and use reflection to get more details.

Copy List<A> to List<B> when both types share some or all properties [duplicate]

Say I have one class that looks like this:
public class Person
{
public string Name {get; set;}
public int Number {get; set;}
}
And another that looks like this:
public class Dog
{
public string Name {get; set;}
public int Number {get; set;}
}
They are two different classes, but they happen to have the exact same elements (a string called Name and an int called Number)
Is there an easy way in C# to, say, if I had an instance of Person to then create an instance of Dog with the same Name and Number?
For example if I had:
Person person = new Person();
person.Name = "George";
person.Number = 1;
I know I can't simply go:
Dog dog = person;
Because they are two different types. But is there a way in C# to check "oh, if they have the same element, set the same elements of Dog to equal that of Person.
But I feel there has to be an easier way than doing something like:
dog.Name = person.Name;
dog.Number = person.Number;
Especially if the class has a LOT of elements. Also if anyone is wondering, these two different classes are in two different pieces of the API, so I can't simply make them related either.
You can use AutoMapper:
public Dog UsingAMR(Person prs)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, Dog>();
});
IMapper mapper = config.CreateMapper();
return mapper.Map<Person, Dog>(prs);
}
Then you can easily:
Person ps = new Person {Name = "John", Number = 25};
Dog dog = UsingAMR(ps);
Just don't forget to install AutoMapper first from the package manager console as mentioned in the reference:
From Tools menu click on NuGet Package Manager ==> Package Manager Console
Then type the following command:
PM> Install-Package AutoMapper
An object oriented approach.
public class Mammal
{
public Mammal(Mammal toCopy)
{
Name = toCopy.Name;
Number = toCopy.Number;
}
public string Name {get; set;}
public int Number {get; set;}
}
public class Person: Mammal
{
public Person(Mammal toCopy) {} /* will default to base constructor */
}
public class Dog: Mammal
{
public Dog(Mammal toCopy) {} /* will default to base constructor */
}
This will allow the following:
Person person = new Person();
person.Name = "George";
person.Number = 1;
Dog dog = new Dog(person);
Install AutoMapper package in your project.
As a best practice (for web applications) you can create new class (should derives from Profile) in your App_Start folder, that will contain all your mappings for your project.
namespace MyApp.App_Start
{
public class MyAppMapping : Profile
{
public MyAppMapping()
{
CreateMap<Person, Dog>();
//You can also create a reverse mapping
CreateMap<Dog, Person>();
/*You can also map claculated value for your destination.
Example: you want to append "d-" before the value that will be
mapped to Name property of the dog*/
CreateMap<Person, Dog>()
.ForMember(d => d.Days,
conf => conf.ResolveUsing(AppendDogName));
}
private static object AppendDogName(Person person)
{
return "d-" + person.Name;
}
}
}
Then Initialize your mapping inside the Application_Start method in Global.asax
protected void Application_Start()
{
Mapper.Initialize(m => m.AddProfile<MyAppMapping>());
}
You can now use the mappings that you have created
var dog = AutoMapper.Mapper.Map<Person, Dog>(person);
If you don't work with big generic list, you can do it using LinQ.
var persons = new List<Person>();
// populate data [...]
var dogs = persons.Select(p=>new Dog{Name=p.Name,Number=p.Number}).ToList();
It's easy to remember, and you can filter data previously.

How to tell AutoMapper to use "pass by reference?"

By default automapper creates a new object based on the destination's type:
public void Doit( Person personMissingStuff )
{
PersonTemplate template = _personDao.GetPersonTemplate(1);
Mapper.CreateMap<PersonTemplate, Person>();
Person basePerson = Mapper.Map<Person>( template );
Mapper.CreateMap<Person, Person>();
Person completePerson =
Mapper.Map<Person, Person>( basePerson, personMissingStuff );
...
}
Instead of getting a completePerson I just get a basePerson again. How do I tell AutoMapper to run the mappings by reference instead of by value?
Mapper.Map(source, dest) actually returns the destination object, in your case it'll be personMissingStuff.
With that said, assuming that you want to fill in only the null properties in the destination, you need to configure the mapping properly, and not map when the destination property has value.
The following sample does exactly this for class properties. For value properties, probably you need to do additional configuration. The example uses NUnit and SharpTestsEx:
[TestFixture]
public class LoadIntoInstance
{
public class Template
{
public string Name { get; set; }
}
public class Person
{
public string Name { get; set; }
public string OtherData { get; set; }
}
[Test]
public void Should_load_into_instance()
{
Mapper.CreateMap<Template, Person>()
.ForMember(d=>d.OtherData, opt=>opt.Ignore());
Mapper.CreateMap<Person, Person>()
.ForAllMembers(opt=>opt.Condition(ctx=>ctx.DestinationValue==null));
Mapper.AssertConfigurationIsValid();
var template = new Template {Name = "template"};
var basePerson = Mapper.Map<Person>(template);
var noNamePerson = new Person {OtherData = "other"};
var result = Mapper.Map(basePerson, noNamePerson);
result.Should().Be.SameInstanceAs(noNamePerson);
result.Satisfy(r =>
r.Name == "template" &&
r.OtherData == "other");
}
}
Just use traditional shallow cloning...
Person completePerson = basePerson.MemberwiseClone();
This should keep the reference types and clone the value types.
MSDN Link

AutoMapper, how to keep references between mapped objects?

I am using AutoMapper to convert a UI model to POCOs that I later serialize to XML using a DataContractSerializer in order to preserve the references between them.
The problem comes that, when mapping, the references between those entities are lost.
The UI classes reference each other, but the mapping process makes new instances for every reference, so the original relations are broken :(
Let me explain:
I have 2 entities of type Person
Person
{
List<House> OwnedHouses
}
And these 2 objects
John
who owns
House1
Will
who also owns
House1
When AutoMapper maps each Person correctly, but when it also maps House1 as two different instances!!
So I have a two copies of House1. John owns his House1 (#1) and Will owns his House1 (#2).
They are not linked anymore.
Is there any way to keep the relations that originally existed?
Thanks.
EDITED: Actually what I have is this:
A Document contains a list of ChildDocuments. Each ChildDocument has a list of Designables (Rectangles, Lines, Ellipses…) and a especial designable called ChildDocumentAdapter that contains itself ANOOTHER ChildDocument. This is the trouble, it can reference another ChildDocument.
If I'm understanding the question, you're performing two separate mapping operations - one for John, another for Will.
#Sunny is right. AutoMapper is not designed to do this. Each call you make to Mapper.Map() is typically independent of any other. By using the same instance of the HouseListConverter, you get the benefit of caching all mapped houses in a dictionary. But you have to either register it globally or pass it as an option to the mapping calls you want grouped together. That's not just extra work, it's hiding a very important implementation detail deep within the converter.
If you map both John and Will in one operation, by putting them into a collection, the output would be what you want without the need for a custom converter or resolver.
It may be an easier alternative for other people with a similar problem.
public void MapListOfPeopleWithSameHouse()
{
Mapper.CreateMap<Person, PersonDTO>();
Mapper.CreateMap<House, HouseDTO>();
var people = new List<Person>();
var house = new House() { Address = "123 Main" };
people.Add(new Person() { Name = "John", Houses = new List<House>() { house } });
people.Add(new Person() { Name = "Will", Houses = new List<House>() { house } });
var peopleDTO = Mapper.Map<List<PersonDTO>>(people);
Assert.IsNotNull(peopleDTO[0].Houses);
Assert.AreSame(peopleDTO[0].Houses[0], peopleDTO[1].Houses[0]);
}
While Automapper is not designed with this in mind, it's powerful enough to let you do it, using custom type converters. You need to create your own converter from IList<House> to IList<HouseDto>, and inject it using a factory:
using System;
using System.Collections.Generic;
using AutoMapper;
using NUnit.Framework;
using SharpTestsEx;
namespace StackOverflowExample
{
public class House
{
public string Address { get; set; }
}
public class Person
{
public IList<House> OwnedHouse { get; set; }
}
public class HouseDto
{
public string Address { get; set; }
}
public class PersonDto
{
public IList<HouseDto> OwnedHouse { get; set; }
}
[TestFixture]
public class AutomapperTest
{
public interface IHouseListConverter : ITypeConverter<IList<House>, IList<HouseDto>>
{
}
public class HouseListConverter : IHouseListConverter
{
private readonly IDictionary<House, HouseDto> existingMappings;
public HouseListConverter(IDictionary<House, HouseDto> existingMappings)
{
this.existingMappings = existingMappings;
}
public IList<HouseDto> Convert(ResolutionContext context)
{
var houses = context.SourceValue as IList<House>;
if (houses == null)
{
return null;
}
var dtos = new List<HouseDto>();
foreach (var house in houses)
{
HouseDto mapped = null;
if (existingMappings.ContainsKey(house))
{
mapped = existingMappings[house];
}
else
{
mapped = Mapper.Map<HouseDto>(house);
existingMappings[house] = mapped;
}
dtos.Add(mapped);
}
return dtos;
}
}
public class ConverterFactory
{
private readonly IHouseListConverter resolver;
public ConverterFactory()
{
resolver = new HouseListConverter(new Dictionary<House, HouseDto>());
}
public object Resolve(Type t)
{
return t == typeof(IHouseListConverter) ? resolver : null;
}
}
[Test]
public void CustomResolverTest()
{
Mapper.CreateMap<House, HouseDto>();
Mapper.CreateMap<IList<House>, IList<HouseDto>>().ConvertUsing<IHouseListConverter>();
Mapper.CreateMap<Person, PersonDto>();
var house = new House {Address = "any"};
var john = new Person {OwnedHouse = new List<House> {house}};
var will = new Person { OwnedHouse = new List<House> { house } };
var converterFactory = new ConverterFactory();
var johnDto = Mapper.Map<PersonDto>(john, o=>o.ConstructServicesUsing(converterFactory.Resolve));
var willDto = Mapper.Map<PersonDto>(will, o=>o.ConstructServicesUsing(converterFactory.Resolve));
johnDto.OwnedHouse[0].Should().Be.SameInstanceAs(willDto.OwnedHouse[0]);
johnDto.OwnedHouse[0].Address.Should().Be("any");
}
}
}

Polymorphism with AutoMapper

I have these business classes:
class BaseNode
{
public string name;
}
class CompositeNode : BaseNode
{
public List<BaseNode> childs = new List<BaseNode>();
}
And this flat dto:
class NodeDto
{
public string name;
public List<NodeDto> childs;
}
(note how all derived types are represented by one dto class)
I use auto mapper to do a conversion:
Mapper.CreateMap<BaseNode, NodeDto>()
.Include<CompositeNode, NodeDto>()
.ForMember(s => s.childs, prop => prop.Ignore());
Mapper.CreateMap<CompositeNode, NodeDto>();
Mapper.AssertConfigurationIsValid();
var root = new CompositeNode() { name = "root" };
var child = new CompositeNode {name = "child"};
var child2 = new CompositeNode { name = "child2" };
root.childs.Add(child);
child.childs.Add(child2);
var rootDto = Mapper.Map<CompositeNode, NodeDto>(root);
However the below is always null instead of having the child list:
rootDto.childs[0].childs
(i.e. only first level child is mapped correctly)
If I remove the prop.Ignore part I get an assert error that the childs property is not mapped.
What am I doing wrong?
This is old, but came across it looking for something else... You're telling it to ignore the childs field. AutoMapper is doing what it was told to do.
.ForMember(s => s.childs, prop => prop.Ignore());
You don't have properties in your classes public string Name {get;set;}, you have public Fields, I think that's the problem
also in order to map this classes you only need to create 2 simple maps
Mapper.CreateMap<CompositeNode, NodeDto>();
Mapper.CreateMap<BaseNode, NodeDto>()
.ForMember(s => s.childs, prop => prop.Ignore());;

Categories