Automapper - Map array of objects to one single object - c#-4.0

I would like map array of objects to one single object.
For example:
public class ArrayData
{
//name of property in class MyObject in upper under score casing
public string PropName{get;set;}
//value of property in class MyObject
public string PropValue{get;set;}
}
Source data:
ArrayData [] sourceData = new ArrayData[]{new ArrayData{PropName="MY_ID",PropValue="1"}}
Destination object:
public class MyObject
{
public int MyId{get;set;}
}
My aim is set MyObject.MyId to 1.
Convention is:
if(ArrayData.PropName == MyObject.Property.Name)
{
MyObject.PropName = ArrayData.PropValue;
}
EDITED: I tried this way:
public class UpperUnderscoreNamingConvention : INamingConvention
{
#region Implementation of INamingConvention
public Regex SplittingExpression
{
get { throw new System.NotImplementedException(); }
}
public string SeparatorCharacter
{
get { return string.Format("_"); }
}
#endregion
}
public class TestProfile: Profile
{
public override string ProfileName { get { return "TestProfile"; } }
protected override void Configure()
{
SourceMemberNamingConvention = new UpperUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<ArrayData, MyObject>()
.ForMember(dest => dest.MyId, opt =>
{
opt.Condition(src => src.ColumnName == "MY_ID");
opt.MapFrom(src => src.Value);
});
}
}
Conversion:
Mapper.Initialize((obj) => obj.AddProfile(new TestProfile()));
var myClass = Mapper.Map<ArrayData[], MyClass>(sourceData);
It doesnt work, I get this exception:
AutoMapper.AutoMapperMappingException: Missing type map configuration
or unsupported mapping.
Also I think it is not good solution map all properties manualy:
.ForMember(dest => dest.MyId, opt => opt.Condition(src => src.ColumnName =="MY_ID"))

Related

Automapper - v5.2.0 Nullsubstitute String working with Map but not with ProjectTo

I tried to use AutoMapper NullSubstitute feature with source member and destination member of type string.
It doesn't seem to work with Projection.
As an example, I adapted src/UnitTests/Projection/NullSubstitutes.cs.
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using QueryableExtensions;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.AsQueryable();
_dests = source.ProjectTo<Dest>(Configuration).ToList();
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
Expected : "5" equals "5"
Actual : "5" equals null
With Map everything is ok
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.ToList();
_dests = Configuration.CreateMapper().Map<List<Dest>>(source);
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
It looks a bit like what was described in https://github.com/AutoMapper/AutoMapper/issues/642

Ignore member of base class in YamlDotNet

I have a class which I want to serialize with YamlDotNet:
public class AwesomeClass : PropertyChangedBase
{
private bool _element1;
private bool _enabled;
public bool Element1
{
get { return _element1; }
set
{
_element1 = value;
NotifyOfPropertyChange(() => Element1);
}
}
public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
NotifyOfPropertyChange(() => Enabled);
}
}
}
My problem is, in the base class is an element named: IsNotifying
Is there a way to exclude this element from serialization, without the change of the base class?
You could override the property in the derived class and apply the YamlIgnore attribute there. While the sample below works, I suspect for more complicated class hierarchies you would really need to ensure no behavior changes.
public class AwesomeClass : PropertyChangedBase
{
[YamlIgnore]
public new bool IsNotifying
{
get { return base.IsNotifying; }
set { base.IsNotifying = value; }
}
[YamlIgnore]
public override bool Blah
{
get { return base.Blah; }
set { base.Blah = value; }
}
}
public class PropertyChangedBase
{
public bool IsNotifying
{
get;
set;
}
public virtual bool Blah
{
get;
set;
}
}
I had a similar problem (needed to filter properties of a particular type from classes I couldn't change, so using the attribute was not an option) and is what I came up with:
Create a custom type inspector:
public class MyTypeInspector : TypeInspectorSkeleton
{
private readonly ITypeInspector _innerTypeDescriptor;
public MyTypeInspector(ITypeInspector innerTypeDescriptor)
{
_innerTypeDescriptor = innerTypeDescriptor;
}
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
{
var props = _innerTypeDescriptor.GetProperties(type, container);
props = props.Where(p => !(p.Type == typeof(Dictionary<string, object>) && p.Name == "extensions"));
props = props.Where(p => p.Name != "operation-id");
return props;
}
}
Create the serializer as follows:
var builder = new SerializerBuilder();
builder.WithTypeInspector(inspector => new MyTypeInspector(inspector));
var serializer = builder.Build();

Problems mapping a type that inherits from IEnumerable

I have a problem mapping a property containing a custom list that inherits from IEnumerable (if i remove that inheritance, this example works). I have simplified the problem into this model:
public interface IMyEnumerable<T> : IEnumerable<T> { }
public class MyIEnumerable<T> : IMyEnumerable<T>
{
private readonly IEnumerable<T> _items;
public MyIEnumerable(IEnumerable<T> items)
{
_items = items;
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Source
{
public List<SourceItem> Items { get; set; }
}
public class Destination
{
public IMyEnumerable<DestinationItem> Items { get; set; }
}
public class SourceItem
{
public string Name { get; set; }
}
public class DestinationItem
{
public string Name { get; set; }
}
Then i try to use is this way:
public class MyResolver : ValueResolver<Source, IMyEnumerable<DestinationItem>>
{
protected override IMyEnumerable<DestinationItem> ResolveCore(Source source)
{
var destinationItems = Mapper.Map<List<SourceItem>, IEnumerable<DestinationItem>>(source.Items);
return new MyIEnumerable<DestinationItem>(destinationItems);
}
}
// Mappings
Mapper.CreateMap<Source, Destination>()
.ForMember(x => x.Items, m => m.ResolveUsing<MyResolver>());
Mapper.CreateMap<SourceItem, DestinationItem>();
// Using the mappings
var source = // not really relevant
var destination = Mapper.Map<Destination>(source);
This gives me the following exception (slightly edited for readability):
Mapping types:
MyIEnumerable`1 -> IMyEnumerable`1
MyIEnumerable`1[[DestinationItem]] -> IMyEnumerable`1[[DestinationItem]]
Destination path:
Destination.Items.Items
Source value:
MyIEnumerable`1[DestinationItem]
----> System.ArgumentException : Object of type System.Collections.Generic.List`1[DestinationItem] cannot be converted to type IMyEnumerable`1[DestinationItem].
Any idea how i can fix the mapping so that i can get this to work?
Assuming the following:
var source = new Source
{
Items = new List<SourceItem>
{
new SourceItem { Name = "foo" },
new SourceItem { Name = "bar" },
new SourceItem { Name = "cow" },
}
};
Then the following work:
// Method 1: Straight up mapping the collections:
Mapper.CreateMap<List<SourceItem>, IMyEnumerable<DestinationItem>>()
.ConstructUsing(list => new MyEnumerable<DestinationItem>(list.ConvertAll(Mapper.Map<SourceItem, DestinationItem>)));
// Method 2: Ignore the property and do it ourselves after the rest of the mapping:
Mapper.CreateMap<Source, Destination>()
.ForMember(q => q.Items, r => r.Ignore())
.AfterMap((s, d) => d.Items = new MyEnumerable<DestinationItem>(
s.Items.Select(Mapper.Map<SourceItem, DestinationItem>)));
Nothing else seems to work due to some combination of covariance and contravariance between List<T>, IEnumerable<T> and IMyEnumerable<T>

map to derived type of destination in AutoMapper

I want to specify a mapping so that all mappings from "Source" to "Destination" is returned as a derived class of "Destination"
[Test]
public void Map_SourceToDestinationAsDerivedType_ReturnsDerivedType()
{
// arrange
AutoMapper.Mapper.CreateMap<Source, Destination>()
.CreateAs<ActualDestination>() // psedu code
.ForMember(dst => dst.Transformed, opt => opt.ResolveUsing(src => src.Property));
var source = new Source{Property = "hi mom" };
// act
var destination = AutoMapper.Mapper.Map<Destination>(source);
// assert
Assert.That(destination, Is.InstanceOf<ActualDestination>());
}
public class Source
{
public string Property { get; set; }
}
public class Destination
{
}
public class ActualDestination : Destination
{
public string Transformed { get; set; }
}
This is not directly supported by Automapper
However the closest what you can get is to define a mapper for the Source, ActualDestination pair
AutoMapper.Mapper.CreateMap<Source, ActualDestination>()
.ForMember(dst => dst.Transformed, opt => opt.ResolveUsing(src => src.Property));
And then use the ConstructUsing option in the Source, Destination mapping to do the translation from the Source to the ActualDestination:
AutoMapper.Mapper.CreateMap<Source, Destination>()
.ConstructUsing((Source s) => AutoMapper.Mapper.Map<ActualDestination>(s));

Get BindingTarget in nested MvxDialogViewController in MvxCollectionViewCell

I have a ViewModel called LocationsViewModel, in which I have a ObservableCollection<LocationViewModel>. Additionally I have a LocationsView, which is an MvxCollectionViewController, in which I create a binding set and bind a MvxCollectionViewSource to the ObservableCollection.
In the LocationCell, which is a MvxCollectionViewCell, I want to display a MonoTouch.Dialog which is bound to various properties in the currently
selected LocationViewModel. The easiest way seems to be to create a nested MvxDialogViewController in the MvxCollectionViewCell, however to bind the
Elements in the MvxDialogViewController, I obviously need to create a Binding Target. My question is really can I pass a binding target from the MvxCollectionViewCell to the MvxDialogViewController?
Let me also try to explain it briefly with some code to improve the understanding.
LocationsViewModel.cs
public class LocationsViewModel : MvxViewModel
{
...
public ObservableCollection<LocationViewModel> Locations
{
get { return _locationDataService.Locations.Locations; }
}
...
}
LocationViewModel.cs
public class LocationViewModel : MvxNotifyPropertyChanged
{
...
//Tons of public properties like:
public string Name
{
get { return LinkedDataModel.Name; }
set
{
LinkedDataModel.Name = value;
RaisePropertyChanged(() => Name);
}
}
public double CurrentNoiseLevel
{
get { return LinkedDataModel.CurrentNoiseLevel; }
set
{
LinkedDataModel.CurrentNoiseLevel = value;
RaisePropertyChanged(() => CurrentNoiseLevel);
}
}
...
}
LocationsView.cs
[Register("LocationView")]
public class LocationsView
: MvxCollectionViewController
{
static readonly NSString LocationCellId = new NSString("LocationCell");
private readonly bool _isInitialized;
public LocationsView()
: base(new UICollectionViewFlowLayout()
{
MinimumInteritemSpacing = 0f,
ScrollDirection = UICollectionViewScrollDirection.Horizontal,
MinimumLineSpacing = 0f
})
{
_isInitialized = true;
ViewDidLoad();
}
public new LocationsViewModel ViewModel
{
get { return (LocationsViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
public sealed override void ViewDidLoad()
{
if (!_isInitialized)
return;
base.ViewDidLoad();
CollectionView.RegisterClassForCell(typeof(LocationCell), LocationCellId);
var source = new MvxCollectionViewSource(CollectionView, LocationCellId);
CollectionView.Source = source;
CollectionView.PagingEnabled = true;
var set = this.CreateBindingSet<LocationsView, LocationsViewModel>();
set.Bind(source).To(vm => vm.Locations);
set.Apply();
CollectionView.ReloadData();
}
}
LocationCell.cs
public class LocationCell : MvxCollectionViewCell
{
[Export("initWithFrame:")]
public LocationCell(RectangleF frame)
: base(string.Empty, frame)
{
InitView();
}
public LocationCell(IntPtr handle)
: base(string.Empty, handle)
{
InitView();
}
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
}
public class LocationCellDialog
: MvxDialogViewController
{
public LocationCellDialog()
: base(UITableViewStyle.Grouped, null, true)
{ }
public override void ViewDidLoad()
{
base.ViewDidLoad();
//How do I get the target here?
var target = ??;
Root = new RootElement
{
new Section
{
new StringElement().Bind(target, t => t.Name),
new StringElement().Bind(target, t => t.CurrentNoiseLevel)
}.Bind(target, t => t.Name),
};
}
}
}
So the question is can I simply pass along a binding target from the parent LocationCell to nested LocationCellDialog or is that a no go?
Each bindable view in MvvmCross has its own DataContext
For a top level View this DataContext is the ViewModel
For a Cell within a List, Table or Collection then the DataContext is set to the object in the list which the Cell is currently showing.
If you want to data-bind any property within a Cell to a property path on the DataContext then you can do so using the Fluent binding syntax.
For example, to bind the Text value of a child UILabel called myLabel to a child property Name on a Person in the list you could use:
this.CreateBinding(myLabel).For(label => label.Text).To<Person>(p => p.Name).Apply();
Or if you wanted to bind the Text to the Person itself you could use:
this.CreateBinding(myLabel).For(label => label.Text).Apply();
In your LocationCell I think you are saying you want to bind the DataContext of the nested LocationCellDialog to the DataContext of the containing cell.
To do this you should be able to use:
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
this.CreateBinding(cell).For(cell => cell.DataContext).Apply();
}

Resources