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

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

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.

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.

zend : Fatal error: Class 'Application_Model_User' not found

I am trying connecting to my model class defined in /application/models named user.php.
Following is how my Bootstrap looks like:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap{
protected function _initAppAutoload() {
$autoloader = new Zend_Loader_Autoloader_Resource(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
'resourceTypes' => array(
'model' => array(
'path' => 'models',
'namespace' => 'Model',
)
)
));
echo '<pre>';
var_dump($autoloader);
return $autoloader;
}
}
Folling is my application.ini
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0
Following is my IndexController.php
class IndexController extends Zend_Controller_Action {
private $user_connection;
private $login_status;
public function init() {
/* Initialize action controller here */
$this->user_connection = new Application_Model_User();
}
public function indexAction() {
$sql = " SELECT *
FROM USER";
$this->view->deals = $this->user_connection->select($sql);
}
following is my user.php file:
class Application_Model_User extends Application_Model_Db_Connection {
public function __construct() {
parent::__construct();
}
public function Connection(){
return $this->getConnection();
}
public function insert($data, $table = 'user'){
return parent::insert($table, $data);
}
public function select($sql){
return parent::select($sql);
}
}
Strange part is, I am developing on windows and everything runs fine, but when I push the same code to my ec2 linux instance, I get this fatal error.
Fatal error: Class 'Application_Model_User' not found in /var/www/html/dev/application/controllers/IndexController.php on line 11
I went through many questions on stack overflow and tried most of them, but I am not close to solve this problem. Any help would be appreciated.
I was able to fix the above issue, it was due to case sensitivity of linux. I renamed my models with first letter capital and was able to access them in my Controller.
user.php -> User.php
also for adding subfolder to your model you can add following to the bootstrap.
protected function _initAppAutoload() {
$autoloader = new Zend_Loader_Autoloader_Resource(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
'resourceTypes' => array(
'model' => array(
'path' => 'models/',
'namespace' => 'Model_',
),
'model_db' => array(
'path' => 'models/db',
'namespace' => 'Model_Db_'
)
)
));

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;

Auto Mapper Null Reference

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"
)
)

Resources