Hey I'm trying to map my generic class to concrete class but using it's interface.
My service returns me data which type is
IPaggedResults<Customer>
and I want to be able to map this to
IPaggedResults<CustomerDto>
It works if I invoke mapping with:
_mapper.Map<PaggedResults<CustomerDto>>
but I want use following syntax:
_mapper.Map<IPaggedResults<CustomerDto>>
public class PaggedResults<T> : IPaggedResults<T>
{
public IEnumerable<T> Results { get; protected set; }
public int TotalResults { get; protected set; }
public int TotalPages { get; protected set; }
public int ResultsPerPage { get; protected set; }
public PaggedResults(IEnumerable<T> results, int totalResults, int resultsPerPage)
{
Results = results;
TotalResults = totalResults;
TotalPages = totalResults / resultsPerPage;
ResultsPerPage = resultsPerPage;
}
}
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
public string NIP { get; set; }
}
My mapper configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap<CustomerCompany, CustomerDto>();
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.CreateMap(typeof(IPaggedResults<>), typeof(IPaggedResults<>)).As(typeof(PaggedResults<>));
}).CreateMapper();
Im'using Automapper by Jimmy Bogard.
I could achieve it through the following code:
Create an extension for IMapperConfigurationExpression
public static class IMapperConfigurationExpressionExtensions
{
public static void MapPaggedResults<TSource, TDestination>(this IMapperConfigurationExpression exp){
exp.CreateMap(typeof(PaggedResults<TSource>), typeof(IPaggedResults<TDestination>))
.ConstructUsing((source, ctx) => { return ctx.Mapper.Map<PaggedResults<TDestination>>(source) as IPaggedResults<TDestination>; });
}
}
Then use this configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.MapPaggedResults<CustomerCompany, CustomerDto>();
}).CreateMapper();
Then both results can be obtained:
var _mapper = Initialize();
IPaggedResults<CustomerCompany> source = new PaggedResults<CustomerCompany>(
new List<CustomerCompany>() { new CustomerCompany() {Id =42, Name = "SomeName", NIP = "someNIP" } }, 1, 1);
var resut = _mapper.Map<PaggedResults<CustomerDto>>(source);
var resut2 = _mapper.Map<IPaggedResults<CustomerDto>>(source);
Related
I am using EF Core 2.2.2 in my project, and I am getting this error when trying to fetch any data from DB, I am not sure what is the reason of this, but it works fine when trying to insert new item to DB:
Error Message :
"A column has been specified more than once in the order by list.
Columns in the order by list must be unique"
I think the issue happened because of using nested OwnsMany many times but not sure:
public class Program
{
private static void Main(string[] args)
{
using(var context = new OwnsManyIssueContext())
{
var accidentEstimationDetails = new List<AccidentEstimationDetails>
{
new AccidentEstimationDetails(true, "test1"),
new AccidentEstimationDetails(false, "test2"),
new AccidentEstimationDetails(true, "test3")
};
var accidentEstimation = new AccidentEstimation(12, 11, accidentEstimationDetails);
var partiesLiabilityAmounts = new List<PartyAmountDetails>
{
new PartyAmountDetails(1,11,120),
new PartyAmountDetails(1,12,121),
new PartyAmountDetails(1,10,122)
};
var accident = new Accident(accidentEstimation, partiesLiabilityAmounts);
context.Accidents.Add(accident);
context.SaveChanges();
var dbAggregate = context.Accidents.FirstOrDefault();
}
}
}
public class Accident
{
public int Id { get; private set; }
public AccidentEstimation AccidentEstimation { get; private set; }
private List<PartyAmountDetails> _partiesAmountDetails;
public IReadOnlyCollection<PartyAmountDetails> PartiesAmountDetails => _partiesAmountDetails;
private Accident()
{
_partiesAmountDetails = new List<PartyAmountDetails>();
}
public Accident(AccidentEstimation accidentEstimation, List<PartyAmountDetails> partiesLiabilityAmount)
{
AccidentEstimation = accidentEstimation;
_partiesAmountDetails = partiesLiabilityAmount;
}
}
public class AccidentEstimation
{
public int EstimationPartyId { get; private set; }
public int FollowerId { get; private set; }
private readonly List<AccidentEstimationDetails> _details;
public IReadOnlyCollection<AccidentEstimationDetails> Details => _details;
private AccidentEstimation()
{
_details = new List<AccidentEstimationDetails>();
}
public AccidentEstimation(int estimationPartyId, int followerId, List<AccidentEstimationDetails> details)
{
EstimationPartyId = estimationPartyId;
FollowerId = followerId;
_details = details;
}
}
public class AccidentEstimationDetails
{
public bool IsApproved { get; private set; }
public string Name { get; private set; }
private AccidentEstimationDetails()
{
}
public AccidentEstimationDetails(bool isApproved, string name)
{
IsApproved = isApproved;
Name = name;
}
}
public class PartyAmountDetails
{
public int ItemTypeId { get; private set; }
public decimal Amount { get; private set; }
public decimal? Discount { get; private set; }
private PartyAmountDetails()
{
}
public PartyAmountDetails(int itemTypeId, decimal amount, decimal? discount)
{
ItemTypeId = itemTypeId;
Amount = amount;
Discount = discount;
}
}
public class AccidentTypeConfiguration : IEntityTypeConfiguration<Accident>
{
public void Configure(EntityTypeBuilder<Accident> builder)
{
builder.ToTable("Accidents");
builder.Property(b => b.Id).ValueGeneratedOnAdd(); ;
builder.HasKey(b => b.Id);
builder.OwnsOne(a => a.AccidentEstimation, a =>
{
a.Property(p => p.FollowerId).HasColumnName("FollowerId");
a.Property(p => p.EstimationPartyId).HasColumnName("EstimationPartyId");
a.OwnsMany(dd => dd.Details, dd =>
{
dd.ToTable("AccidentEstimationDetails");
dd.Property<int>("Id");
dd.HasKey("Id");
});
});
builder.OwnsMany(a => a.PartiesAmountDetails, a =>
{
a.ToTable("PartiesLiabilityAmounts");
a.Property<long>("Id");
a.HasKey("Id");
});
}
}
public class OwnsManyIssueContext : DbContext
{
public DbSet<Accident> Accidents { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"ConnectionString");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new AccidentTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
During a runtime mapping operation (like when you use ResolveUsing or a custom TypeConverter) is it possible to get the container classes (or types at least) of the source and destination members?
I know that when you map one object to another, that the objects don't have to be members of some "parent" or "container" object, but I'm talking about the situation when AutoMapper is recursively copying a complex object.
Here's an example:
Here I'm copying (or setting it up at least) Cars & Boats of "kind A" to "kind B".
public class VehicleCopyProfile : AutoMapper.Profile
{
public VehicleCopyProfile()
{
this.CreateMap<CarA, CarB>();
this.CreateMap<BoatA, BoatB>();
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// *** How can I tell if s is coming from a Car or a Boat? ***
});
}
}
// Cars & Boats each have a Windshield
public class CarA
{
public WindshieldA Windshield {get;set;}
}
public class BoatA
{
public WindshieldA Windshield {get;set;}
}
public class WindshieldA
{
public string Name {get;set;}
}
public class CarB
{
public WindshieldB Windshield {get;set;}
}
public class BoatB
{
public WindshieldB Windshield {get;set;}
}
public class WindshieldB
{
public string Name {get;set;}
}
Here is a solution using AutoMapper ResolutionContext Items as proposed by #Lucian Bargaoanu in comment. The idea is to use Before and After Map to store information in the Resolution Context. I use a Stack to keep track of the whole chain of relationship.
namespace SO51101306
{
public static class IMappingExpressionExtensions
{
public static IMappingExpression<A, B> RegisterChainOfTypes<A, B>(this IMappingExpression<A, B> mapping)
{
mapping.BeforeMap((a, b, ctx) => {
ctx.PushTypeInChainOfTypes(typeof(A));
});
mapping.AfterMap((a, b, ctx) => {
ctx.PopLastTypeInChainOfTypes();
});
return mapping;
}
}
public static class ResolutionContextExtensions
{
const string chainOfTypesKey = "ChainOfTypes";
private static Stack<Type> GetOrCreateChainOfTypesStack(ResolutionContext ctx)
{
var hasKey = ctx.Items.ContainsKey(chainOfTypesKey);
return hasKey ? (Stack<Type>)ctx.Items[chainOfTypesKey] : new Stack<Type>();
}
public static void PushTypeInChainOfTypes(this ResolutionContext ctx, Type type)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
stack.Push(type);
ctx.Items[chainOfTypesKey] = stack;
}
public static Type PopLastTypeInChainOfTypes(this ResolutionContext ctx)
{
var stack = (Stack<Type>)ctx.Items[chainOfTypesKey];
return stack.Pop();
}
public static bool HasParentType(this ResolutionContext ctx, Type parentType)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
return stack.Contains(parentType);
}
}
public class CarCopyProfile : Profile
{
public CarCopyProfile()
{
CreateMap<CarA, CarB>().RegisterChainOfTypes();
CreateMap<BoatA, BoatB>().RegisterChainOfTypes();
CreateMap<WindshieldA, WindshieldB>()
.ConvertUsing((wa,wb,ctx)=> {
if(ctx.HasParentType(typeof(CarA)))
{
Console.WriteLine("I'm coming from CarA");
//Do specific stuff here
}
else if (ctx.HasParentType(typeof(BoatA)))
{
Console.WriteLine("I'm coming from boatA");
//Do specific stuff here
}
return wb;
});
}
}
public class CarA
{
public WindshieldA Windshield { get; set; }
}
public class BoatA
{
public WindshieldA Windshield { get; set; }
}
public class CarB
{
public WindshieldB Windshield { get; set; }
}
public class BoatB
{
public WindshieldB Windshield { get; set; }
}
public class WindshieldA
{
public string Name { get; set; }
}
public class WindshieldB
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(c => c.AddProfile<CarCopyProfile>());
var carA = new CarA{Windshield = new WindshieldA()};
var boatA = new BoatA{Windshield = new WindshieldA()};
var carB = Mapper.Map<CarB>(carA);
var boatB = Mapper.Map<BoatB>(boatA);
}
}
}
This will output:
I'm coming from CarA
I'm coming from boatA
Another way is to use custom value resolver:
class CustomResolver<T1, T2>:IValueResolver ... { ... }
this.CreateMap<CarA, CarB>()
.ForMember(x => x.Windshield , opt => opt.ResolveUsing(new CustomResolver<CarA, CarB>()));
Then in you CustomResolver implementation:
var windshieldB = Mapper.Map<WindshieldB>(windshieldA, x => {x.Items["type1"] = typeof(T1); x.Items["type2"] = typeof(T2);});
And then:
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// resContext.Options.Items["type1"]
});
See http://docs.automapper.org/en/stable/Custom-value-resolvers.html
I have list of my class type which I need to copy to another class type. In following code I am trying to copy GenderEntity to Gender but getting no results
private void MapGender()
{
List<GenderEntity> _GenderEntity = _GenderServiceObject.GenderEntity();
List<Gender> _Gender = new List<Gender>();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<List<GenderEntity>, List<Gender>>();
});
IMapper mapper = config.CreateMapper();
_Gender = mapper.Map<List<GenderEntity>, List<Gender>>(_GenderEntity);
var x = "d";
}
GenderEntity - Source
public class GenderEntity
{
public GenderEntity() { }
public string Code { get; set; }
public string Description { get; set; }
}
Gender - Destination
[DataContract]
public class Gender
{
[DataMember]
public string Code { get; set; }
[DataMember]
public string Description { get; set; }
}
Gender entity is used for WCF SOAP services as data contract
Return List Class
public class GenderService
{
#region EBS Service return 'Gender'
public Gender GenderEBSEntity()
{
return GenderEBS.GetGender_EBS();
}
#endregion
#region Mapped Gender Entity
public List<GenderEntity> GenderEntity()
{
return GenderEBS.GetGenderEntity();
}
#endregion
}
You don't need to map Lists with AutoMapper, just create a map like:
cfg.CreateMap<GenderEntity, Gender>();
and then map it like this:
_Gender = mapper.Map<Gender[]>(_GenderEntity).ToList();
my answer...
private void MapGender()
{
List<Gender> MappedGender = new List<Contracts.Data.Gender>();
IList<GenderEntity> genderEntity = _GenderServiceObject.GenderEntity();
if(genderEntity!=null)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<GenderEntity, Gender>();
});
IMapper mapper = config.CreateMapper();
gender = mapper.Map<IList<GenderEntity>, IList<Gender>>(genderEntity).ToList();
}
}
I'm working on a project where I have some recursive data structure and I want to create a fixture for it.
The data structure is XmlCommandElement, it has a single method ToCommand that converts XmlCommandElement to Command.
Each node on the tree can be a XmlCommandElement and/or XmlCommandPropertyElement.
Now, in order to test the behaviour of the method ToCommand I want to fetch XmlCommandElement with some arbitrary data.
I want to control the depth of the tree and the amount of instances of XmlCommandElement and/or XmlCommandPropertyElement per node.
So here is the code I'm using for the fixture:
public class XmlCommandElementFixture : ICustomization
{
private static readonly Fixture _fixture = new Fixture();
private XmlCommandElement _xmlCommandElement;
public int MaxCommandsPerDepth { get; set; }
public int MaxDepth { get; set; }
public int MaxPropertiesPerCommand { get; set; }
public XmlCommandElementFixture BuildCommandTree()
{
_xmlCommandElement = new XmlCommandElement();
var tree = new Stack<XmlCommandElementNode>();
tree.Push(new XmlCommandElementNode(0, _xmlCommandElement));
while (tree.Count > 0) {
var node = tree.Pop();
node.Command.Key = CreateRandomString();
node.Command.Properties = CreateProperties();
if (MaxDepth > node.Depth) {
var commands = new List<XmlCommandElement>();
for (var i = 0; i < MaxCommandsPerDepth; i++) {
var command = new XmlCommandElement();
tree.Push(new XmlCommandElementNode(node.Depth + 1, command));
commands.Add(command);
}
node.Command.Commands = commands.ToArray();
}
}
return this;
}
public void Customize(IFixture fixture)
{
fixture.Customize<XmlCommandElement>(c => c.FromFactory(() => _xmlCommandElement)
.OmitAutoProperties());
}
private static string CreateRandomString()
{
return _fixture.Create<Generator<string>>().First();
}
private XmlCommandPropertyElement[] CreateProperties()
{
var properties = new List<XmlCommandPropertyElement>();
for (var i = 0; i < MaxPropertiesPerCommand; i++) {
properties.Add(new XmlCommandPropertyElement {
Key = CreateRandomString(),
Value = CreateRandomString()
});
}
return properties.ToArray();
}
private struct XmlCommandElementNode
{
public XmlCommandElementNode(int depth, XmlCommandElement xmlCommandElement)
{
Depth = depth;
Command = xmlCommandElement;
}
public XmlCommandElement Command { get; }
public int Depth { get; }
}
}
And this is how I'm using it:
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
This works perfectly fine! but the issue I have with it is it isn't generic, the whole point of AutoFixture at least as far as I know is to avoid making specific fixtures.
So what I would really like to do is something like this (found it here but it doesn't work for me.):
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandElement), 3));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandPropertyElement), 4));
xmlCommandElement = fixture.Create<XmlCommandElement>();
Here is all the code for reference:
Interfaces:
public interface ICommandCollection : IEnumerable<ICommand>
{
ICommand this[string commandName] { get; }
void Add(ICommand command);
}
public interface ICommandPropertyCollection : IEnumerable<ICommandProperty>
{
string this[string key] { get; }
void Add(ICommandProperty property);
}
public interface ICommandProperty
{
string Key { get; }
string Value { get; }
}
public interface ICommand
{
ICommandCollection Children { get; set; }
string Key { get; }
ICommandPropertyCollection Properties { get; }
}
public interface ICommandConvertible
{
ICommand ToCommand();
}
Classes:
public sealed class CommandPropertyCollection : ICommandPropertyCollection
{
private readonly IDictionary<string, ICommandProperty> _properties;
public CommandPropertyCollection()
{
_properties = new ConcurrentDictionary<string, ICommandProperty>();
}
public string this[string key]
{
get
{
ICommandProperty property = null;
_properties.TryGetValue(key, out property);
return property.Value;
}
}
public void Add(ICommandProperty property)
{
_properties.Add(property.Key, property);
}
public IEnumerator<ICommandProperty> GetEnumerator()
{
return _properties.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public sealed class CommandProperty : ICommandProperty
{
public CommandProperty(string key, string value)
{
Key = key;
Value = value;
}
public string Key { get; }
public string Value { get; }
}
public sealed class Command : ICommand
{
public Command(string key, ICommandPropertyCollection properties)
{
Key = key;
Properties = properties;
}
public ICommandCollection Children { get; set; }
public string Key { get; }
public ICommandPropertyCollection Properties { get; }
}
public class XmlCommandPropertyElement : ICommandPropertyConvertible
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
public ICommandProperty ToCommandProperty()
{
return new CommandProperty(Key, Value);
}
}
Finally, the class I'm trying to test is as follow:
public class XmlCommandElement : ICommandConvertible
{
[XmlArray]
[XmlArrayItem("Command", typeof(XmlCommandElement))]
public XmlCommandElement[] Commands { get; set; }
[XmlAttribute("key")]
public string Key { get; set; }
[XmlArray]
[XmlArrayItem("Property", typeof(XmlCommandPropertyElement))]
public XmlCommandPropertyElement[] Properties { get; set; }
public ICommand ToCommand()
{
ICommandPropertyCollection properties = new CommandPropertyCollection();
foreach (var property in Properties) {
properties.Add(property.ToCommandProperty());
}
ICommand command = new Command(Key, properties);
return command;
}
}
The test itself looks like this:
namespace Yalla.Tests.Commands
{
using Fixtures;
using FluentAssertions;
using Ploeh.AutoFixture;
using Xbehave;
using Yalla.Commands;
using Yalla.Commands.Xml;
public class XmlCommandElementTests
{
[Scenario]
public void ConvertToCommand(XmlCommandElement xmlCommandElement, ICommand command)
{
$"Given an {nameof(XmlCommandElement)}"
.x(() =>
{
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
});
$"When the object is converted into {nameof(ICommand)}"
.x(() => command = xmlCommandElement.ToCommand());
"Then we need to have a root object with a key"
.x(() => command.Key.Should().NotBeNullOrEmpty());
"And 4 properties as its children"
.x(() => command.Properties.Should().HaveCount(4));
}
}
}
Thanks to Mark Seemann! the final solution looks like this:
public class RecursiveCustomization : ICustomization
{
public int MaxDepth { get; set; }
public int MaxElements { get; set; }
public void Customize(IFixture fixture)
{
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(MaxDepth));
fixture.RepeatCount = MaxElements;
}
}
And can be used like this:
xmlCommandElement = new Fixture().Customize(new RecursiveCustomization {
MaxDepth = 2,
MaxElements = 3
}).Create<XmlCommandElement>();
You can fairly easily create a small tree by changing the Fixture's recursion behaviour:
[Fact]
public void CreateSmallTree()
{
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(recursionDepth: 2));
var xce = fixture.Create<XmlCommandElement>();
Assert.NotEmpty(xce.Commands);
}
The above test passes.
My object mapping requires me to pass the source object and an additional object to be able to map the destination object appropriately. However, I am unable to determine a way to be able to get that done.
public class SourceDto
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string SourceValue3 { get; set; }
public string SourceValue4 { get; set; }
}
public class AnotherSourceDto
{
public string AnotherSourceValue1 { get; set; }
public string AnotherSourceValue2 { get; set; }
public string AnotherSourceValue3 { get; set; }
public string AnotherSourceValue4 { get; set; }
}
public class Destination
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string DestValue3 { get; set; }
}
public string ConvertToDestValue3(string sourceValue3, string anotherSourceValue2)
{
// Some logic goes here
return sourceValue3 + " " + anotherSourceValue2;
}
void Main()
{
var config = new MapperConfiguration(
cfg =>cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3,
opt => opt.MapFrom(
src => ConvertToDestValue3(src.SourceValue3, "" //Here I need to pass AnotherSourceDto.AnotherSourceValue2 ))));
}
I'm afraid the answer is to map that property outside AutoMapper.
Here's an example where you could do this using an extension method, so it still has the look and feel of being done with AutoMapper.
// put inside a static class
public static Destination CustomMap(
this IMapper mapper,
SourceDto source,
AnotherSourceDto anotherSource)
{
var destination = mapper.Map<Destination>(source);
destination.DestValue3 =
source.SourceValue3 + " " + anotherSource.AnotherSourceValue2;
return destination;
}
void Main()
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3, opt => opt.Ignore()));
// usage
var mapper = config.CreateMapper();
var source = new SourceDto { /* add properties */ };
var anotherSource = new AnotherSourceDto { /* add properties */ };
var destination = mapper.CustomMap(source, anotherSource);
}