Automapper: Mapping onto existing nested complex property - c#

Is it possible to tell AutoMapper during map creating to map onto existing instance of nested property?
Let's suppose I've got a class:
public class SomeClass
{
public int Id {get; set;}
public Complex Settings {get; set;}
}
public class Complex
{
public int Id { get; set;}
public string SomeText { get; set;}
}
I want to create map from SomeClass to SomeClass and use it to map properties onto existing instance.
Mapper.CreateMap<SomeClass, SomeClass>()
.ForMember(src => src.Settings, opts => opts.MapFrom(src => Mapper.Map<Complex, Complex>(src));
Mapper.CreateMap<Complex, Complex>();
Mapper.Map<SomeClass, SomeClass>(a, b);
Where a and b are instances of SomeClass. The problem is this solution maps properties onto existing instance but creates new instance of Complex instead of mapping a.Complex onto existing b.Complex.
Is it possible to configure AutoMapper to get desired behavior?
(It's causing me a lot of problems with Entity Framework).

I figured it out myself. Solution was pretty simple.
Correct map creation looks like this:
Mapper.CreateMap<SomeClass, SomeClass>()
.ForMember(src => src.Settings, opts => opts.Ignore())
.AfterMap((src, dst) => Mapper.Map<TestSettings,TestSettings>(src.TestSettings, dst.TestSettings);
Mapper.CreateMap<Complex, Complex>();

Related

Elasticsearch mapping DTO after source filtering in .NET

I have a POCO FooIndex that is mapped to an index in elasticsearch and I want to perform a query on this index and return only specific fields. I created a DTO FooStatus that only contains properties with the fields I am returning.
This works fine if the property names in the DTO match the names in the index mapping, as expected, but I would like to have different names for some properties and map them to the index fields. How can I do this?
var searchDescriptor = new SearchDescriptor<FooIndex>()
.Index(index)
.Source(sf => sf
.Includes(i => i
.Fields(
f => f.FooId,
f => f.FirstName,
f => f.LastName,
f => f.PrimaryEmail,
f => f.UpdatedDate,
f => f.Statuses)))
.Query(q => q
.Bool(b => b
.Filter(f => f
.Terms(t => t
.Field(p => p.Statuses.Select(
x => x.blahId))
.Terms(reqIds)))));
var results = await _elasticClient.SearchAsync<FooStatus>(searchDescriptor);
return results.Documents;
public class FooStatus
{
public string FooId { get; set; }
public string FirstName {get; set;}
public string LastName {get; set; }
public string Email {get; set;} // Map this from FooIndex.PrimaryEmail
public DateTime? DateModified {get; set; } // Map this from FooIndex.UpdatedDate
public List<FooStatus> Statuses { get; set; }
}
I would like to have different names for some properties and map them to the index fields. How can I do this?
If the properties that you want to map are at the same depth in the object graph, then you can use DataMemberAttribute or PropertyNameAttribute to map fields to differently named properties on the POCO.
If the properties are at different depths, this isn't something that can be easily done with the built-in serializer. You could have private members with attributes applied that the serializer will deserialize JSON fields to, and public properties with the desired names. The overall shape of the object graph to deserialize would need to be same as FooIndex however e.g. to map FooIndex.Bar.Baz would require the object graph of FooStatus to be three levels deep in order to map Baz.
To do it with other serializers like JSON.NET and System.Text.Json would require writing a custom JsonConverter, reading each property in turn, and assigning values. It's a fair bit of effort to do, and would require revisiting any time FooStatus' properties need to change.

Deep cloning via Automapper ignoring specific property from the hierarchy

I have fairly simple question regarding Automapper mapping definition. My intent is to deep clone an object via Automapper while ignoring 'Id' property, this is why i have chosen it to customize the mapping.
public interface IEntity<T>
{
T Id { get; }
}
public abstract class Entity : IEntity<Guid>
{
public Guid Id { get; set; }
}
All my entities are deriving from Entity class and i simply wants to ignore all Id property in the nested hierarchy of my object without being so explicit about the mapping definition.
So far i have come up with the following piece of code to do the cloning but how to ignore Id property mapping for the nested properties and not just for the root.
public static T AutomapperClone<T>(this T source)
where T : IEntity<Guid>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<T, T>()
.ForMember(d => d.Id, o => o.Ignore());
});
// checking configuration validity
config.AssertConfigurationIsValid();
// creating mapper
var mapper = config.CreateMapper();
var copy = mapper.Map<T, T>(source);
return copy;
}
The idea is that all entities get their new Id instead of using the same mapped ones. Is it accomplishable via Automapper?
Appreciate your feedback.
I wouldn't use Automapper for this person, try AnyClone to do this. It does deep cloning and can ignore by property name which seems to be what you are looking for.

Automapper : how to eleminate repetitive mapping?

I have following structure for tables. The two tables have a lot of common properties over 20 im just listing a two. Also I have 10 tables similar to this. This is how the tables are in the database. There is over 10 concrete tables with similar properties and are not connected to each other in any way. I am using POCO generator to generate the classes from my database.
public class A
{
public string name {get;set;}
public string address {get;set;}
public string AId {get;set;}
}
public class B
{
public string name {get;set;}
public string address {get;set;}
public string BId {get;set;}
}
I have following viewModels:
public class BaseViewModel
{
public string Fullname {get;set;}
public string Fulladdress {get;set;}
}
public class AviewModel : BaseViewModel
{
public string AId {get;set;}
}
public class BViewModel : BaseViewModel
{
public string BId {get;set;}
}
when I create mapping i have to repeat all this for each viewModel that I have created.
config.CreateMap<A, AviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();
config.CreateMap<B, BviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();
Is it possible to reduce the repetitive mappings that I might potentially have to do ?
You can move the common mapping code to a helper generic method. You will constrain the TDestination type to be a class derived from BaseViewModel, thus allowing to access the destination members in ForMember method. And for source mapping you will use the MapFrom overload accepting string property name:
public static class CommonMappings
{
public static IMappingExpression<TSource, TDestination> MapToBaseViewModel<TSource, TDestination>(this IMappingExpression<TSource, TDestination> map)
where TDestination : BaseViewModel
{
return map
.ForMember(dest => dest.Fulladdress, opt => opt.MapFrom("address"))
.ForMember(dest => dest.Fullname, opt => opt.MapFrom("name"));
}
}
Then you can use it like this:
config.CreateMap<A, AViewModel>()
.MapToBaseViewModel()
// ... specific mappings
.ReverseMap();
config.CreateMap<B, BViewModel>()
.MapToBaseViewModel()
// ... specific mappings
.ReverseMap();
Update: It turns out that automatic reverse mapping in the latest at this time AutoMapper 6.1.1 works for the lambda overload of MapFrom, but not for the string overload (in AutoMapper 5 it doesn't work at all). So until it gets fixed, you can use the following MapFrom(string) replacement:
public static class AMExtensions
{
public static void From<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, string name)
{
var parameter = Expression.Parameter(typeof(TSource), "src");
var body = Expression.PropertyOrField(parameter, name);
var selector = Expression.Lambda(body, parameter);
opt.MapFrom((dynamic)selector);
}
}
Which means you'll need to replace MapFrom calls in the original solution with From, because we can't give the extension method the same name since it has less priority than the concrete interface method.
Probably too much effort compared to base class approach. But useful in case you can't control the design of the entity classes.
You need a base class for the source classes. You create a map between the base source class and the destination class. You include that map in the map for the derived classes. That would allow you to reuse the configuration. The docs. For simple cases you can use As instead of Include.
You set an attribute on properties on your models.
This contains the name of the property it maps from on a source object.
Then you make a generic method that accepts the target object and source object, that finds the customattribute on the property where the attribute was set, and the property on the target object (or vice versa) and then sets the value.
You can even handle nested objects by asking if its a class property or not.

How to configure a mapping of collection types with AutoMapper?

For example, I've implemented two classes like these:
public class A
{
public List<C> Items { get; set; }
}
public class B
{
public IImmutableList<C> Items { get; set; }
}
public class C
{
}
When I try to map A to B and vice versa, I get an exception because List<string> cannot be converted to IImmutable<string>.
Probably I could provide a mapping for A<->B, but since it'll be a very common pattern in my solution, I'd like to avoid to manually mapping each class that may fall into the same case.
Is there anyway I can generalize the whole mapping using generic type definitions from a collection type to another collection type?
This is what I want to avoid
mapperConfig.CreateMap<A, B>()
.ForMember(a => a.Items, opts => opts.Ignore())
.AfterMap
(
(source, target) =>
{
target.Items = source.Items.ToImmutableList();
}
);

C# Automapper Nested Object

Ive searched for this for few days now and cant seem to get anything to work, I am using c# MVC Entity Framework with Automapper and im trying to achieve the below ViewModels (mainly LostDocumentVM) to be mapped from my database, all other properties will be set in controllers.
Here is my ViewModels...
DocumentVM
{
Public Enum.HistoricType HistoricType {get;set;}
Public DocumentChildVM Document { get; set;}
}
DocumentChildVM
{
Public bool ShowHistoricLink {get;set;}
Public IEnumerable<ListDocumentVM> DocumentsToReview {get;set;}
}
ListDocumentVM
{
Public int Id {get;set;}
Public string Name {get; set;}
Public DateTime? ReviewDate {get;set;}
}
I initialise the DocumentVM like this...
DocumentVM documentVM = DataContext.SystemUser.Where(x=>x.SustemUserID==LoggedOnUserID).Project().To<DocumentVM>().SingleOrDefault();
And my mapping is like this...
Mapper.CreateMap<SystemUser,DocumentVM>()
.ForMember(dest=>dest.Document.DocumentsToReview, opt=>opt.MapFrom(src=>src.Documents.Where(x=>x.DocumentType == Enum.DocumentType.Assessment));
Im new to AutoMapper and struggling to get more advanced mappings to work.
Yes, your ForMember member must refer to a member on the destination type, and yours is referring to a member on the child type. Instead, you'll need to create an AfterMap function that fills in this information on that child entity.
It's not difficult, but you have a bit of a strange set up where a child object Document has a property DocumentsToReview from another property on the parent DocumentVM:
documentVM.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment);
When you have to shuffle data between sibling/nephew members, it gets a little more challenging.
To do this with AfterMap:
Mapper.CreateMap<SystemUser, DocumentVM>()
.AfterMap((src, dest) => dest.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment));

Categories