ASP.NET using AutoMapper - c#

I am learning ASP.NET by examples from the book on C# and that book was written at time when AutoMapper supported the method Initialize. I tried to circumvent it but not successfully. I replaced :
// Mapper.Initialize(
// cfg =>
// {
// cfg.CreateMap<Inventory, Inventory>()
//.ForMember(x => x.Orders, opt => opt.Ignore());
// });
with:
var config = new MapperConfiguration(cfg => cfg.CreateMap<Inventory, Inventory>()
.ForMember(x => x.Orders, opt => opt.Ignore()));
and it seems (at least do not show errors in VS 2019).
But in the following:
// GET: api/Inventory
[HttpGet, Route("")]
public IEnumerable<Inventory> GetInventory()
{
var inventories = _repo.GetAll();
return Mapper.Map<List<Inventory>, List<Inventory>>(inventories);
}
I get an error:
An object reference is required for the non-static field, method, or property 'Mapper.Map<Inventory, Inventory>(Inventory)
Can somebody help me? Thanks. I am a newcomer here. Jiri

Try this
return Mapper.Map<List<Inventory>>(inventories);

Related

C# Automapper conditional mapping of child properties

I know, there are tons of questions related to this topic, but I couldn't find an answer to what I was looking for. All the questions seem to be related to the top level properties, and what I'm looking for is a conditional mapping for child properties.
In the official documentation itself, there's no such example either, unfortunately.
Here's an example that they have:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0)));
});
In my case, I want something like this:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForMember(dest => dest.baz.foo, opt => opt.Condition(src => (src.baz.foo >= 0)));
});
But this is ofc throwing an exception, because you can't use "ForMember" for child properties. Instead you need to use "ForPath". What I'd expect, is to have similar code, that will work just fine for the child properties the following way:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForPath(dest => dest.baz.foo, opt => opt.Condition(src => (src.baz.foo >= 0)));
});
But this code won't even compile, since the signature differes for the "Condition" method in "ForPath". So I've adjusted it to based on what the signature wanted and added "Source", before accessing the property, the following way:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForPath(dest => dest.baz.foo, opt => opt.Condition(src => (src.Source.baz.foo >= 0)));
});
And even though the code complies and looks okay to me, the conditional mapping does not work. It's being ignored and the property is being set all the time, no matter what the value of "foo" is.
I have ended up with a working code, though. I took an alternative path to get what I need, using "AfterMap" method:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForPath(dest => dest.baz, opt => opt.MapFrom(src => (src.Source.baz)))
.AfterMap((dest, src) =>
{
// Resetting the value of the property I don't need based on the reversed condition
if (src.baz.foo < 0)
{
src.baz.foo = default;
}
}
});
Even though I got it working, I want to know how to achieve the same result with conditional mapping? And what am I doing wrong?

How to ignore null values in C# Automapper

I am using Automapper in C#.
Currently I am trying to map using below code.
CreateMap<Student, StudentDto>()
.ForMember(dest => dest.FeeType, opt =>
{
opt.PreCondition(src => (src.Key == "Paid"));
opt.MapFrom(src => src.Value);
});
I want to map only if the key == Paid else it should not map. However in this case , it is just passing null.
So lets say if the collection has 100 records and only 1 matches the condition other 99 records are passed as NULL.
Edit -- I am using Azure Function Apps and my Automapper version is 10.0.0
Please advise.
I think you have to update your ConfigureServices like=>
public void ConfigureServices(IServiceCollection services) {
// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc => {
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
//This line should be updated
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(options => options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore);
}
.AddJsonOptions(options => options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore); ofNewtonsoft.Json.NullValueHandling.Ignore, It will handle what you ask for.
Note: Please check the link of a documentation. I believe it will do the trick.
LINK

AutoMapper Projections are not applied

I'm trying to make a framework to abstract my Entity Layers, However for this to work I need automapper projects to work so I can Query my DTO's instead of Querying the Entities
[TestMethod]
public async Task Verify_Mapping_Projection_Behavior()
{
var projectionModifier = "Alabastar";
var services = this.GetRegisteredRestEzServiceCollection();
var serviceProvider = services.BuildServiceProvider();
var context = (AstootContext)serviceProvider.GetService<DbContext>();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDTO>().ForMember(x => x.FirstName,
o => o.MapFrom((entity, dto) => entity.FirstName + projectionModifier));
});
var mapper = config.CreateMapper();
// Hack: we'll verify exeuction happens in sql
// using the behavioral differene between in memory and sql (case insensitivity)
var sqlOnlyModifier = projectionModifier.ToUpper();
var userDTO = mapper.ProjectTo<UserDTO>(context.Users)
.Where(x => x.FirstName.Contains(sqlOnlyModifier))
.FirstOrDefault();
Assert.IsNotNull(userDTO);
}
My test failed, so I decided to materialize the projection directly. When I materialize I can see that my projectionModifier is not being added to the firstName property.
How can I get the project to map my modifier, so that I can use my DTO's as Sql Queryables?
The 3 parameter overload for MapFrom requires assignment. This can be resolved by using the 2 parameter overload.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDTO>()
.ForMember(dto => dto.FirstName,
opt => opt.MapFrom(entity => entity.FirstName + projectionModifier));
});

Automapper for member condition

I am using auto mapper 6.1 and I want to map some values from one object to another, but there is a condition that those values can not be null and not all object properties are supposed to be mapped if so I could easily use ForAllMembers conditions. What I am trying to do is:
config.CreateMap<ClassA, ClassB>()
.ForMember(x => x.Branch, opt => opt.Condition(src => src.Branch != null),
cd => cd.MapFrom(map => map.Branch ?? x.Branch))
Also tried
config.CreateMap<ClassA, ClassB>().ForMember(x => x.Branch, cd => {
cd.Condition(map => map.Branch != null);
cd.MapFrom(map => map.Branch);
})
In another words for every property I define in auto mapper configuration I want to check if its null, and if it is null leave value from x.
Call for such auto mapper configuration would look like:
ClassA platform = Mapper.Map<ClassA>(classB);
If I've understood correctly, it may be simpler than you think. The opt.Condition is not necessary because the condition is already being taken care of in MapFrom.
I think the following should achieve what you want: it will map Branch if it's not null. If Branch (from the source) is null, then it will set the destination to string.Empty.
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => map.Branch ?? string.Empty));
And if you need to use another property from x instead of string.Empty, then you can write:
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => map.Branch ?? x.AnotherProperty));
If you want to implement complex logic but keep the mapping neat, you can extract your logic into a separate method. For instance:
config.CreateMap<ClassA, Class>()
.ForMember(x => x.Branch, cd => cd.MapFrom(map => MyCustomMapping(map)));
private static string MyCustomMapping(ClassA source)
{
if (source.Branch == null)
{
// Do something
}
else
{
return source.Branch;
}
}
You don't need the MapFrom, but you need a PreCondition instead. See here.

Replace Mapping with ConstructUsing

I make a mapping using Automapper 3.2.1:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ForMember(dst => dst.IDAvviso, src => src.MapFrom(v => EstraiIdAvviso(v)))
.ForMember(dst => dst.CodiceErrorePiattaforma, src => src.MapFrom(v => EstraiCodiceErrorePiattaforma(v.History)))
.ForMember(dst => dst.DescrizioneErrorePiattaforma, src => src.MapFrom(v => EstraiDescrizioneErrorePiattaforma(v.History)))
.ForMember(dst => dst.CodiceEsitoPostalizzazione, src => src.MapFrom(v => EstraiEsitoPostalizzazione(v.History)))
And this works good. now I want to remove parameterless construction in order to use a better encapsulation::
public EsitiPostalizzazione(int IDAvviso, int CodiceEsitoPostalizzazione, String CodiceErrorePiattaforma, String DescrizioneErrorePiattaforma)
{
this.IDAvviso = IDAvviso;
this.CodiceEsitoPostalizzazione = CodiceEsitoPostalizzazione;
this.CodiceErrorePiattaforma = CodiceErrorePiattaforma;
this.DescrizioneErrorePiattaforma = DescrizioneErrorePiattaforma;
}
so as far as I documented I should use the ConstructUsingafter the createMap() but I don't know how to use it, can someone help me?
All the example on the web will make something like:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ConstructUsing(x => new EsitiPostalizzazione(x.IdAvviso, ...))
but I have nothing like x.IDAvviso
The x in the ConstructUsing lambda should be your source type, which is AvvisoPec. So when you're using the EsitiPostalizzazione constructor you need to provide the values the way you were doing when using the MapFrom syntax.
Based on your code I would expect it to be:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ConstructUsing(v => new EsitiPostalizzazione(EstraiIdAvviso(v),
EstraiEsitoPostalizzazione(v.History),
EstraiCodiceErrorePiattaforma(v.History),
EstraiDescrizioneErrorePiattaforma(v.History)));

Categories