Using Automapper, I want to map a property that is a List of type Employee using string.Join() to product a comma-delimited string of the names of an employee's rights. Here are the classes I'm using:
public class MappedEmployee
{
public string Name { get; set; }
public string RightNames { get; set; }
}
public class Employee
{
public string Name { get; set; }
public List<Right> Rights { get; set; }
}
public class Right
{
public string Name { get; set; }
}
And here is the code I have:
Mapper.CreateMap<Employee, MappedEmployee>()
.ForMember(d => d.RightNames, o => o.MapFrom(s => s.Rights.SelectMany(r => string.Join(", ", r.Name))));
var employee = new Employee
{
Name = "Joe Schmoe",
Rights = new List<Right>
{
new Right { Name = "Admin" },
new Right { Name = "User" },
}
};
var mappedEmployee = Mapper.Map<Employee, MappedEmployee>(employee);
However, this it's producing the folowing:
System.Linq.Enumerable+<SelectManyIterator>d__14`2[Employee.Right,System.Char]
What can I do do get a comma-delimited string of the Employee's rights?
Try using ResolveUsing instead and putting string.Join before the selection:
Mapper.CreateMap<Employee, MappedEmployee>()
.ForMember(d => d.RightNames, o => o.ResolveUsing(s => string.Join(", ",s.Rights.Select(r => r.Name))));
Related
I have a property in my destination entity with IgnoreMap attribute.
I want to disable only once. I use Automapper list to list mapping.
public class TestDto {
public string Name { get; set; }
public DateTime UpdateDate { get; set; }
}
public class Test {
public string Name { get; set; }
//Normally, I want to ignore this entities all mapping except one method.
[IgnoreMap]
public DateTime UpdateDate { get; set; }
}
class Program {
public void MapMethod(List<TestDto> sourceList)
{
var content = new MapperConfigurationExpression();
content.CreateMap<TestDto,Test>();
var config = new MapperConfiguration(content);
var mapper = config.CreateMapper();
//I do not want to ignore UpdateDate entity in here.
var destinationList = mapper.Map<List<Test>>(sourceList);
}
}
you can try this:
_mapper.Map<DestType>(result, options => options.AfterMap((s, d) => ((DestType) d).Code = null));
Full Example
void Main()
{
IConfigurationProvider conf = new MapperConfiguration(exp => exp.CreateMap<Src, Dest>());
IMapper mapper = new Mapper(conf);
var src = new Src(){
Id =1,
Name= "John Doe"
};
var result = mapper.Map<Dest>(src, options => options.AfterMap((s, d) => ((Dest) d).Name = null));
result.Dump();
var result2 = mapper.Map<List<Dest>>(srcList, options => options.AfterMap((s, d) => ((List<Dest>) d).ForEach(i => i.Name = null)));
result2.Dump();
}
public class Src
{
public int Id {get; set;}
public string Name {get; set;}
}
public class Dest
{
public int Id {get; set;}
public string Name {get; set;}
}
Alternatively
void ConfigureMap(IMappingOperationOptions<Src, Dest> opt)
{
opt.ConfigureMap()
.ForMember(dest => dest.Name, m => m.Ignore());
};
var result3 = mapper.Map<List<Dest>>(srcList, ConfigureMap());
result3.Dump();
Below is the mapper snippet with classes :
Mapper.Initialize(cfg =>
{
cfg.CreateMap<STest, ETest>().ForMember(d => d.customFields[0].stringValue, o => o.MapFrom(s => s.val));
}
);
var result = Mapper.Map<ETest>(JsonDeseriazedSource);
var serialized = JsonConvert.SerializeObject(result, Formatting.Indented);
And these are the source and destination classes to be mapped:
//source class
public class STest
{
public string Id { get; set; }
public string val { get; set; }
}
// destination class
public class ETest
{
public string Id { get; set; }
public Customfield[] customFields { get; set; }
}
public class Customfield
{
public string id { get; set; }
public string fieldName { get; set; }
public string stringValue { get; set; }
}
I need to map stringValue in Customfield object(destination) from val in Stest(source).
Thanks in advance!!
If I understand your question correctly, you can try this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<STest, Customfield>()
.ForMember(d => d.stringValue, o => o.MapFrom(s => s.val));
cfg.CreateMap<STest[], ETest>()
.ForMember(d => d.customFields, o => o.MapFrom(s => s.Select(Mapper.Map<Customfield>)));
});
var sTests= new[] { new STest { Id = "1", val = "val1" }, new STest { Id = "2", val = "val2" } };
var result = Mapper.Map<ETest>(sTests);
I am trying to map source string to List<T> for member but the value of the mapped member list property is always null.
Here is my code.
//Source model
public class Claims
{
public int Id { get; set; }
public string ClaimType {get; set; }
public string ClaimValue { get; set; } // produce json string [{"Action":"read","Status":"active"}]
}
// Dest model
public class ClaimsDto
{
public int Id { get; set; }
public string ClaimType { get; set; }
public List<ResourceActions> ClaimValues { get; set; }
}
public class ResourceActions
{
public string Action { get; set; }
public string Status { get; set; }
}
CreateMap<ResourceActions, ResourceActions>();
CreateMap<Claims, ClaimsDto>()
.ForMember(dest => dest.ClaimValues,
opt => opt.MapFrom( src=> JsonConvert.DeserializeObject<List<ResourceActions>>(src.ClaimValue)))
.ReverseMap();
Also tried with resolver like this.
.ForMember(dto => dto.ClaimValues, opt => opt.ResolveUsing<CustomResolver,string>(src=>src.ClaimValue ))
The resolver:
public class CustomResolver : IMemberValueResolver<Claims, ClaimsDto, string, List<ResourceActions>>
{
public List<ResourceActions> Resolve(Claims source, ClaimsDto destination, string sourceMember, List<ResourceActions> destinationMember, ResolutionContext context)
{
var data = JsonConvert.DeserializeObject<List<ResourceActions>>(sourceMember);
return context.Mapper.Map<List<ResourceActions>>(data); // not working
return JsonConvert.DeserializeObject<List<ResourceActions>>(sourceMember); // not working
}
}
And the controller
var mapped = _mapper.Map<List<ClaimsDto>>(source); // success without exception but the list property is null.
Other properties are mapped except this string to List<T> property.
I think this profile should solve the issue:
public class MyMapperProfile : Profile
{
public MyMapperProfile()
{
CreateMap<Claims, ClaimsDto>()
.ForMember(dto => dto.ClaimValues, cfg => cfg.MapFrom((claim, _) =>
JsonSerializer.Deserialize<IReadOnlyCollection<ResourceActions>>(claim.ClaimValue)));
}
}
Here is an example:
var config = new MapperConfiguration(cfg => cfg.AddProfile<MyMapperProfile>());
var mapper = config.CreateMapper();
var someClaims = new List<Claims>
{
new Claims { Id = 5, ClaimType = "Foo", ClaimValue = #"[{""Action"":""read"",""Status"":""active""}]" },
new Claims { Id = 7, ClaimType = "Bar", ClaimValue = #"[{""Action"":""create"",""Status"":""disabled""}]" },
};
var result = mapper.Map<List<ClaimsDto>>(someClaims);
foreach (var item in result)
{
Console.WriteLine(JsonSerializer.Serialize(item));
}
I have my source class as
public class SourceEmployee
{
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<ResidentialAddress> EmployeeResidences { get; set; }
}
and the ResidentialAddress is as under
public class ResidentialAddress
{
public string State { get; set; }
public string City { get; set; }
public int ZipCode { get; set; }
}
The Destination class is as under
public class DestinationEmployee
{
public int EmployeeID { get; set; }
public string FullName { get; set; }
public List<ResidentialAddress1> Address { get; set; }
}
public class ResidentialAddress1
{
public string FullAddress { get; set; }
}
How to perform the ForMember for the FullAddress which will be State + City + ZipCode ?
I am lost after
Mapper.CreateMap<SourceEmployee, DestinationEmployee>();
Mapper.CreateMap<SourceEmployee, DestinationEmployee>().
ForMember(f => f.FullName, f => f.MapFrom(a => string.Concat(a.FirstName, " ", a.LastName)))
.ForMember(x => x.EmployeeResidences1, x => x.MapFrom(y => string.Concat(y.EmployeeResidences.m, " ", y.LastName)));
Well, you can use LINQ to let AutoMapper know how to map 3 properties into one property and you shouldn't really use Mapper.CreateMap() as it is deprecated and will not be supported from version 5.0 - use Mapper.Initialize() instead.
Let's have a look at this example :
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SourceEmployee, DestinationEmployee>();
cfg.CreateMap<SourceEmployee, DestinationEmployee>()
.ForMember(f => f.FullName, f => f.MapFrom(a => string.Concat(a.FirstName, " ", a.LastName)))
.ForMember(
x => x.Address,
x => x.MapFrom(
y => y.EmployeeResidences.Select(
r => new ResidentialAddress1()
{
FullAddress = String.Concat(
r.State, " ", r.City, " ", r.ZipCode)
}).ToList()));
});
SourceEmployee emp = new SourceEmployee()
{
EmployeeID = 1,
FirstName = "Alex",
LastName = "Green",
EmployeeResidences = new List<ResidentialAddress>()
{
new ResidentialAddress() { State = "abc", City = "def", ZipCode = 110 },
new ResidentialAddress() { State = "foo", City = "qwe", ZipCode = 220 },
new ResidentialAddress() { State = "bar", City = "ert", ZipCode = 330 },
}
};
var sourceEmp = Mapper.Map<SourceEmployee, DestinationEmployee>(emp);
Console.WriteLine(sourceEmp.Address.Count);
Console.WriteLine(sourceEmp.Address[1].FullAddress);
Output :
3
foo qwe 220
You should have a separate map profile for your ResidentialAddress -> ResidentialAddress1 transformation. When your parent object will be transformed using automapper, all child objects will get transformed using defined map profiles:
Mapper.CreateMap<SourceEmployee, DestinationEmployee>()
.ForMember(f => f.FullName, f => f.MapFrom(a => string.Concat(a.FirstName, " ", a.LastName)))
.ForMember(x => x.Address, x => x.MapFrom(y => y.EmployeeResidences)));
Mapper.CreateMap<ResidentialAddress, ResidentialAddress1>
.ForMember(x => x.FullAddress, map=>map.From(from => string.Format("{0} {1} {2}", from.State, from.City, from.ZipCode);
This way if in code you will have to transform ResidentialAddress to ResidentialAddress1 in multiple places, you won't have to add any code, just use Mapper.Map<>.
I also suggest you to switch to profiles, instead of defining your mapper configurations inline: https://github.com/AutoMapper/AutoMapper/wiki/Configuration
Not sure that I'm wording this the right way so hopefully the example is clear enough.
What I'm trying to do seems pretty basic to me so I'm assuming I'm missing something obvious.
For this example, the two ForMember mappings are trivial and get the job done. The question is for a more complex class, given any intermediate mappings are configured, how do you simply map the property of one object to the entire destination?
I searched for a while now and the closest I came to finding an answer is here but the ConvertUsing syntax doesn't work for me (I'm using Automapper 4.2.1)
Here are the example classes:
public class UserRoleDto
{
public string Name { get; set; }
public string Description { get; set; }
}
public class DbRole
{
public Guid RoleId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class DbUserRole
{
public Guid UserId { get; set; }
public DbRole Role { get; set; }
}
And here's my test case with the Automapper config setup (testing in LINQPad, that's what the Dump() is for at the end of the last line)
var dbRole = new DbRole { RoleId = Guid.NewGuid(), Name = "Role Name", Description = "Role Description" };
var dbUserRole = new DbUserRole { UserId = Guid.NewGuid(), Role = dbRole };
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<DbRole, UserRoleDto>();
/* Works but verbose for a class with more than a few props */
cfg.CreateMap<DbUserRole, UserRoleDto>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Role.Name))
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Role.Description))
;
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var userRoleDto = mapper.Map<UserRoleDto>(dbUserRole).Dump();
How about passing in the sub-object to be mapped? E.g.
cfg.CreateMap<DbRole, UserRoleDto>();
Then, instead of mapping dbUserRole, you would map dbUserRole.Role.
var userRoleDto = mapper.Map<UserRoleDto>(dbUserRole.Role);
Here is another similar example using the following classes:
public class Person
{
public int person_id;
public int age;
public string name;
}
public class Address
{
public int address_id;
public string line1;
public string line2;
public string city;
public string state;
public string country;
public string zip;
}
public class PersonWithAddress
{
public int person_id;
public int age;
public string name;
public InnerAddress address;
}
public class InnerAddress
{
public string city;
public string state;
public string country;
}
With the following test case:
var person = new Person { person_id = 100, age = 30, name = "Fred Flintstone" };
var address = new Address { address_id = 500, line1 = "123 Main St", line2 = "Suite 3", city = "Bedrock", state = "XY", country = "GBR", zip="90210" };
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, PersonWithAddress>();
cfg.CreateMap<Address, InnerAddress>();
});
var mapper = config.CreateMapper();
var person_with_address = mapper.Map<Person, PersonWithAddress>(person);
person_with_address.address = new InnerAddress();
mapper.Map<Address, InnerAddress>(address, person_with_address.address);
Regards,
Ross