Introduction.
We would create profile dynamically using reflection and using Autofac we would inject the auto-mapper.
Problem
When we create a lot of profiles. We need to add them in the configuration file as shown below.
Example.
public IMapper ConfigureMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<Employee>();
cfg.AddProfile<Author>();
cfg.AddProfile<Title>();
.
.
.
.
.
.
.
.
.
.
cfg.AddProfile<Book>();
.
.
//30 more profiles
.
.
//30 more profiles
});
return config.CreateMapper();
}
As the project size grows adding the profiles become tedious and maintainability takes a hit.
As the project size grows adding the profiles become tedious and maintainability takes a hit.
Solution
We would create the profile and them dynamically using reflection code and use Autofac to inject them.
By creating auto-mapper profiles dynamically what do we achieve.
1.Open Closed Principle is not violated.
2.Developers would not add any profiles manually.
2.Developers would not add any profiles manually.
3.Code is neatly maintained.
So let’s achieve the same.
Step 1.Create a Common Library in the solution which would be
referred by all the other projects in the application.
Step 2.Create a class in Common Library called Auto Mapper Profile which extends a
base class “Profile” which is under the auto-mapper dll
public class AutoMapperProfile: Profile
{
}
3.Now the next step we would create required profiles which
extends “AutoMapperProfile” so you would create profiles as given below.
public class EmployeeProfile : AutoMapperProfile
{
public EmployeeProfile()
{
CreateMap<Employee, EmployeeModel>();
}
}
Note : If any Profiles that do not extend AutoMapperProfile class then the code would not work.
Step 3.We then need to add the AutoMapperConfiguration class. Where
we create a Method that returns IMapper.
In the above code we have used the reflection to find all the profiles where "AutoMapperProfile" class is extended
If you closely observe in the above code we have used ForEach. We have created an Enumerable extension which create an actionable item for each found profile.
public class AutoMapperConfiguration
{
public IMapper Configure()
{
var profiles =AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(a => typeof(AutoMapperProfile).IsAssignableFrom(a));
// Initialize AutoMapper with each instance of the profiles found.
var mapperConfiguration = new MapperConfiguration(a => profiles.ForEach(a.AddProfile));
return mapperConfiguration.CreateMapper();
}
}
In the above code we have used the reflection to find all the profiles where "AutoMapperProfile" class is extended
If you closely observe in the above code we have used ForEach. We have created an Enumerable extension which create an actionable item for each found profile.
public static class EnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> enumerable,
Action<T> action)
{
foreach (T item in enumerable) { action(item); }
}
}
Step 4: Once we are done with the Creating the Mapper we will return the IMapper to the Autofac object where we create instance for the mapper.
//Creating
an Instance for the Mapper
builder.RegisterInstance(new AutoMapperConfiguration().Configure()).As<IMapper>();
Step 5: After doing the above steps register the Auto-mapper in the
Autofac.
//Creating
an Instance for the Mapper
builder.RegisterInstance(new AutoMapperConfiguration().Configure()).As<IMapper>();
Step 6:To use the Automapper in the Project just Inject the
IMapper in the constructor and it works automatically.
public EmployeeManager(IEmployeeLogic employeelogic, IMapper mapper)
{
if (employeelogic==
null) throw new ArgumentNullException(nameof(employeelogic));
_employeelogic = employeelogic;
if (mapper == null) throw new ArgumentNullException(nameof(mapper));
_mapper = mapper;
}
private async Task<EmployeeModel>
GetEmployeeModel(Employee employee)
{
var employeeInfoModel = await _exampleCode.GetEmployeeRepo(employee);
return _mapper.Map<EmployeeModel>(employeeInfoModel );
}
Just follow the above steps and everything would work
seemelesly.
Code is available in the below repository.
https://github.com/BatIronMan007/AutoMapperReflection
Code is available in the below repository.
https://github.com/BatIronMan007/AutoMapperReflection
Hi Vaibhav,
ReplyDeleteI got error System.Reflection.ReflectionTypeLoadException. Did i miss something to do ?
If you have followed the above steps. It should work just fine. I will create a git hub repository and publish the code. It would take some time.
ReplyDeleteHi Hendi,
ReplyDeleteDid you happen to resolve the issue..?
Thanks
Hi Hendi,
ReplyDeleteI have uploaded the sample code in the below repository. Please let me know your thoughts.
https://github.com/BatIronMan007/AutoMapperReflection
Thank you so much. Thank you for the Motivation
ReplyDeleteHello Vaibhav, I am extending Profile class for createMap , than I am registering automapper as "services.AddAutoMapper();" in configureService method of startup.cs. than I am injecting IMapper _mapper in my controller for web project it is working fine but while unit testing project I am creating mock object of IMapper whereas I don't need mock object I want to create object of IMapper because using mock object I need to explicitly map objects just like:
ReplyDeletevar _mapper = new Mock();
_mapper.Setup(m => m.Map(It.IsAny())).Returns((Student student) =>
{
var studentView = new StudentViewModel()
{
Id = student.Id,
CollegeId = student.CollegeId,
FirstName = student.person.FirstName,
LastName = student.person.LastName,
StudentRegId = student.StudentRegId
};
return studentView;
});
I want to use the object of IMapper that i injected in Startup.cs is there any way that i can do it? please help me with this issue.