Auto Mapper Null Reference - automapper

I have an issue with auto mapper which throws a Null reference exception.
Mapper.CreateMap<People, PeopleDto>()
.ForMember(d => d.Country, opt => opt.MapFrom(o => o.Address.Country))
The problem is when Address is null and trying to get map Address.Country

Mapper.CreateMap()
.ForMember(d => d.Country,
opt => opt.MapFrom(
o => (o.Address != null) ? o.Address.Country : "ADDRESS NOT SPECIFIED"
)
)

Related

How to map multiple entities to one class in Mapster

In AutoMapper, we can map multiple entities to one, but can't impl in mapster.
Automapper demo code:
Entity:
var users = await _userManager.Users
.AsNoTracking()
.ProjectTo<UserDto>(new { roles = _roleManager.Roles })
.ToListAsync();
AutoMapper config:
IQueryable<IdentityRole> roles = null;
CreateMap<User, UserDto>()
.ForMember(x => x.Roles, opt =>
opt.MapFrom(src =>
src.Roles
.Join(roles, a => a.RoleId, b => b.Id, (a, b) => b.Name)
.ToList()
)
);
There is no parameter in Mapster Querable.ProjectTo() method.
Who can help me see what I should do? Thanks.

AutoMapper not working since upgrading from version `7.0.1` to `10.1.1`

I have an existing .NET Framework 4.8 Web API project that uses AutoMapper version 7.0.1.
I have upgraded the project to .NET 6 and the AutoMapper nuget package to version 10.1.1.
It appears that the existing mappings no longer work as they did in version 7.0.1.
The existing mappings class look like this:
public static class AutoMapperConfig
{
public static void CreateMaps()
{
Mapper.Initialize(cfg =>
{
CreateAccountConnectorMaps(cfg);
CreateConnectorMaps(cfg);
});
}
private static void CreateAccountConnectorMaps(IMapperConfigurationExpression cfg)
{
cfg.CreateMap<AccountConnector, AccountConnectorModel>()
.ReverseMap()
.ForMember(m => m.CustomFields, o => o.Ignore())
.ForMember(m => m.Properties, o => o.Ignore())
.ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
}
private static void CreateConnectorMaps(IMapperConfigurationExpression cfg)
{
cfg.CreateMap<Connector, ConnectorModel>()
.ForMember(d => d.Parameters, o => o.MapFrom(s => s.Parameters.Where(p =>
!p.IsPartnerProperty &&
!p.Hide &&
p.Values.None(v => ParameterValues.IsPagingParameter(v.Value)))))
.ForMember(d => d.AuthDescription, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthDescription))
.ForMember(d => d.AuthType, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthType))
.ForMember(d => d.OAuth2Type, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).OAuth2Type))
.ForMember(d => d.AuthScheme, o => o.MapFrom(s => s.Authentication.FirstOrDefault(x => x.IsDefault).AuthScheme))
.ForMember(d => d.IsSystemConnector, o => o.MapFrom(s => s.ProductAddon != null && s.ProductAddon.IsSystemConnector))
.ForMember(d => d.Categories, o => o.MapFrom(s => s.ProductAddon == null ? null : s.ProductAddon.Categories.Select(c => c.Category.Name)))
.ForMember(d => d.PartnerSetupGuideUrl, o => o.MapFrom(s => s.ProductAddon == null ? null : s.ProductAddon.PartnerSetupGuideUrl))
.ForMember(d => d.CustomizableCategories, o => o.MapFrom(s => s.MethodCategories == null ? null : s.MethodCategories.Where(item => item.CanCopyToAccountConnectorMethodCategory)))
.ReverseMap()
.ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
}
private static void IgnoreSourceValuesWithInternalOrPrivateSetters<TSource, TDestination, TMember>(IMemberConfigurationExpression<TSource, TDestination, TMember> member)
{
member.Condition((source, destination, sourceMember, destMember) =>
{
if (sourceMember == null)
{
return false;
}
var sourceProp = source.GetType().GetProperty(member.DestinationMember.Name);
return sourceProp?.GetSetMethod(false) != null;
});
}
}
The new mappings haven't changed except since the version upgrade the AutoMapperConfig class now inherits from AutoMapper.Profile.
The new mappings class looks like this:
public class AutoMapperConfig : Profile
{
public AutoMapperConfig()
{
CreateAccountConnectorMaps();
CreateConnectorMaps();
}
private void CreateAccountConnectorMaps()
{
CreateMap<AccountConnector, AccountConnectorModel>()
.ReverseMap()
.ForMember(src => src.CustomFields, opt => opt.Ignore())
.ForMember(src => src.Properties, opt => opt.Ignore())
.ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
}
private void CreateConnectorMaps()
{
CreateMap<Connector, ConnectorModel>()
.ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => src.Parameters.Where(p =>
!p.IsPartnerProperty &&
!p.Hide &&
p.Values.None(v => ParameterValues.IsPagingParameter(v.Value)))))
.ForMember(dest => dest.AuthDescription, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthDescription))
.ForMember(dest => dest.AuthType, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthType))
.ForMember(dest => dest.OAuth2Type, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).OAuth2Type))
.ForMember(dest => dest.AuthScheme, opt => opt.MapFrom(src => src.Authentication.FirstOrDefault(x => x.IsDefault).AuthScheme))
.ForMember(dest => dest.IsSystemConnector, opt => opt.MapFrom(src => src.ProductAddon != null && src.ProductAddon.IsSystemConnector))
.ForMember(dest => dest.Categories, opt => opt.MapFrom(src => src.ProductAddon == null ? null : src.ProductAddon.Categories.Select(c => c.Category.Name)))
.ForMember(dest => dest.PartnerSetupGuideUrl, opt => opt.MapFrom(src => src.ProductAddon == null ? null : src.ProductAddon.PartnerSetupGuideUrl))
.ForMember(dest => dest.CustomizableCategories, opt => opt.MapFrom(src => src.MethodCategories == null ? null : src.MethodCategories.Where(mc => mc.CanCopyToAccountConnectorMethodCategory)))
.ReverseMap()
.ForAllMembers(IgnoreSourceValuesWithInternalOrPrivateSetters);
}
private void IgnoreSourceValuesWithInternalOrPrivateSetters<TSource, TDestination, TMember>(IMemberConfigurationExpression<TSource, TDestination, TMember> member)
{
member.Condition((source, destination, sourceMember, destMember) =>
{
if (sourceMember == null)
{
return false;
}
var sourceProp = source.GetType().GetProperty(member.DestinationMember.Name);
return sourceProp?.GetSetMethod(false) != null;
});
}
}
N.B. The domain entities and the models are the same across both projects.
In the Web API controller action on the existing .NET Framework Web API project when I try to map an instance of an AccountConnector to the AccountConnectorModel type using:
var mappedModel = Mapper.Map<AccountConnectorModel>(accountConnector);
it maps successfully.
However when I do the same on the new .NET 6 Web API project using:
var mappedModel = _mapper.Map<AccountConnectorModel>(accountConnector);
It throws a mapping exception, with 2 inner exceptions.
Outer Exception
Error mapping types.
Mapping types:
AccountConnector -> AccountConnectorModel
Domain.Connectors.AccountConnector -> Models.AccountConnectorModel
Type Map configuration:
AccountConnector -> AccountConnectorModel
Domain.Connectors.AccountConnector -> Models.AccountConnectorModel
Destination Member:
Connector
Inner Exception 1
Error mapping types.
Mapping types:
Connector -> ConnectorModel
Domain.Connectors.Connector -> Models.ConnectorModel
Type Map configuration:
Connector -> ConnectorModel
Domain.Connectors.Connector -> Models.ConnectorModel
Destination Member:
Authentication
Inner Exception 2
Missing type map configuration or unsupported mapping.
Mapping types:
ConnectorAuthentication -> ConnectorAuthenticationModel
Domain.Connectors.ConnectorAuthentication -> Models.ConnectorAuthenticationModel

FindTypeMapFor method not exists in AutoMapper 11

I am currently upgrading a web API that was developed on NET Core 5.0 and upgrading it to NET Core 6.0. When upgrading the NuGet AutoMapper package to version 11.0.1, I find that the FindTypeMapFor method does not exist in the ConfigurationProvider definition.
public Dictionary<string, PropertyMappingValue> GetPropertyMappingFromAutomapper<TSource, TDestination>(List<string> reverseOrderProperties) where TSource : class where TDestination : class
{
Dictionary<string, PropertyMappingValue> dictionaryPropertyMapping = new(StringComparer.OrdinalIgnoreCase);
if (typeof(TSource).Equals(typeof(ForNotIncludeDto)) || typeof(TSource).Equals(typeof(ForNotSortingDto)) || typeof(TSource).Equals(typeof(ForNotDistinctDto)))
{
return dictionaryPropertyMapping;
}
TypeMap typeMap = this.Mapper.ConfigurationProvider.FindTypeMapFor<TSource, TDestination>();
if (typeMap is null)
{
throw new Exception($"Cannot find exact property mapping instance " + $"for <{typeof(TSource)},{typeof(TDestination)}>");
}
List<PropertyMap> propertyMaps = typeMap.PropertyMaps.Where(x => x.Ignored == false).ToList();
List<PathMap> pathMaps = typeMap.PathMaps.Where(x => x.Ignored == false).ToList();
foreach (MemberInfo member in typeMap.SourceTypeDetails.AllMembers)
{
List<string> originPropertyMap = propertyMaps.Where(x => x.SourceMember is not null && x.SourceMember.Name.Equals(member.Name)).Select(x => x.DestinationName).ToList();
if (originPropertyMap.Count.Equals(0))
{
originPropertyMap = pathMaps.Where(x => x.SourceMember is not null && x.SourceMember.Name.Equals(member.Name)).Select(x => x.DestinationName).ToList();
}
if (originPropertyMap.Count > 0)
{
dictionaryPropertyMapping.Add(member.Name, new PropertyMappingValue(originPropertyMap, reverseOrderProperties.Where(x => x.Equals(member.Name)).Any()));
}
}
return dictionaryPropertyMapping;
}
How can I get the TypeMap object of a specific mapping?
How can I use FindTypeMapFor or some method that will replace it?
This was moved to the "Internal" object on AutoMapper 11.
Here's the new usage:
using AutoMapper.Internal;
Mapper.ConfigurationProvider.Internal().FindTypeMapFor

Issue for a "two dimentionnal 'add another item' " with FormStateInterface::getTriggeringElement()

I'm learning drupal 8. I want create a page who contain a 'two dimensionnal' 'add another item' form. My code works well almost, but I have a strange behavior when I add rooms to a house (there is a strange value in my debug logs from the FormStateInterface::getTriggeringElement(), see to the bottom for the code and log)
First : I have two structures, houses and rooms. The user can create some houses and for each house, he can create some rooms :
When I add some houses, the form works fine :
When I add some rooms to the last house, the form works fine too :
But when I add some rooms to any "no-last" house, the form doesn't work fine (in the screenshot, I click one time to the "add room" in the block house '1', the label of the "house 1" became "house 2" (?!) and on click add 5 rooms (?!) :
Here my code and a strange debug log, I don't explain why I get this value (from the getTriggeringElement() in the room_addMoreSubmit callback and this is the problem I think)
<?php
namespace Drupal\projet\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class HouseForm extends FormBase {
public function getFormId(){
return 'custom_rooms_form';
}
function buildForm(array $form, FormStateInterface $form_state) {
$house_count = $form_state->get('house_count');
if (is_null($house_count)) {
$house_count = 1;
$form_state->set('house_count', $house_count);
}
$form['house'] = array(
//'#tree' => TRUE,
'#prefix' => '<div id="house-replace">',
'#suffix' => '</div>'
);
for ($house_delta = 0; $house_delta < $house_count; $house_delta++) {
if (!isset($form['house'][$house_delta])) {
$room_count[$house_delta] = $form_state->get('room_count_'.$house_delta);
if (is_null($room_count[$house_delta])) {
$room_count[$house_delta] = 1;
$form_state->set('room_count_'.$house_delta, $room_count[$house_delta]);
}
dd($room_count, "room_COUNT");
$form['house'][$house_delta]['room'] = array(
'#type' => 'fieldset',
'#title' => t('house : '.$house_delta),
//'#tree' => TRUE,
'#prefix' => '<div id="room-replace-'.$house_delta.'">',
'#suffix' => '</div>'
);
for ($room_delta = 0; $room_delta < $room_count[$house_delta]; $room_delta++) {
if (!isset($form['house'][$house_delta]['room'][$room_delta])) {
$room = array(
'#type' => 'textfield'
);
$check = array(
'#type' => 'checkbox'
);
$form['house'][$house_delta]['room'][$room_delta] = array(
'#type' => 'fieldset',
'#title' => t('room : '.$house_delta.'.'.$room_delta),
);
$form['house'][$house_delta]['room'][$room_delta]['text'] = $room;
$form['house'][$house_delta]['room'][$room_delta]['check'] = $check;
}
}
$form['house'][$house_delta]['room']['add'] = array(
'#type' => 'submit',
'#name' => 'add',
'#value' => t('Add room'),
'#attributes' => array('class' => array('field-add-more-submit'), 'house_delta' => array($house_delta)),
'#submit' => array(array(get_class($this), 'room_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'room_addMoreCallback'),
'wrapper' => 'room-replace-'.$house_delta,
'effect' => 'fade',
),
);
}
}
$form['house']['add'] = array(
'#type' => 'submit',
'#name' => 'add',
'#value' => t('Add house'),
'#attributes' => array('class' => array('field-add-more-submit')),
'#submit' => array(array(get_class($this), 'house_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'house_addMoreCallback'),
'wrapper' => 'house-replace',
'effect' => 'fade',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Create'),
);
return $form;
}
public function room_addMoreSubmit(array $form, FormStateInterface $form_state) {
dd($form_state->getTriggeringElement(), "room : getTriggeringElement()"); // below, the log when I add a room to the house '1' (result see above with the last screenshot: "the house 1" became "house 2" and one click add 5 rooms)
$house = $form_state->getTriggeringElement()["#array_parents"][1];
$c = $form_state->get('room_count_'.$house) + 1;
$form_state->set('room_count_'.$house, $c);
$form_state->setRebuild(TRUE);
}
public function room_addMoreCallback(array $form, FormStateInterface $form_state) {
$house = $form_state->getTriggeringElement()["#array_parents"][1];
return $form['house'][$house]['room'];
}
public function house_addMoreSubmit(array $form, FormStateInterface $form_state) {
dd($form_state->getTriggeringElement()["#array_parents"], "house : getTriggeringElement()");
$c = $form_state->get('house_count') + 1;
$form_state->set('house_count', $c);
$form_state->setRebuild(TRUE);
}
public function house_addMoreCallback(array $form, FormStateInterface $form_state) {
return $form['house'];
}
}
The log ('dd' in the room_addMoreSubmit) when I click on the "add room" button in the house "1":
When I click on the "add room" button in the house number 1, getTriggeringElement return the array parents of the add button. And, as you can see, the parent is "2" not "1" (the house 1)
So when I click on the "add room" button of the house 1, this is the house "2" which is identified and not the house "1".
I don't understand why...Use the getTriggeringElement is not the good way ?
The solution :
$form['house'][$house_delta]['room']['add'] = array(
'#type' => 'submit',
'#name' => 'add-'.$house_delta,
'#value' => t('Add room'),
'#attributes' => array('class' => array('field-add-more-submit'), 'house_delta' => array($house_delta)),
'#submit' => array(array(get_class($this), 'room_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'room_addMoreCallback'),
'wrapper' => 'room-replace-'.$house_delta,
'effect' => 'fade',
),
);
The unique name was the issue of my problem. So, I change the name attribute.

EntityCollection<T> ToArray() extension method not working in Global.asax.cs

I'm trying to move my Automapper Entity -> ViewModel map definition from one of my controllers to my MVC app's OnApplicationStarted() method. When I copy it, my Entity's EntityCollection property loses access to its ToArray() extension method. When I try to compile, I get an error telling me that there is no method or extension method that matches its signature.
Code:
protected override void OnApplicationStarted()
{
// some Ninject setup code
Mapper.CreateMap<Game, AdminGameViewModel>()
.BeforeMap((s, d) =>
{
int platCount = s.Platforms.Count;
var plats = s.Platforms.ToArray(); // <-- line in question
d.PlatformIDs = new int[platCount];
for (int i = 0; i < platCount; ++i)
{
d.PlatformIDs[i] = plats[i].ID;
}
})
.ForMember(dest => dest.Pros, opt => opt.MapFrom(src => src.Pros.Split(new char[] { '|' })))
.ForMember(dest => dest.Cons, opt => opt.MapFrom(src => src.Cons.Split(new char[] { '|' })))
.ForMember(dest => dest.PlatformIDs, opt => opt.Ignore());
}
Again, this code is straight copied and pasted from my controller, where it compiles and runs fine. I've tried casting to IEnumerable, but that doesn't give me access to the method either.
Add the following using.
using System.Data.Linq;

Resources