AutoMapper ignores Properties - c#

when I'm using this mapping
Mapper.CreateMap<DataSourceConfigurationContract, DataSourceConfigurationContract>().ForMember(x => (object)x.DatabaseTypeException, opt => opt.Ignore())
.ForMember(x => (object)x.DatabaseType, opt => opt.Ignore());
var mappedValue = Mapper.Map<DataSourceConfigurationContract, DataSourceConfigurationContract>(dataSourceConfiguration);
for this class
public sealed class DataSourceConfigurationContract {
public string Name { get; set; }
public string ConnectionString { get; set; }
public string ConnectionType { get; set; }
public DataSourcePropertyContractCollection Properties { get; set; }
public DataSourceAreaConfigurationContractCollection Areas { get; set; }
public UserContractCollection AllowedUsers{ get; set; }
public DataSourceType? DatabaseType { get; set; }
public ExceptionContract DatabaseTypeException { get; set; }
public DataSourceType DataSourceType { get; set; } }
some Properties are ignored (e.g. Areas) that should be mapped. The string properties seem to be always correctly mapped. What have I done wrong?

AutoMapper only support the following collections out of the box: http://automapper.codeplex.com/wikipage?title=Lists%20and%20Arrays&referringTitle=Home . I guess that your properties that are not copied are of type XXXCollection.
You can solve this by creating a custom type converter for your collection types: http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters&referringTitle=Home

For your collections you need to do something similar to the following (taken from some code I've recently worked on):
Mapper.CreateMap<List<QuizItemTypeModel>, List<Quiz.DataContracts.QuizItemType>>()
.Include<QuizDataCompositeModel, Quiz.DataContracts.QuizDataComposite>();
Where QuizDataCompositeModel and Quiz.DataContracts.QuizDataComposite both extend List<"RespectiveType">

It's quite simple:
Mapper.CreateMap<DataSourceAreaConfigurationContract, DataSourceAreaConfiguration>();
Mapper.CreateMap<DataSourceConfigurationContract, DataSourceConfigurationContract>()
.ForMember(dest => dest.Areas, opt => opt.UseDestinationValue());
Tipp: Download the source code and learn from the given unittests and samples!
You can get it there: http://automapper.codeplex.com/SourceControl/list/changesets

Related

Map components of DTO which are DTOs as well

This is my class which holds database data:
public partial class PermissionGroup
{
public int Id { get; set; }
public string Name { get; set; }
// other database properties
public virtual ICollection<GroupActionPermission> GroupActionPermissions { get; set; }
}
And that's my dto's:
public class PermissionGroupDTO
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GroupActionPermissionDTO> ActionPermissions { get; set; }
}
public class GroupActionPermissionDTO
{
public int Id { get; set; }
public int GroupId { get; set; }
public int PermissionActionId { get; set; }
public PermissionGroupDTO Group { get; set; }
}
Now, I am making mapping:
public IEnumerable<PermissionGroupDTO> GetGroups()
{
return OnConnect<IEnumerable<PermissionGroupDTO>>(db =>
{
return db.PermissionGroups
.Include(i => i.GroupActionPermissions)
.ProjectTo<PermissionGroupDTO>()
.ToList();
});
}
And I am getting collection of PermissionGroupDTO which should contains collection of GroupActionPermissionDTO, but that collection stays null. Is there something wrong with my code? I am afraid that automapper can map collections from foreign keys.
Also, thats my automapper initializer:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<PermissionGroup, PermissionGroupDTO>();
cfg.CreateMap<GroupActionPermission, GroupActionPermissionDTO>();
});
I believe the reason is desribed here http://docs.automapper.org/en/stable/Queryable-Extensions.html
Note that for this feature to work, all type conversions must be explicitly handled in your Mapping.
So that means you should manually configure the mapping:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<PermissionGroup, PermissionGroupDTO>()
.ForMember(dto => dto.ActionPermissions , conf => conf.MapFrom(ol => ol.GroupActionPermissions )));;
cfg.CreateMap<GroupActionPermission, GroupActionPermissionDTO>();
});
BTW, note that fields are named differently: GroupActionPermissions vs. ActionPermissions. This is also the reason why automapper doesn't map it automatically and then you should use the manual configuration I wrote.

Flatten Complex Object To Multiple Flatten Objects Using AutoMapper

I have a View Model such as
public class RootViewModel
{
public CreateCompanyViewModel Company { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public CreateUserTypeViewModel UserType { get; set; }
}
And CreateCompanyViewModel and CreateUserTypeViewModel are like
public class CreateCompanyViewModel
{
public string CompanyName { get; set; }
}
public class CreateUserTypeViewModel
{
public string UserTypeName { get; set; }
}
I want this RootVM to be flattened to multiple DTO's. The 3 DTO's for the above RootVM I have are like
public class UserDTO
{
public string Name { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class CompanyDTO
{
public string CompanyName { get; set; }
}
public class UserTypeDTO
{
public string UserTypeName { get; set; }
}
NOTE : Note that CompanyDTO and UserTypeDTO are not nested object (part of) UserDTO unlike RootVM.
When I'm doing the mapping using AutoMapper RootVM properties gets mapped to UserDTO but CompanyDTO and UserTypeDTO are null as expected.
I tried mapping them by using ForMember function with MapFrom and ResolveUsing methods, but both of them shows error as
Custom configuration for members is only supported for top-level
individual members on a type.
UPDATE
Below is my mapping code
CreateMap<RootViewModel, CompanyDTO>();
CreateMap<RootViewModel, UserDTO>();
CreateMap<RootViewModel, UserTypeDTO>();
CreateMap<CreateCompanyViewModel, CompanyDTO>();
CreateMap<CreateUserTypeViewModel, UserTypeDTO>();
I'm using AutoMapper 5.2.0
UPDATE - Fix :
Well what I found is, either I have to use .ForMember for all the properties manually, else for automatic convention to work, I need to use https://github.com/AutoMapper/AutoMapper/wiki/Flattening or https://arnabroychowdhurypersonal.wordpress.com/2014/03/08/flattening-object-with-automapper/.
This is the only way to make it work.
Wish I could do .ForMember(d => d, s => s.MapFrom(x => x.Company)) and it'd map all the properties from CreateCompanyViewModel => CompanyDTO. This would have been very handy, but AutoMapper doesn't supports this.
Try following
CreateMap<CreateCompanyViewModel, CompanyDTO>();
CreateMap<CreateUserTypeViewModel, UserTypeDTO>();
CreateMap<RootViewModel, CompanyDTO>()
.ForMember(dest => dest.CompanyName, opt => opt.MapFrom(src => src.Company.CompanyName));
CreateMap < RootViewModel, UserTypeDTO()
.ForMember(dest => dest.UserTypeName, opt => opt.MapFrom(src => src.UserType.UserTypeName));
CreateMap<RootViewModel, UserDTO>();

Automapper with nested destination Property

I am trying to map a nested child property like so.
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Collection, CollectionDTO>()
.ForMember(dest => dest.Items.Select(x => x.AddedToCollectionDate),
opts => opts.MapFrom(src =>
src.CollectionItems.Select(ci => ci.AddedToCollectionDate)));
});
Collection.Items is a List<Item>. Each Item has a AddedToCollectionDate property that I need to populate from the source mapping.
CollectionDTO has a navigational property to a cross-join table called CollectionItem, which has a property called AddedToCollectionDate.
Error:
Custom configuration for members is only supported for top-level individual members on a type.
How can I achieve this with AutoMapper?
Clases (omitted other properties for brevity):
public partial class Collection
{
public virtual ICollection<CollectionItem> CollectionItems { get; set; }
}
public partial class CollectionItem
{
public System.DateTime AddedToCollectionDate { get; set; }
public virtual Collection Collection { get; set; }
public virtual Item Item { get; set; }
}
public class CollectionDTO
{
public List<ItemDTO> Items { get; set; }
public DateTime LastAccessedDate { get; set; }
}
public class Item
{
public DateTime LastAccessedDate { get; set; }
public virtual ICollection<CollectionItem> CollectionItems { get; set; }
}
I got it to work by doing a FirstOrDefault() instead of aSelect() selection like this.
cfg.CreateMap<Item, ItemDTO>()
.ForMember(dest => dest.AddedToCollectionDate,
opts => opts.MapFrom(src =>
src.CollectionItems.FirstOrDefault().AddedToCollectionDate));
Instead of the ForMember call, you should configure a mapping for CollectionItem as well.
cfg.CreateMap<CollectionItem, ItemDTO>();
cfg.CreateMap<Collection, CollectionDTO>();
If you also rename CollectionDTO.Items to CollectionDTO.CollectionItems AutoMapper knows enough to map Collection.CollectionItems to the right collection in CollectionDTO.

AutoMapper: map DTO back to domain object with child objects [duplicate]

I've been trying to use AutoMapper to save some time going from my DTOs to my domain objects, but I'm having trouble configuring the map so that it works, and I'm beginning to wonder if AutoMapper might be the wrong tool for the job.
Consider this example of domain objects (one entity and one value):
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; }
}
My DTO (from a Linq-to-SQL object) is coming out looking roughly like this:
public class PersonDTO
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
}
I'd like to be able to do this in my repository:
return Mapper.Map<PersonDTO, Person>(result);
I've tried configuring AutoMapper every way I can figure, but I keep getting the generic Missing type map configuration or unsupported mapping error, with no details to tell me where I'm failing.
I've tried a number of different configurations, but here are a few:
Mapper.CreateMap<PersonDTO, Person>()
.ForMember(dest => dest.Address, opt => opt.MapFrom(Mapper.Map<Person, Domain.StreetAddress>));
and
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));
I've read that flattening objects with AutoMapper is easy, but unflattening them isn't easy...or even possible. Can anyone tell me whether I'm trying to do the impossible, and if not what I'm doing wrong?
Note that my actual objects are a little more complicated, so it's possible I'm leaving out info that is the key to the error...if what I'm doing looks right I can provide more info or start simplifying my objects for testing.
This also seems to work for me:
Mapper.CreateMap<PersonDto, Address>();
Mapper.CreateMap<PersonDto, Person>()
.ForMember(dest => dest.Address, opt => opt.MapFrom( src => src )));
Basically, create a mapping from the dto to both objects, and then use it as the source for the child object.
Can't post a comment, so posting an answer. I guess there were some changes in AutoMapper implementation so answer https://stackoverflow.com/a/5154321/2164198 proposed by HansoS is no longer compilable. Though there is another method that can be used in such scenarios - 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 }; }))
In addition to sydneyos answer and according to Trevor de Koekkoek comment, two way mapping is possible this way
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 mappings
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 )));
If you implement NameOf class, you can get rid of prefix magic string
Mapper.Initialize(cfg => cfg.RecognizePrefixes(Nameof<Person>.Property(x => x.Address)));
EDIT: In C# 6
Mapper.Initialize(cfg => cfg.RecognizePrefixes(nameof(Person.Address)));
use https://github.com/omuleanu/ValueInjecter, it does flattening and unflattening, and anything else you need, there is an asp.net mvc sample application in the download where all the features are demonstrated (also unit tests)
This might be late but you can solve this by using lambda expressions to create the object like this:
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 }; }))
I have another solution. The main idea is that AutoMapper know how to flatten nested objects when you name properly properties in flattened object: adding nested object property name as a prefix. For your case Address is prefix:
public class PersonDTO
{
public string Name { get; set; }
public string AddressCity { get; set; }
public string AddressState { get; set; }
...
}
So creating familiar mapping from nested to flattened and then using ReverseMap method allows AutomMapper to understand how to unflatten nested object.
CreateMap<Person, PersonDTO>()
.ReverseMap();
That's all!
I'm using this
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());
}
Configuration
new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, PersonDTO>();
cfg.CreateMap<PersonDTO, Person>().ForMember(x => x.HomeAddress, opt => opt.Unflatten());
});
Models
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; }
}
Following AutoMapper flattening conventions
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; }
}
Probably needs many improvements but it works...

AutoMapper: Mapping a collection of Object to a collection of strings

I need help with a special mapping with AutoMapper. I want to map a collection of objects to a collection of strings.
So I have a Tag class
public class Tag
{
public Guid Id { get; set; }
public string Name {get; set; }
}
Than in a model I have a IList of this class. Now I want to map the name's to a collection of strings.
Thats how I define the mapping rule:
.ForMember(dest => dest.Tags, opt => opt.ResolveUsing<TagNameResolver>())
And here is my ValueResolver:
protected override string ResolveCore(Tag source)
{
return source.Name;
}
But you know.. it doesn't work ;-) So maybe someone know how to do it right and can help me.
thanks a lot
Update to Jan
Sooo.. you wanted more details.. here you got it.. but I have shorten it ;)
So the Model:
public class Artocle
{
public Guid Id { get; set; }
public string Title {get; set; }
public string Text { get; set; }
public IList<Tag> Tags { get; set; }
}
And the Tag model you can see above.
I want to map it to a ArticleView... I need the tag model only for some business context, not for the output.
So here is the ViewModel I need to map to:
public class ArticleView
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public IList<string> Tags { get; set; } // The mapping problem :-)
}
So I have a BootStrapper for the mappings. My Mapping looks like this:
Mapper.CreateMap<Article, ArticleView>()
.ForMember(dest => dest.Tags, opt => opt.ResolveUsing<TagNameResolver>())
And I map it manuelly with a special method
public static ArticleView ConvertToArticleView(this Article article)
{
return Mapper.Map<Article, ArticleView>(article);
}
A unit test validated the following would map from IList<Tag> to IList<string>
private class TagNameResolver : ValueResolver<IList<Tag>, IList<string>>
{
protected override IList<string> ResolveCore(IList<Tag> source)
{
var tags = new List<string>();
foreach (var tag in source)
{
tags.Add(tag.Name);
}
return tags;
}
}
This is a shorter way of creating the map:
.ForMember(dest => dest.Tags, opt => opt.MapFrom(so => so.Tags.Select(t=>t.Name).ToList()));

Categories