How to map multiple entities to one class in Mapster - automapper

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.

Related

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

Edit value on mapping with AutoMapper

I'm trying to refactor my project and use automapper to map view model to entity model. Here is my my current code. I have used Guid.NewGuid(), GetValueOrDefault() and DateTime.Now. How can I edit those value on mapping?
var product = new Product
{
Id = Guid.NewGuid(),
Name = model.Name,
Price = model.Price.GetValueOrDefault(),
ShortDescription = model.ShortDescription,
FullDescription = model.FullDescription,
SEOUrl = model.SEOUrl,
MetaTitle = model.MetaTitle,
MetaKeywords = model.MetaKeywords,
MetaDescription = model.MetaDescription,
Published = model.Published,
DateAdded = DateTime.Now,
DateModified = DateTime.Now
};
then here is my map code
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Product, ProductCreateUpdateModel>().ReverseMap();
});
Tell me if I understood you right. You want to create AutoMapper configuration, but some of the properties you want to map manually? In this case, you can do following:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Product, ProductCreateUpdateModel>()
.ReverseMap()
.ForMember(product => product.Price, expression => expression.MapFrom(model => model.Price.GetValueOrDefault()))
.ForMember(product => product.DateAdded, expression => expression.UseValue(DateTime.Now))
.ForMember(product => product.DateModified, expression => expression.UseValue(DateTime.Now));
});
If not, please, specify your question.

Changing content of MvxPickerViewModel

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...

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