AutoMapper 10.0.0 with F# - How to fix it without additional mapping? - automapper

I have 3 types :
type [<CLIMutable>] status = { id : Guid; name : string }
type [<CLIMutable>] container = { id : Guid; name : string; status : status}
type [<CLIMutable>] scontainer = { id : Guid; name : string; status : string}
and next configuration
let c =
MapperConfiguration(
fun config ->
config.CreateMap<container, scontainer>()
.ForMemberFs((fun sc -> sc.name), (fun opts -> opts.MapFrom(fun m _ -> m.status.name))) |> ignore
)
I'm trying to map with next code
let con = { id = Guid.NewGuid(); name = "Template 1"; container.status = { id = Guid.NewGuid(); name = "Draft" } }
let mapper = c.CreateMapper()
let sc = mapper.Map<scontainer>(con)
But member mapping isn't called and sc.status contains a string representation of status object(id and name together).
When I add new map:
config.CreateMap<status, string>().ConvertUsing(fun s -> s.name) |> ignore
then sc.status contains correct value.
Does anyone know how to make it work without additional mappings?

Next lines solves my problem:
let c =
AutoMapper.MapperConfiguration(
fun config ->
config.ShouldMapProperty <- fun _ -> false
config.ShouldMapField <- fun _ -> true
)

Metadata produced for constructor is (id, name, status) in both scontainer and container.
Metadata produced for properties is: id, name, status in both scontainer and container.
Metadata produced for fields is: id#, name#, status# in both scontainer and container.
If I don't disable properties usage then automapper will find a match between constructor parameters and properties and will use properties as resolver, which means constructor resolver will be used.
If I disable properties usage, then there will be no match between constructor parameters and fields and constructor will no be used.
I might be wrong but bug is in the next few lines in method: private void MapDestinationCtorToSource(TypeMap typeMap, List ctorParamConfigurations)
in next code:
var resolvers = new LinkedList();
var canResolve = typeMap.Profile.MapDestinationPropertyToSource(typeMap.SourceTypeDetails, destCtor.DeclaringType, parameter.GetType(), parameter.Name, resolvers, IsReverseMap);
if ((!canResolve && parameter.IsOptional) || IsConfigured(parameter))
{
canResolve = true;
}
Here is a test that reproduces a bug
using AutoMapper;
using System;
using Xunit;
namespace ConstructorBug
{
public class Status
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Container
{
public Guid Id { get; set; }
public string Name { get; set; }
public Status Status { get; set; }
}
public class SContainer
{
public SContainer()
{
}
public SContainer(string id, string name, string status)
{
Id = id;
Name = name;
Status = status;
}
public string Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
public class ConstructorBug
{
[Fact]
public void ConstructorMapBug()
{
var mapperConfiguration = new MapperConfiguration(
config =>
{
config.CreateMap<Container, SContainer>()
.ForMember(dest => dest.Status, opts => opts.MapFrom(x => x.Status.Name));
}
);
var mapper = mapperConfiguration.CreateMapper();
var con = new Container
{
Id = Guid.NewGuid(),
Name = "Container 1",
Status = new Status { Id = Guid.NewGuid(), Name = "Draft" }
};
var scon = mapper.Map<SContainer>(con);
Assert.Equal(scon.Id, con.Id.ToString());
Assert.Equal(scon.Name, con.Name);
Assert.Equal(scon.Status, con.Status.Name);
}
}
}

Related

Automapper: map an anonymous/dynamic type

I need some help to map an anonymous object using Automapper. The goal is combine Product and Unity in a ProductDto (in which unity is a product's property).
Autommaper CreateMissingTypeMaps configuration is set to true.
My Classes:
public class Product
{
public int Id { get; set; }
}
public class Unity
{
public int Id { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
public UnityDto Unity{ get; set; }
}
public class UnityDto
{
public int Id { get; set; }
}
Test Code
Product p = new Product() { Id = 1 };
Unity u = new Unity() { Id = 999 };
var a = new { Product = p, Unity = u };
var t1 = Mapper.Map<ProductDto>(a.Product);
var t2 = Mapper.Map<UnityDto>(a.Unity);
var t3 = Mapper.Map<ProductDto>(a);
Console.WriteLine(string.Format("ProductId: {0}", t1.Id)); // Print 1
Console.WriteLine(string.Format("UnityId: {0}", t2.Id)); // Print 999
Console.WriteLine(string.Format("Anonymous ProductId: {0}", t3.Id)); // Print 0 <<< ERROR: It should be 1 >>>
Console.WriteLine(string.Format("Anonymous UnityId: {0}", t3.Unity.Id)); // Print 999
There are two maps added to the profile:
CreateMap<Product, ProductDto>();
CreateMap<Unity, UnityDto>();
The problem is how Automapper map anonymous objects. I haven't time to check out Automapper source code but I got the desired behaviour with minor changes on my anonymous object:
var a = new { Id = p.Id, Unity = u };
By doing this, I might even delete previous mappings because now it is using only CreateMissingTypeMaps.
Note: As matter of fact I'm not sure if it is really an issue or I it was just my unreal expectations.

ServiceStack Ormlite Join Wrapper

I've created a wrapper in my data access for joins in OrmLite.
I'm now getting the exception:
System.Exception : Expression should have only one column
All of my entities have a base class of BaseEntity.
JoinType is just a facade to contain the column, selection and where of a join.
My wrapper is as follows:
public IEnumerable<TResultEntity> Join<TResultEntity>(IList<JoinType<BaseEntity, BaseEntity>> joins)
{
var result = new List<TResultEntity>();
if (joins != null && joins.Any())
{
var joinBuilder = new JoinSqlBuilder<T, BaseEntity>();
foreach (var join in joins)
{
joinBuilder = joinBuilder.Join(join.LeftColumn, join.RightColumn, join.LeftSelection, join.RightSelection, join.LeftWhere, join.RightWhere);
}
var connection = this.DataConnection;
using (connection)
{
var joinSql = joinBuilder.SelectDistinct().ToSql();
result = connection.SqlList<TResultEntity>(joinSql);
}
}
return result;
}
Doing the same thing, without the list seems to work:
public IEnumerable<TResultEntity> Join<TLeftTable1, TRightTable1, TLeftTable2, TRightTable2, TResultEntity>(
JoinType<TLeftTable1, TRightTable1> join1,
JoinType<TLeftTable2, TRightTable2> join2)
where TLeftTable1 : BaseEntity
where TRightTable1 : BaseEntity
where TLeftTable2 : BaseEntity
where TRightTable2 : BaseEntity
EDIT - I'm testing using the below call:
// Act
var join1 = new JoinType<AnswerEntity, UserSurveyStateEntity>(
l => l.OwnerId,
r => r.UserId,
x => new { UserId = x.OwnerId, x.QuestionId, AnswerId = x.Id, x.AnswerValue });
var join2 = new JoinType<SurveyEntity, UserSurveyStateEntity>(
l => l.Id,
r => r.SurveyInstanceId,
x => new { SurveyId = x.Id, SurveyName = x.Name, x.StatusValue },
null,
null,
x => x.StatusValue == (int)UserSurveyStatus.Complete);
var joins = new List<JoinType<BaseEntity, BaseEntity>>();
joins.Add(join1.As<JoinType<BaseEntity, BaseEntity>>());
joins.Add(join2.As<JoinType<BaseEntity, BaseEntity>>());
var result = dataAccess.Join<AnswerEntity>(joins).ToList();
EDIT - Now seeing the use case, the error is related to casting to the base type and the builder storing more than one column selector for the concrete BaseEntity. Consider adding an abstract JoinType class, and modifying the JoinType class so it will apply the join for the builder.
For example:
public class Entity
{
public string Id { get; set; }
}
public class Foo
: Entity
{
public string Value { get; set; }
}
public class Bar
: Entity
{
public string FooId { get; set; }
public string Value { get; set; }
}
public abstract class JoinType
{
public abstract JoinSqlBuilder<TNew, TBase> ApplyJoin<TNew, TBase>(
JoinSqlBuilder<TNew, TBase> bldr);
}
public class JoinType<TSource, TTarget>
: JoinType
{
private Expression<Func<TSource, object>> _sourceColumn;
private Expression<Func<TTarget, object>> _destinationColumn;
private Expression<Func<TSource, object>> _sourceTableColumnSelection;
private Expression<Func<TTarget, object>> _destinationTableColumnSelection;
private Expression<Func<TSource, bool>> _sourceWhere;
private Expression<Func<TTarget, bool>> _destinationWhere;
public JoinType(Expression<Func<TSource, object>> sourceColumn,
Expression<Func<TTarget, object>> destinationColumn,
Expression<Func<TSource, object>>
sourceTableColumnSelection = null,
Expression<Func<TTarget, object>>
destinationTableColumnSelection = null,
Expression<Func<TSource, bool>> sourceWhere = null,
Expression<Func<TTarget, bool>> destinationWhere =
null)
{
this._sourceColumn = sourceColumn;
this._destinationColumn = destinationColumn;
this._sourceTableColumnSelection = sourceTableColumnSelection;
this._destinationTableColumnSelection =
destinationTableColumnSelection;
this._sourceWhere = sourceWhere;
this._destinationWhere = destinationWhere;
}
public override JoinSqlBuilder<TNew, TBase> ApplyJoin<TNew, TBase>(
JoinSqlBuilder<TNew, TBase> bldr)
{
bldr.Join(_sourceColumn,
_destinationColumn,
_sourceTableColumnSelection,
_destinationTableColumnSelection,
_sourceWhere,
_destinationWhere);
return bldr;
}
}
public class FooBar
{
[References(typeof(Foo))]
public string FooId { get; set; }
[References(typeof(Bar))]
public string BarId { get; set; }
[References(typeof(Foo))]
public string FooValue { get; set; }
[References(typeof(Bar))]
public string BarValue { get; set; }
}
/*
This join accomplishes the same thing, but just returns the SQL as a string.
*/
public string Join<TResultEntity,TBase>(IList<JoinType>joins)
{
var result = new List<TResultEntity>();
if (joins != null && joins.Any())
{
var joinBuilder = new JoinSqlBuilder<TResultEntity, TBase>();
foreach (var joinType in joins)
{
//call the apply join, and the join type will know the valid types
joinBuilder = joinType.ApplyJoin(joinBuilder);
}
return joinBuilder.SelectDistinct().ToSql();
}
return null;
}
[TestMethod]
public void TestMethod1()
{
OrmLiteConfig.DialectProvider = SqlServerDialect.Provider;
var joins = new List<JoinType>();
var jointype1 = new JoinType<Bar, FooBar>(
bar => bar.Id,
bar => bar.BarId,
bar => new { BarId = bar.Id, BarValue = bar.Value }
);
joins.Add(jointype1);
var joinType2 = new JoinType<Foo, FooBar>(
foo => foo.Id,
bar => bar.FooId,
foo => new { FooId = foo.Id, FooValue = foo.Value}
);
joins.Add(joinType2);
var str = Join<FooBar, Bar>(joins);
}
Old Answer - still relevant to the error
This error is caused by your selector join.LeftColumn or your join.RightColumn containing two selectors. Make sure they only contain a single one.
I was able to reproduce the error with the following test:
public class Entity
{
public string Id { get; set; }
}
public class Foo
: Entity
{
public string Value { get; set; }
}
public class Bar
: Entity
{
public string FooId { get; set; }
public string Value { get; set; }
}
public class FooBar
{
[References(typeof(Foo))]
public string FooId { get; set; }
[References(typeof(Bar))]
public string BarId { get; set; }
[References(typeof(Foo))]
public string FooValue { get; set; }
[References(typeof(Bar))]
public string BarValue { get; set; }
}
[TestMethod]
public void TestMethod1()
{
OrmLiteConfig.DialectProvider = SqlServerDialect.Provider;
var bldr = new JoinSqlBuilder<FooBar,Bar>();
bldr = bldr.Join<FooBar, Bar>(
bar => bar.BarId,
bar => new { Id1 = bar.Id, Id2 = bar.Id},//<-- this should only contain a single member
bar => new { BarId =bar.BarId },
bar => new { BarId = bar.Id, BarValue = bar.Value},
bar => bar.BarId != null,
bar => bar.Id != null
);
var str = bldr.SelectDistinct().ToSql();
}

Register Custom Generic type in AutoMapper

i want register custo generic type like default generic type that register in autoMaper (like List, Array) in AutoMappper.
i have one generic type in project this Code :
class PagedResult<T>
{
public List<T> PageOfResults { get; set; }
public int TotalItems { get; set; }
}
and Dto Class is:
class StudentDto
{
public int ID { get; set; }
public string Name { get; set; }
}
and VM Model is :
class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and service class:
class MyServie
{
public PagedResult<StudentDto> Swap()
{
var test2 = new PagedResult<StudentDto>();
test2.PageOfResults = new List<StudentDto>();
test2.PageOfResults.Add(new StudentDto() { ID = 10, Name = "Ten" });
test2.TotalItems = 10;
return test2;
}
}
i want use from AutoMapper Object Manager for register PagedResult<> in automapper but i can not do this
var allMappers = MapperRegistry.AllMappers();
MapperRegistry.AllMappers = () => new IObjectMapper[]{
new IdentifiableMapper(),
}.Concat(allMappers);
var service = new MyServie();
PagedResult<StudentDto> pageableStudentDto = service.Swap();
Mapper.CreateMap<StudentDto, StudentVM>();;
PagedResult<StudentVM> vm = Mapper.Map<PagedResult<StudentDto>, PagedResult<StudentVM>>(pageableStudentDto);
and implement of
public class PageOfMapper : IObjectMapper
{
public bool IsMatch(ResolutionContext context)
{
return typeof(PagedResult<>).IsAssignableFrom(context.SourceType.GetGenericTypeDefinition()) &&
typeof(PagedResult<>).IsAssignableFrom(context.DestinationType.GetGenericTypeDefinition());
//return true;
}
public object Map(ResolutionContext context, IMappingEngineRunner mapper)
{
// please help me in this code for map ******************
return null;
}
}

AutoMapper failing to map a simple list

I have used automapper for mapping lists in the past, for for some reason it won't work in this case.
public class MyType1 {
public int Id { get; set; }
public string Description { get; set; }
}
public class MyType2 {
public int Id { get; set; }
public string Description { get; set; }
}
public void DoTheMap() {
Mapper.CreateMap<MyType2, MyType1>();
Mapper.AssertConfigurationIsValid();
var theDto1 = new MyType2() { Id = 1, Description = "desc" };
var theDto2 = new MyType2() { Id = 2, Description = "desc2" };
List<MyType2> type2List = new List<MyType2> { theDto1, theDto2 };
List<MyType1> type1List = Mapper.DynamicMap<List<MyType1>>(type2List);
//FAILURE. NO EXCEPTION, BUT ZERO VALUES
List<MyType1> type1List2 =type2List.Select(Mapper.DynamicMap<MyType1>).ToList();
//SUCCESS, WITH LINQ SELECT
}
Change this:
Mapper.DynamicMap<List<MyType1>>(type2List)
To this:
Mapper.Map<List<MyType1>, List<MyType2>>(type2List);
DynamicMap is only if you don't know the type at compile time - for things like anonymous types.

ValueInjecter question

After working with AutoMapper I came across ValueInjecter on this site. I am trying it out but I am stuck on what is probably a very simple scenario.
But before I dig into the code sample, does anyone know if ValueInjecter works in a Medium-Trust web environment? (Like Godaddy?)
Ok, onto the code! I have the following models:
public class NameComponent
{
public string First { get; set; }
public string Last { get; set; }
public string MiddleInitial { get; set; }
}
public class Person
{
public NameComponent Name { get; set; }
}
that I want to map to the following DTO:
public class PersonDTO : BaseDTO
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { NotifyPropertyChanged(() => FirstName, ref _firstName, value); }
}
private string _middleInitial;
public string MiddleInitial
{
get { return _middleInitial; }
set { NotifyPropertyChanged(() => MiddleInitial, ref _middleInitial, value); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { NotifyPropertyChanged(() => LastName, ref _lastName, value); }
}
}
So when I want to Map from Model to DTO I need a Model.Name.First -> DTO.FirstName
and when going from DTO to Model I need FirstName -> Name.First. From my understanding this is not a simple Flatten/UnFlatten, because the words also reverse themselves, ie: FirstName <--> Name.First. So First and Last names could use the same kind of rule, but what about MiddleInitial? Model.Name.MiddleInitial -> DTO.MiddleInitial.
I see there are some plugins, but none of them seem to do what I would want. Has anyone else come across this scenario?
the basic idea is that I match the Name with the FirstName, I take this as a pivot point, and in the method that usually sets the value to just one (FirstName) property I set it to 3 properties - that's for the FromNameComp
in the ToNameComp i match the same properties but I take the value from 3 and create one and set it
public class SimpleTest
{
[Test]
public void Testit()
{
var p = new Person { Name = new NameComponent { First = "first", Last = "last", MiddleInitial = "midd" } };
var dto = new PersonDTO();
dto.InjectFrom<FromNameComp>(p);
Assert.AreEqual(p.Name.First, dto.FirstName);
Assert.AreEqual(p.Name.Last, dto.LastName);
Assert.AreEqual(p.Name.MiddleInitial, dto.MiddleInitial);
var pp = new Person();
pp.InjectFrom<ToNameComponent>(dto);
Assert.AreEqual(dto.LastName, pp.Name.Last);
Assert.AreEqual(dto.FirstName, pp.Name.First);
Assert.AreEqual(dto.MiddleInitial, pp.Name.MiddleInitial);
}
public class FromNameComp : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == "Name" && c.SourceProp.Type == typeof(NameComponent)
&& c.TargetProp.Name == "FirstName"
&& c.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo c)
{
dynamic d = c.Target.Value;
var nc = (NameComponent)c.SourceProp.Value;
//d.FirstName = nc.First; return nc.First does this
d.LastName = nc.Last;
d.MiddleInitial = nc.MiddleInitial;
return nc.First;
}
}
public class ToNameComponent : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.TargetProp.Name == "Name" && c.TargetProp.Type == typeof(NameComponent)
&& c.SourceProp.Name == "FirstName";
}
protected override object SetValue(ConventionInfo c)
{
dynamic d = c.Source.Value;
var nc = new NameComponent { First = d.FirstName, Last = d.LastName, MiddleInitial = d.MiddleInitial };
return nc;
}
}
public class NameComponent
{
public string First { get; set; }
public string Last { get; set; }
public string MiddleInitial { get; set; }
}
public class Person
{
public NameComponent Name { get; set; }
}
public class PersonDTO
{
public string FirstName { get; set; }
public string MiddleInitial { get; set; }
public string LastName { get; set; }
}
}
But before I dig into the code sample,
does anyone know if ValueInjecter
works in a Medium-Trust web
environment? (Like Godaddy?)
it doesn't use reflection.emit so it should work

Resources