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;
Related
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.
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
I am writing a simple application that contains a database of items. The items have a type, manufacturer, model, and a few other properties. I have a implemented three UIPickerView's with MvxPickerViewModel's as outlined in N=19 of the N+1 series for MvvmCross. There is one UIPickerView/MvxPickerViewModel for each the type, the manufacturer, and the model (only one is ever on the screen at a time). However if I update the ItemSource data for a MvxPickerViewModel, the rows that were already visible in the UIPickerView do not refresh until they are scrolled off the screen. The N=19 example, does not update the list of items in the UIPickerView so it isn't clear that the problem didn't exist there. Have I made a mistake or has anyone else experienced this? Is there a work around?
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
NavigationController.NavigationBarHidden = true;
var comparableTableViewSource = new MvxStandardTableViewSource(ComparableLV);
ComparableLV.Source = comparableTableViewSource;
var ManufacturerPicker = new UIPickerView();
var manufacturerPickerModel = new MvxPickerViewModel(ManufacturerPicker);
ManufacturerPicker.Model = manufacturerPickerModel;
ManufacturerPicker.ShowSelectionIndicator = true;
ManufacturerTextField.InputView = ManufacturerPicker;
var ModelPicker = new UIPickerView();
var modelPickerModel = new MvxPickerViewModel(ModelPicker);
ModelPicker.Model = modelPickerModel;
ModelPicker.ShowSelectionIndicator = true;
ModelTextField.InputView = ModelPicker;
var TypePicker = new UIPickerView();
var typePickerModel = new MvxPickerViewModel(TypePicker);
TypePicker.Model = typePickerModel;
TypePicker.ShowSelectionIndicator = true;
TypeTextField.InputView = TypePicker;
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(comparableTableViewSource).For(s => s.ItemsSource).To(vm => vm.Comparables);
set.Bind(manufacturerPickerModel).For(p => p.ItemsSource).To(vm => vm.Manufacturers);
set.Bind(manufacturerPickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedManufacturer);
set.Bind(ManufacturerTextField).To(vm => vm.SelectedManufacturer);
set.Bind(modelPickerModel).For(p => p.ItemsSource).To(vm => vm.Models);
set.Bind(modelPickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedModel);
set.Bind(ModelTextField).To(vm => vm.SelectedModel);
set.Bind(typePickerModel).For(p => p.ItemsSource).To(vm => vm.Types);
set.Bind(typePickerModel).For(p => p.SelectedItem).To(vm => vm.SelectedType);
set.Bind(TypeTextField).To(vm => vm.SelectedType);
set.Apply();
var g = new UITapGestureRecognizer(() => {
HornTextField.ResignFirstResponder();
ManufacturerTextField.ResignFirstResponder();
ModelTextField.ResignFirstResponder();
});
View.AddGestureRecognizer(g);
}
Looking at MvxPickerViewModel.cs I'm suspicious that there is no call to ReloadAllComponents (or to ReloadComponent[0]) when the ItemsSource itself changes, but there is a call when the Collection internally changes.
As a workaround, perhaps try a subclass like:
public class MyPickerViewModel
: MvxPickerViewModel
{
private readonly UIPickerView _pickerView;
public MyPickerViewModel(UIPickerView pickerView)
: base(pickerViww)
{
_pickerView = pickerView;
}
[MvxSetToNullAfterBinding]
public override IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
base.ItemsSource = value;
if (value != null)
_pcikerView.ReloadComponent(0);
}
}
}
Would also be great to get a fix back into MvvmCross...
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_'
)
)
));
I am trying to work out how to populate Foo from FooConfig:
class Bar
{
int A;
int B;
}
class Foo
{
Bar BarValues;
Bar BarErrorValues;
}
class FooConfig
{
int A;
int B;
int AError;
int BError;
}
FooConfig is essentially a flattened version of Foo.
I can populate the BarValues easily with AutoMapper (by having a map defined for FooConfig to Bar), but I am having trouble getting the BarErrorValues populated - everything I try it ends up with the A and B values in it instead of the AError and BError values.
I suspect I need to use a "transformer" or RecognizePostfixes (although this does not seem to work as I tried it).
This should work "out of the box":
Mapper.CreateMap<FooConfig, Foo>()
.ForMember(d => d.BarValues, o => o.MapFrom(s => new Bar { A = s.A, B = s.B }))
.ForMember(d => d.BarErrorValues, o => o.MapFrom(s => new Bar {A = s.AError, B = s.BError}));
I think I have found the problem. It appears that if AutoMapper finds an exact match then the name transformer is never used. Bugger :( Here is the AutoMapper source code that shows it:
private static MemberInfo FindTypeMember(IEnumerable<MemberInfo> modelProperties, IEnumerable<MethodInfo> getMethods, string nameToSearch, IMappingOptions mappingOptions)
{
MemberInfo pi = modelProperties.FirstOrDefault(prop => NameMatches(prop.Name, nameToSearch));
if (pi != null)
return pi;
...
pi = modelProperties.FirstOrDefault(prop => NameMatches(mappingOptions.SourceMemberNameTransformer(prop.Name), nameToSearch));
if (pi != null)
return pi;