AutoMapAttribute and polymorphism - automapper

Update 1
Following the suggestion by Lucian, I removed the attributes from the classes used for the property and added a profile; like this, both tests succeed:
public class TargetBaseClass
{
}
public class TargetConcreteClass : TargetBaseClass
{
}
public class TestProfile : Profile
{
public TestProfile()
{
this.CreateMap<TargetBaseClass, SourceBaseClass>()
.Include<TargetConcreteClass, SourceConcreteClass>();
this.CreateMap<TargetConcreteClass, SourceConcreteClass>();
this.CreateMap<SourceBaseClass, TargetBaseClass>()
.Include<SourceConcreteClass, TargetConcreteClass>();
this.CreateMap<SourceConcreteClass, TargetConcreteClass>();
}
}
The question is: can it be translated to AutoMap attribute(s)?
I have several derived classes and I will might add more in the future.
The attribute looked at first sight an easy way to do it.
original question
I'm having issues using AutoMapper with the AutoMapAttribute.
I created the following test:
namespace Tests
{
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
public class SourceContainerClass
{
public SourceBaseClass MyProperty { get; set; }
}
public class SourceBaseClass
{ }
public class SourceConcreteClass : SourceBaseClass
{ }
[AutoMap(typeof(SourceContainerClass), ReverseMap = true)]
public class TargetContainerClass
{
public TargetBaseClass MyProperty { get; set; }
}
[AutoMap(typeof(SourceBaseClass), ReverseMap = true, IncludeAllDerived = true)]
public class TargetBaseClass
{ }
[AutoMap(typeof(SourceConcreteClass), ReverseMap = true)]
public class TargetConcreteClass : TargetBaseClass
{ }
public class AutoMapperTests
{
[Fact]
public void TestSourceToTargetMapping()
{
var services = new ServiceCollection().AddAutoMapper(this.GetType().Assembly).BuildServiceProvider();
var mapper = services.GetRequiredService<IMapper>();
var source = new SourceContainerClass
{
MyProperty = new SourceConcreteClass()
};
var target = mapper.Map<TargetContainerClass>(source);
Assert.IsType<TargetConcreteClass>(target.MyProperty);
}
[Fact]
public void TestBackwardMapping()
{
var services = new ServiceCollection().AddAutoMapper(this.GetType().Assembly).BuildServiceProvider();
var mapper = services.GetRequiredService<IMapper>();
var source = new TargetContainerClass
{
MyProperty = new TargetConcreteClass()
};
var back = mapper.Map<SourceContainerClass>(source);
Assert.IsType<SourceConcreteClass>(back.MyProperty);
}
}
}
With the second test failing:
Tests.AutoMapperTests.TestBackwardMapping:
Outcome: Failed
Error Message:
Assert.IsType() Failure
Expected: Tests.SourceConcreteClass
Actual: Tests.SourceBaseClass
Stack Trace:
at Tests.AutoMapperTests.TestBackwardMapping() in /{..}/AutoMapperTests.cs:line 59
Tests.AutoMapperTests.TestSourceToTargetMapping:
Outcome: Passed
Total tests: 2. Passed: 1. Failed: 1. Skipped: 0
Is this mapping not supported or am I missing something?
I'm referencing the package AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0.

Related

Expression mapping using Automapper throws a System.EntryPointNotFoundException after migrating to Automapper 10

I am using automapper to map expressions between classes that implement IEnumerable. The base classes look like this:
public abstract class EntityDtoBase<T> : DtoBase<T> where T : EntityDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class PersistenceDtoBase<T> : DtoBase<T> where T : PersistenceDtoBase<T>
{
public virtual int Id { get; set; }
}
public abstract class DtoBase<T> : IEnumerable<T> where T : DtoBase<T>
{
private readonly IList<T> _items;
public int Count => _items.Count;
protected DtoBase()
{
this._items = new List<T>();
}
public void Add(T item)
{
_items.Add(item);
}
/* other methods like AddRange... */
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
}
After migrating to Automapper 10, expression mapping between classes enheriting from EntityDtoBase and PersistenceDtoBase throws a System.EntryPointNotFoundException : Entry point was not found. The configuration I am using in my project is similar to the one used in this unit test:
public class UserEntityDto : EntityDtoBase<UserEntityDto> { }
public class UserPersistenceDto : PersistenceDtoBase<UserPersistenceDto> { }
public class UserProfile : Profile
{
public UserProfile() { CreateMap<UserEntityDto, UserPersistenceDto>().ReverseMap(); }
}
public class UnitTest
{
private readonly IMapper _mapper;
public UnitTest()
{
var sp = CreateServices();
_mapper = sp.GetRequiredService<IMapper>();
}
private static IServiceProvider CreateServices()
{
return new ServiceCollection()
.AddAutoMapper(cfg =>
{
cfg.AddExpressionMapping();
cfg.AddCollectionMappers();
cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
cfg.AllowNullCollections = true;
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
}, typeof(UnitTest).Assembly)
.BuildServiceProvider(false);
}
[Fact]
public void Should_Map_Expression()
{
Expression<Func<UserEntityDto, bool>> searchExpression = u => u.Id == 1;
var searchExpressionMapped = _mapper.Map<Expression<Func<UserPersistenceDto, bool>>>(searchExpression);
Assert.NotNull(searchExpressionMapped);
}
You can find the complete unit test project here. The test succeeds using Automapper 9 and fails using Automapper 10.

autofac not resolve properly generic List type

I am trying to resolve list of object using autofac Container, and expecting an empty list in response. However, I am not able to get empty list in return instead getting count as 1.
I also try with without list registration in aotufac conatiner but getting same response.
<pre><code>
class autofacFactory : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>));
builder.RegisterType<Response>().As<IResponse>();
builder.RegisterType<CustomDependencyResolver>().As<ICustomDependencyResolver>();
}
}
public class Response : IResponse
{
public string TransactionNo { get; set; }
public string SchemeCode { get; set; }
}
public interface IResponse
{
string TransactionNo { get; set; }
string SchemeCode { get; set; }
}
public interface ICustomDependencyResolver
{
TResolved Resolve<TResolved>();
}
internal class CustomDependencyResolver : ICustomDependencyResolver
{
private readonly ILifetimeScope _lifetimeScope;
public CustomDependencyResolver(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public TResolved Resolve<TResolved>()
=> _lifetimeScope.Resolve<TResolved>();
}
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule(new autofacFactory());
using (var container = builder.Build())
{
ICustomDependencyResolver customDependencyResolver = container.Resolve<ICustomDependencyResolver>();
var collection = customDependencyResolver.Resolve<ICollection<IResponse>>();
var list = customDependencyResolver.Resolve<IList<IResponse>>();
}
}
Actual response:
[Image1][1]
[Image2][2]
[Expected Response][3]
[1]: https://i.stack.imgur.com/NVXeW.jpg
[2]: https://i.stack.imgur.com/k58QX.jpg
[3]: https://i.stack.imgur.com/EcFyc.jpg
Try not registering IList<> or List<> - Autofac has built-in support for that.

Registering AutoMapper with Unity fails

I have the following code to register Mapping (version 4.2)
public class ModelMapperProfile : Profile
{
protected override void Configure()
{
CreateMap<Case, CaseModel>();
CreateMap<CaseDetail, CaseDetailModel>();
}
}
public static class AutoMapperService
{
public static MapperConfiguration Initialize()
{
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ModelMapperProfile>();
});
return config;
}
}
And I register the dependency using unity as follows...
public static void RegisterTypes(IUnityContainer container)
{
container.LoadConfiguration();
var mapper = AutoMapperService.Initialize()
.CreateMapper();
container.RegisterInstance<IMapper>(mapper);
}
My here service constructor..
public TaxLiabilityCaseService(IMapper mapper,
IUnitOfWork unitofWork,
IRepository<Case> caseR,
IRepository<CaseDetail> caseDetailR)
{
_mapper = mapper;
_unitofWork = unitofWork;
_caseR = caseR;
_caseDetailR = caseDetailR;
}
And I get the following error message..
The current type, AutoMapper.IMapper, is an interface and cannot be
constructed. Are you missing a type mapping?
Answers found here did not work for me
What am I missing here
Try following these steps (MVC5):
Get Unity Nuget package:
Unity.Mvc5
Create this class:
public class MapperConfig
{
public static IMapper Mapper { get; set; }
public static void RegisterProfiles()
{
var config = new MapperConfiguration(cfg =>
{
// add profiles here
});
config.AssertConfigurationIsValid();
Mapper = config.CreateMapper();
}
}
In the UnityConfig file (created by the package), add this:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterInstance<IMapper>(MapperConfig.Mapper);
}
In the Global.asax, add these:
protected void Application_Start()
{
MapperConfig.RegisterProfiles();
UnityConfig.RegisterComponents();
}
You should be good after this.

Does ServiceStack support generics in end-to-end typed requests

I was playin' around with ServiceStack and was wondering if it supported this scenario. I'm using generics in my request types so that many DTOs that inherit from a common interface will support the same basic methods [ like... GetById(int Id) ].
Using a request type specific to a single kind of DTO works, but breaks the generics nice-ness...
var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id)); //PASS
Mapping a route to the generic also works:
Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
...
var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id)); //PASS
But using the end-to-end generic request type fails:
var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id)); //FAIL
I wonder if I'm just missing something, or if i'm trying to abstract just ooone layer too far... :)
Below is a complete, failing program using NUnit, default ServiceStack stuff:
namespace ssgenerics
{
using NUnit.Framework;
using ServiceStack.ServiceClient.Web;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using ServiceStack.WebHost.Endpoints;
[TestFixture]
class Program
{
public static PersonDto GetNewTestPersonDto()
{
return new PersonDto()
{
Id = 123,
Name = "Joe Blow",
Occupation = "Software Developer"
};
}
static void Main(string[] args)
{}
[Test]
public void TestPutGet()
{
var listeningOn = "http://*:1337/";
var appHost = new AppHost();
appHost.Init();
appHost.Start(listeningOn);
try
{
var BaseUri = "http://localhost:1337/";
var client = new JsvServiceClient(BaseUri);
var person = GetNewTestPersonDto();
client.Put(person);
var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id));
var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id));
Assert.That(person.Name, Is.EqualTo(fetchedPerson2.Name));
Assert.That(person.Occupation, Is.EqualTo(fetchedPerson2.Occupation));
var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id));
Assert.That(person.Name, Is.EqualTo(fetchedPerson3.Name));
Assert.That(person.Occupation, Is.EqualTo(fetchedPerson3.Occupation));
}
finally
{
appHost.Stop();
}
}
}
public interface IDto : IReturnVoid
{
int Id { get; set; }
}
public class PersonDto : IDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Occupation { get; set; }
}
public class DtoGetById<T> : IReturn<T> where T : IDto { public int Id { get; set; } }
public class PersonDtoGetById : IReturn<PersonDto> { public int Id { get; set; } }
public abstract class DtoService<T> : Service where T : IDto
{
public abstract T Get(DtoGetById<T> Id);
public abstract void Put(T putter);
}
public class PersonService : DtoService<PersonDto>
{
public override PersonDto Get(DtoGetById<PersonDto> Id)
{
//--would retrieve from data persistence layer
return Program.GetNewTestPersonDto();
}
public PersonDto Get(PersonDtoGetById Id)
{
return Program.GetNewTestPersonDto();
}
public override void Put(PersonDto putter)
{
//--would persist to data persistence layer
}
}
public class AppHost : AppHostHttpListenerBase
{
public AppHost()
: base("Test HttpListener",
typeof(PersonService).Assembly
) { }
public override void Configure(Funq.Container container)
{
Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
}
}
}
No, It's a fundamental concept in ServiceStack that each Service requires its own unique Request DTO, see this answer for more examples on this.
You could do:
[Route("/persons/{Id}", "GET")]
public class Persons : DtoGetById<Person> { ... }
But I strongly advise against using inheritance in DTOs. Property declaration is like a DSL for a service contract and its not something that should be hidden.
For more details see this answer on the purpose of DTO's in Services.

Is AutoMapper able to auto resolve types base on existing maps

I have the following code:
[SetUp]
public void SetMeUp()
{
Mapper.CreateMap<SourceObject, DestinationObject>();
}
[Test]
public void Testing()
{
var source = new SourceObject {Id = 123};
var destination1 = Mapper.Map<SourceObject, DestinationObject>(source);
var destination2 = Mapper.Map<ObjectBase, ObjectBase>(source);
//Works
Assert.That(destination1.Id == source.Id);
//Fails, gives the same object back
Assert.That(destination2 is DestinationObject);
}
public class ObjectBase
{
public int Id { get; set; }
}
public class SourceObject : ObjectBase { }
public class DestinationObject : ObjectBase { }
So basically, I want AutoMapper to automatically resolve the destination type to "DestinationObject" based on the existing Maps set up in AutoMapper. Is there a way to achieve this?
You could try the following mapping with the latest version (1.1):
Mapper.CreateMap<ObjectBase,ObjectBase>()
.Include<SourceObject, DestinationObject>();
Mapper.CreateMap<SourceObject, DestinationObject>();

Resources