Automapper Map lists with layered properties - c#

I'm having some trouble mapping an object with multiple layers of properties to an object with a single layer of properties. Here's an example:
My destination class
public class Part
{
public string Name { get; set; }
public string PartNumber { get; set; }
public string Position { get; set; }
public IList<Part> ReplacedBy { get; set; } = new List<Part>();
}
My source classes
public class PartType
{
public string PartNumber { get; set; }
public PartInformationType Part { get; set; }
}
public class PartInformationType
{
public string Position { get; set; }
public string Name { get; set; }
public IList<PartType> ReplacedBy { get; set; } = new List<PartType>();
}
Note that the real objects have ALOT more properties in each layer so it would be a hassle to do a ForMember() on each affected property. Is there any automated way to do this for me?
Expected result:
This is giving me the expected result but only for one generation of parts, say that each replacedBy part is replaced by another part for 10 generations, that quickly becomes unmanageable
var part = Mapper.DynamicMap<Part>(result);
part.ReplacedBy = new List<ReplacementPart>();
foreach (var partType in result.ReplacedBy)
{
var replacementPart = Mapper.DynamicMap<ReplacementPart(partType.Part);
replacementPart.Name= partType.Name;
replacementPart.Position= partType.Position;
part.ReplacedBy.Add(replacementPart);
}

This is an interesting problem, and I happen to have solved a very similar one recently in one of my projects so hopefully my answer will suit your needs as well or at least get you going on the right track. I've adjusted the sample code somewhat to your case.
My destination model class (pretty much same as yours):
public class PartModel
{
public string Name { get; set; }
public string PartNumber { get; set; }
public string Position { get; set; }
public List<PartModel> ReplacedBy { get; set; }
}
My two source classes (also pretty much same as yours)
public class PartEntity
{
public string PartNumber { get; set; }
public PartEntityInformation PartEntityInformation { get; set; }
}
public class PartEntityInformation
{
public string Position { get; set; }
public string Name { get; set; }
public List<PartEntity> ReplacedBy { get; set; }
}
I have defined a static EntityMap with the configurations for my two mappings. As I have mentioned in the comments of your question - I am not specifying any particular member mapping config as Automapper will just map by convention because the property names match between my source and destination objects.
public static class EntityMap
{
public static IMapper EntityMapper { get; set; }
static EntityMap()
{
EntityMapper = new MapperConfiguration(config =>
{
config.CreateMap<PartEntity, PartModel>();
config.CreateMap<PartEntityInformation, PartModel>();
}).CreateMapper();
}
}
You can use EntityMap like below. Nothing special here, it will just yield me the base model where only PartNumber property is mapped.
var rootPart = GetPart();
var rootPartModel = EntityMap.EntityMapper.Map<PartModel>(rootPart);
In order to get the nested mappings for your ReplacedBy parts, you can use recursion (this is how I solved this nested mapping requirement in my project, there may be better solutions). In this method, I recursively map the nested child objects to their destination objects because the initial mapping will give me the number of items that there is in the nested ReplacedBy list.
public static void MapRecursively(PartModel partModel, PartEntity partEntity)
{
EntityMap.EntityMapper.Map(partEntity.PartEntityInformation, partModel);
if (partEntity.PartEntityInformation.ReplacedBy == null
|| partEntity.PartEntityInformation.ReplacedBy.Count == 0) return;
for (var i = 0; i < partModel.ReplacedBy.Count; i++)
{
MapRecursively(partModel.ReplacedBy[i], partEntity.PartEntityInformation.ReplacedBy[i]);
}
}
So you can now just use the rootPart and rootPartModel with this recursive method to map out of the rest of your nested objects.
MapRecursively(rootPartModel, rootPart);
This should work out of the box, I have provided the GetRootPart() method specification below as it is just the sample data I have used.
private static PartEntity GetPart()
{
var partEntityInfo = new PartEntityInformation
{
Name = "SomeName",
Position = "2",
ReplacedBy = new List<PartEntity>
{
new PartEntity
{
PartNumber = "22",
PartEntityInformation = new PartEntityInformation
{
Name = "SomeSubName"
}
},
new PartEntity
{
PartNumber = "33",
PartEntityInformation = new PartEntityInformation
{
Name = "33SubName",
Position = "33SubPosition",
ReplacedBy = new List<PartEntity>
{
new PartEntity
{
PartNumber = "444",
PartEntityInformation = new PartEntityInformation
{
Name = "444SubSubname"
}
}
}
}
}
}
};
var part = new PartEntity
{
PartNumber = "1",
PartEntityInformation = partEntityInfo
};
return part;
}

Related

Convert nested array class to another nested array class c#

I have 2 classes that look exactly the same but reside in different namespaces. One property of the nested class is an array of itself, which allows property nesting/recursion (sort of like a command pattern)
I am trying to convert/cast the class from one namespace to the class in another namespace. I have the below code:
namespace Common.Class
{
public class Root
{
public string Key { get; set; }
public Child[] Children { get; set; }
public class Child
{
public string Content { get; set; }
public Child[] RecursionChild { get; set; }
}
}
}
namespace Uncommon.Class
{
class Root
{
public string Key { get; set; }
public Child[] Children { get; set; }
public class Child
{
public string Content { get; set; }
public Child RecursionChild { get; set; }
}
}
}
The main program
static void Main(string[] args)
{
var commonRoot = new Common.Class.Root
{
Key = "1234-lkij-125l-123o-123s",
Children = new Common.Class.Root.Child[]
{
new Common.Class.Root.Child
{
Content = "Level 1 content",
RecursionChild = new Common.Class.Root.Child[] { }
}
}
};
var uncommonRoot = new Uncommon.Class.Root
{
Key = commonRoot.Key,
Children = commonRoot.Children // here I get error: Cannot implicitly convert type 'Common.Class.Root.Child[]' to 'Uncommon.Class.Root.Child[]'
};
}
You need to convert the children too.
Because you've got that recursive child, you can't pull this off with just anonymous functions, because the function has to be able to call itself, and you need a name for that. So we need to introduce a local function with a real name, e.g. Converter.
Uncommon.Root.Child Converter(Common.Root.Child source) => new Uncommon.Root.Child
{
Content = source.Content,
RecursiveChild = Converter(source.ResursiveChild)
};
var uncommonRoot = new Uncommon.Class.Root
{
Key = commonRoot.Key,
Children = commonRoot.Children.Select(Converter).ToArray();
};

How to manually map a List of object to a list of DTO?

I have these:
public class FamilyHead
{
public Guid HeadId { get; set; }
public string Name { get; set; }
}
public class Citizen
{
public Guid Id { get; set; }
public string Name { get; set; }
public short Age { get; set; }
// more properties
[ForeignKey("FamilyHead")]
public Guid HeadId { get; set; }
public virtual FamilyHead FamilyHead { get; set; }
}
public class CitizenDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
public short Age { get; set; }
public Guid HeadId
public string HeadName { get; set; }
}
I can manually map it via extension method if it is a single instance:
public static CitizenDTO ToDTO(this Citizen citizen)
{
if (citizen == null) return null;
return new CitizenDTO {
Id = citizen.Id,
Name = citizen.Name,
HeadId = citizen.HeadId,
HeadName = citizen.FamilyHead.Name
}
}
var dto = aCitizen.ToDTO();
But how to map a list of citizens? I think Select() might do the work but I only know how to do it if the model and the dto have a same structure. Like this example:
IEnumerable<int> integers = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<string> strings = integers.Select(i => i.ToString());
So how to map a list of it?
You can use Linq Select() as you used for string in your question, no need to write long extension method
IEnumerable<CitizenDTO> dto = citizens.Select(x => x.ToDTO());
I found the answer before finishing my question. Just iterate through the list and add mapped DTO to it. Silly me
// Extension method
public static IEnumerable<CitizenDTO> ToDTO(this IEnumerable<Citizen> citizens)
{
if (citizen == null) return null;
var dto = new List<CitizenDTO>();
foreach(var citizen in citizens) {
dto.Add(citizen.ToDTO());
}
return dto;
}
// How to use
IEnumerable<CitizenDTO> result = listOfCitizens.ToDTO();

Add items to an entity collection via ChangeTracker

I chose to put a complete example of my code in order to demonstrate what I exactly need. Briefly, what I would like to do, is to get a generic code in public override int SaveChanges() that could work to all entities that implements a translation rather than write a one-by-one.
ENTITIES
public partial class EntityOne
{
public long EntityOneId { get; set; }
public string SomeProperty { get; set; }
public virtual ICollection<EntityOneTranslation> EntityOneTranslations { get; set; }
public EntityOne()
{
this.EntityOneTranslations = new HashSet<EntityOneTranslation>();
}
}
public class EntityOneTranslation : EntityTranslation<long, EntityOne>
{
public string LocalizedEntityOneProp1 { get; set; }
public string LocalizedEntityOneProp1 { get; set; }
}
public partial class EntityTwo
{
public long EntityTwoId { get; set; }
public string SomeProperty { get; set; }
public virtual ICollection<EntityTwoTranslation> EntityTwoTranslations { get; set; }
public EntityTwo()
{
this.EntityTwoTranslations = new HashSet<EntityTwoTranslation>();
}
}
public class EntityTwoTranslation : EntityTranslation<long, EntityTwo>
{
public string LocalizedEntityTwoProp1 { get; set; }
public string LocalizedEntityTwoProp2 { get; set; }
}
public class EntityTranslation<TEntityKey, TEntity> : ILanguage
{
[Key, Column(Order = 0), ForeignKey("Entity")]
public TEntityKey EntityKey { get; set; }
[Key, Column(Order = 1), ForeignKey("Language")]
public long LanguageId { get; set; }
public virtual TEntity Entity { get; set; }
public virtual Language Language { get; set; }
}
INTERFACE
public interface ILanguage
{
long LanguageId { get; set; }
}
Here is the target
How would I get the entity navigation property using reflection/or something in order to reuse my code that could work too all entities that has a translation property collection?
I already tried to ask the same thing from 2 other posts, but I didn't give all the info. I guess that's why nobody could give me the expected answer.
Adding new entries over entity navigation property collection
Cast PropertyInfo to Collection
SAVE CHANGES OVERRIDE
public override int SaveChanges()
{
foreach (var entityOneEntry in ChangeTracker.Entries<EntityOne>())
{
if (entityOneEntry.State == EntityState.Added)
{
//Get entity localized properties values of current language.
var currentLanguageEntry = entityOneEntry.Entity.EntityOneTranslations.FirstOrDefault();
var localizedEntityOneProp1 = currentLanguageEntry.LocalizedEntityOneProp1;
var localizedEntityOneProp2 = currentLanguageEntry.LocalizedEntityOneProp2;
//Get all languages but the current one.
var languages = Language.Where(l => l.LanguageId != currentCulture.Key);
//Add missing translations copying the same values.
foreach (var language in languages)
entityOneEntry.Entity.EntityOneTranslations.Add(new EntityOne()
{
LanguageId = language.LanguageId,
LocalizedEntityOneProp1 = localizedEntityOneProp1,
LocalizedEntityOneProp2 = localizedEntityOneProp2
});
}
}
foreach (var entityOneEntry in ChangeTracker.Entries<EntityTwo>())
{
if (entityOneEntry.State == EntityState.Added)
{
//Get entity localized properties values of current language.
var currentLanguageEntry = entityOneEntry.Entity.EntityTwoTranslations.FirstOrDefault();
var localizedEntityTwoProp1 = currentLanguageEntry.LocalizedEntityTwoProp1;
var localizedEntityTwoProp2 = currentLanguageEntry.LocalizedEntityTwoProp2;
//Get all languages but the current one.
var languages = Language.Where(l => l.LanguageId != currentCulture.Key);
//Add missing translations copying the same values.
foreach (var language in languages)
entityOneEntry.Entity.EntityTwoTranslations.Add(new EntityTwo()
{
LanguageId = language.LanguageId,
LocalizedEntityTwoProp1 = localizedEntityTwoProp1,
LocalizedEntityTwoProp2 = localizedEntityTwoProp2
});
}
}
}

Passing dynamic type, iterating fields and replacing values

I have 3/4 different models that each contain their own nested model. I need a way of iterating all fields, including those of the nested model and do a string replace (although not all fields are strings).
My initial idea was to write a method which allows for a 'dynamic' type to be passed.
Input model:
Name = Joe
Surname = Smith
Address = new ClientAddress
{
Line1: Item A
Line2: mistake
Line3: mistake
}
My example method:
MyMethod (dynamic passInModel)
{
....
passInModel.Replace("mistake","correction");
return passInModel;
}
Output:
Name = Joe
Surname = Smith
Address = new ClientAddress
{
Line1: Item A
Line2: correction
Line3: correction
}
Despite trying various ways of doing it I've not had any success in writing something that does the job.
You could write a method that accepts an object and use reflection to iterate through all the fields, but you're getting into messy territory there. In my opinion, even using dynamic here is messy.
Consider using a modified visitor pattern here. If your domain objects look like this:
public class ModelBase
{
}
public class MyModel1 : ModelBase
{
public string Name { get; set; }
public string Surname { get; set; }
public ClientAddress Address { get; set; }
}
public class MyModel2 : ModelBase
{
public string CompanyName { get; set; }
public string Region { get; set; }
public CompanyAddress Address { get; set; }
}
public class ClientAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
}
public class CompanyAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public List<string> AdditionalLines { get; set; }
}
Write a visitor that takes an abstract ModelBase and dispatches the correct type-safe visitor:
public class ModelFixVisitor
{
public ModelBase Visit(ModelBase model)
{
var asModel1 = model as MyModel1;
if (asModel1 != null)
{
return new Model1FixVisitor().Visit(asModel1);
}
var asModel2 = model as MyModel2;
if (asModel2 != null)
{
return new Model2FixVisitor().Visit(asModel2);
}
throw new NotImplementedException("Unknown model type.");
}
}
Then write a simple class for each type (and subtype) you need to visit:
public class Model1FixVisitor
{
public MyModel1 Visit(MyModel1 model)
{
model.Name = new StringFixVisitor().Visit(model.Name);
model.Surname = new StringFixVisitor().Visit(model.Surname);
model.Address = new ClientAddressFixVisitor().Visit(model.Address);
return model;
}
}
public class Model2FixVisitor
{
public MyModel2 Visit(MyModel2 model)
{
model.CompanyName = new StringFixVisitor().Visit(model.CompanyName);
model.Region = new StringFixVisitor().Visit(model.Region);
model.Address = new CompanyAddressFixVisitor().Visit(model.Address);
return model;
}
}
public class ClientAddressFixVisitor
{
public ClientAddress Visit(ClientAddress address)
{
address.Line1 = new StringFixVisitor().Visit(address.Line1);
address.Line2 = new StringFixVisitor().Visit(address.Line2);
address.Line3 = new StringFixVisitor().Visit(address.Line3);
return address;
}
}
public class CompanyAddressFixVisitor
{
public CompanyAddress Visit(CompanyAddress address)
{
address.Line1 = new StringFixVisitor().Visit(address.Line1);
address.Line2 = new StringFixVisitor().Visit(address.Line2);
address.AdditionalLines = new StringListFixVisitor().Visit(address.AdditionalLines);
return address;
}
}
public class StringFixVisitor
{
public string Visit(string element)
{
return element.Replace("mistake", "correction");
}
}
public class StringListFixVisitor
{
public List<string> Visit(List<string> elements)
{
return elements
.Select(x => new StringFixVisitor().Visit(x))
.ToList();
}
}
I'm sure the code could be refactored and optimized, but it should express the general idea.
What I like about this type of solution is that it breaks the problem down into small, manageable chunks: How do I fix a string? How do I fix a ClientAddress?
Fixing entire models then becomes simple composition of these smaller classes. It's a little more verbose, but you get to keep type safety, and don't have to mess with reflection.
You can use the power of .Net reflection to solve this.
I created a class called DeepStringReplacer. Using reflection it iterates through object properties and if the type is string, perform string replace.
Check the code below:
public class DeepStringReplacer
{
public object Replace(object input, string oldValue, string newValue)
{
if (input is string)
{
return input.ToString().Replace(oldValue, newValue);
}
var fields = input.GetType().GetProperties();
foreach (var field in fields)
{
var fieldValue = field.GetValue(input);
field.SetValue(input, Replace(fieldValue, oldValue, newValue));
}
return input;
}
}
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public ClientAddress Address { get; set; }
}
public class ClientAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
}

Only return specific properties

I have developed my first API controlled in MVC4 and through the scaffolding I have got it to automatically output a list of items:
// GET api/ItemList
public IEnumerable<ItemOption> GetItemOptions()
{
var itemoptions = db.ItemOptions.Include(i => i.Item);
return itemoptions.AsEnumerable();
}
This shows all the item properties from my model:
public class ItemOption
{
public int ItemOptionId { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
public string test1 { get; set; }
public double PriceNet { get; set; }
}
How can I specify specific fields I wish to be returned? For example, I just want the ItemOptionId, Active and Name to be returned.
I have tried adding additional includes, but this seems to be at an object level.
Try creating a new type to represent the properties you'd like to return:
public class ItemOptionResult
{
public int ItemOptionId { get; set; }
public bool Active { get; set; }
public string Name { get; set; }
}
And then projecting your ItemOption collection, like this:
// GET api/ItemList
public IEnumerable<ItemOptionResult> GetItemOptions()
{`enter code here`
var itemoptions =
db.ItemOptions
.Select(i =>
new ItemOptionResult
{
ItemOptionId = i.ItemOptionId,
Active = i.Active,
Name = i.Name
});
return itemoptions.AsEnumerable();
}
Try this :
var itemoptions = db.ItemOptions.Select(io => new ItemOption()
{
ItemOptionId = io.ItemOptionId,
Active = io.Active ,
Name = io.Name
}
return itemoptions.AsEnumerable();

Categories