I have question about Automapper. This is my usecase:
I have these classes:
Bussiness:
public class Item
{
public NestedItem NestedItem{get;set;}
}
public class NestedItem
{
public string Name{get;set;}
}
ViewModel:
public class ItemViewModel
{
public string NestedItemName{get;set;}
}
When I want to map Item to ItemViewModel. Automapper map property NestedItem.Name to NestedItemName correctly.
But in the opposite way it doesn´t work. Mapping doesn´t change value of NestedItem.Name.
I've got a slightly contradictory answer to the link stated above, maybe I just go about my things the awkward way but I believe you can do it within one map. For example:
Mapper.CreateMap<ItemViewModel, Item>()
.ForMember(dest => dest.NestedItem, opt =>
opt.MapFrom(src => new NestedItem(src.NestedItemName));
Now of course this would require you to create a new constructor or add a blank constructor and use curly brackets.
I can't comment on performance as AutoMapper is a seriously complicated monster. However this should do the trick.
Related
How can I map a List<string> to List<Class>?
Usecase: from the Webservice I'm getting a class with a list of string but in my MVC Viewmodel, I want to have Class instead with a single property, which has the value of the string. That way I can add Validation attributes to the property.
I have the way how I convert the List into a List, however I can't get the other way around to work.
Any simple solutions?
The way to do this with AutoMapper is to use .ConstructUsing:
Mapper.CreateMap<string, Class>()
.ConstructUsing(str => new Class { MyProp = str });
Example: https://dotnetfiddle.net/0Vlc8b
You can easily do it with Linq.
var newList = oldList.Select(x => new Item(x)).ToList();
You could do this:
void Main()
{
AutoMapper.Mapper.CreateMap<string,A>()
.ForMember(a => a.Name, m => m.MapFrom(s => s));
new[] {"A", "B"}.Select (AutoMapper.Mapper.Map<A>).Dump();
}
class A
{
public string Name { get; set; }
}
(Linqpad code)
But I think this can go down as a textbook example of over-engineering. Just do it as in Daniel's example.
I have a button in View "A" which already has a bindingSet attached to it (it binds to ViewModel "A"). I have button though which needs to be bound to ViewModel "B".
What is the best way to do this?
Your ViewModel is your Model for your View.
If that ViewModel is made up of parts, then that can be done by aggregation - by having your ViewModel made up of lots of sub-models - e.g:
// simplified pseudo-code (add INPC to taste)
public class MyViewModel
{
public MainPartViewModel A {get;set;}
public SubPartViewModel B {get;set;}
public string Direct {get;set;}
}
With this done, then a view component can be bound to direct sub properties as well as sub properties of sub view models:
set.Bind(button).For("Title").To(vm => vm.Direct);
set.Bind(button).For("TouchUpInside").To(vm => vm.A.GoCommand);
set.Bind(button).For("Hidden").To(vm => vm.B.ShouldHideThings);
As long as each part supports INotifyPropertyChanged then data-binding should "just work" in this situation.
If that approach doesn't work for you... In mvvmcross, you could set up a nested class within the View that implemented IMvxBindingContextOwner and which provided a secondary binding context for your View... something like:
public sealed class Nested : IMvxBindingContextOwner, IDisposable {
public Nested() { _bindingContext = new MvxBindingContext(); }
public void Dispose() {
_bindingContext.Dispose();
}
private MvxBindingContext _bindingContext;
public IMvxBindingContext BindingContext { get { return _bindingContext; } }
public Thing ViewModel {
get { return (Thing)_bindingContext.DataContext; }
set { _bindingContext.DataContext = value; }
}
}
This could then be used as something like:
_myNested = new Nested();
_myNested.ViewModel = /* set the "B" ViewModel here */
var set2 = _myNested.CreateBindingSet<Nested, Thing>();
// make calls to set2.Bind() here
set2.Apply();
Notes:
I've not run this pseudo-code, but it feels like it should work...
to get this fully working, you will also want to call Dispose on the Nested when Dispose is fired on your View
given that Views and ViewModels are normally written 1:1 I think this approach is probably going to be harder to code and to understand later.
Just started using Value Injector: http://valueinjecter.codeplex.com/, and I have a question:
I have a Source class that has many properties. Some properties have a common suffix of "Profile". These properties are not always String, but mostly are. Target has 1 property, Dictionary<string, string>. I would like all properties that end with "Profile" to be inserted into the Dictionary<string, string> with key = PropertyName and value = Property's value. I think it can be done but the documentation isn't very clear to me. Can someone point me to the right direction? Thanks!
Sorry I have no experience with Value Injector, but if you chose to use AutoMapper you would most likely use a Custom Resolver:
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Profile, opt => opt.ResolveUsing<ProfileResolver>());
Where the resolver would look something like:
public class ProfileResolver : ValueResolver<Source, Dictionary<string, string>>
{
protected override int ResolveCore(Source source)
{
var rc = new Dictionary<string, string>();
// Do some funky reflection stuff here
return rc;
}
}
Full details can be found in the custom resolver documentation.
I have an mvc controller which has a helper class injected into it. I would like to convert from a viewmodel to a dto using automapper. most of the properties are simple mappings but one involves calling the helper class with a parameter from the viewmodel. Ideally I would want to do something like this:
Mapper.CreateMap<TheViewModel, TheDto>()
.ForMember(dest => dest.Url, o => o.MapFrom(src => _urlHelper.GenerateUrlFromUsername(src.Username)));
...but I cannot because I cannot access a non-static field.
What is the best approach?
EDIT:
OK, so I have a custom resolver but how do I hook this in to my IoC container?
public class CustomResolver : ValueResolver<TheViewModel, string>
{
private readonly IUrlHelper _urlHelper;
public CustomResolver(IUrlHelper urlHelper)
{
_urlHelper = urlHelper;
}
protected override string ResolveCore(TheViewModel source)
{
return _urlHelper.GenerateUrlFromUsername(source.Username);
}
}
Use a custom resolver in this case:
http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers
Custom resolvers can be instantiated from a container, so you can get whatever instance fields of services you need.
Is it possible to use AutoMapper with Immutable types?
For example my Domain type is immutable and I want to map my view type to this.
I believe it is not but just want this confirmed.
Also as it is best practice to have your domain types immutable, what is the best practice when mapping your view types to domain types?
I typically do the mapping from view types to domain types by hand, as I'll typically be working through a more complex interface, using methods and so on. If you use AutoMapper to go from view to domain, you're now locked in to an anemic domain model, whether you've intentionally decided to or not.
Suppose that you really did want an immutable property on your Domain type, say Id. Your domain type might look something like this:
public class DomainType
{
public DomainType(int id)
{
Id = id;
}
public int Id { get; }
// other mutable properties
// ...
}
Then you can use ConstructUsing using a public constructor of your choice, such as:
CreateMap<ViewType, DomainType>()
.ConstructUsing(vt => new DomainType(vt.Id));
Then map all the mutable properties in the normal way
AutoMapper relies on property setters to do its work, so if you have read-only properties, AutoMapper won't be of much use.
You could override the mapping behaviour and, for example, configure it to invoke a specific constructor, but that basically defeats the purpose of AutoMapper because then you are doing the mapping manually, and you've only succeeded in adding a clumsy extra step in the process.
It doesn't make a lot of sense to me that your domain model is immutable. How do you update it? Is the entire application read-only? And if so, why would you ever need to map to your domain model as opposed to from? An immutable domain model sounds... pretty useless.
P.S. I'm assuming that you mean this AutoMapper and not the auto-mapping feature in Fluent NHibernate or even some other totally different thing. If that's wrong then you should be more specific and add tags for your platform/language.
We have immutable objects using the builder pattern. Mapping them takes a little more boilerplate code, but it is possible
// ViewModel
public class CarModel : IVehicleModel
{
private CarModel (Builder builder)
{
LicensePlate = builder.LicensePlate;
}
public string LicensePlate { get; }
//
public Builder
{
public string LicensePlate { get; set; }
}
}
// Model
public class CarViewModel : IVehicleViewModel
{
private CarViewModel (Builder builder)
{
LicensePlate = builder.LicensePlate ;
}
public ILicensePlate LicensePlate { get; }
//
public Builder
{
public ILicensePlate LicensePlate { get; set; }
}
}
Our AutoMapper Profiles have three mappings registered:
CreateMap<IVehicleModel, CarViewModel.Builder>();
CreateMap<CarViewModel.Builder, IVehicleViewModel>().ConvertUsing(x => x.Build());
CreateMap<IVehicleModel, IVehicleViewModel>().ConvertUsing<VehicleModelTypeConverter>();
The VehicleModelTypeConverter then defines a two stage conversion:
public IVehicleViewModel Convert(IVehicleModel source, IVehicleViewModel destination,
ResolutionContext context)
{
var builder = context.Mapper.Map<CarViewModel.Builder>(source);
var model = context.Mapper.Map<IVehicleViewModel>(builder);
return model;
}
(An implementation of ITypeListConverter<string, ILicensePlate> carries out that mapping).
Usage in our system is as normal:
var result = _mapper<IVehicleViewModel>(_carModel);
This is using AutoMapper v7.0.1
You can use Automapper with classes or records that have properties init only setters. This is new in C# 9.0.
Automapper can set the properties at object creation because the properties have init only setters, but after Automapper has mapped them, they are locked in (immutable).
https://www.tsunamisolutions.com/blog/c-90-records-and-dtos-a-match-made-in-redmond