Access automapper from other class library in .net core - c#

I am using asp.net core 2.0. I have DataAccess project and 20 API service project in my solution. And this number increasing every day. I will use AutoMapper. But I don't want to add automapper from NuGet for all projects. So, I want to add to the only DataAccess solution and adding profiles to DataAccess solution. And I want to call from API project by writing "mapper.Map(originalObject)". We can add Automapper to API project by adding startup.cs. But my DataAccess project is a class library. So it hasn't got startup.cs. How can I do this and can I access automapper from service projects? (I don't want to add automapper from NuGet to API)

There may be many solutions to this problem, I suggest only two of them and these aproaches may also change depending on your choice and scenario. Whether your helper class knows all the types that will be mapped or other user libraries need to register their own POCO classes, whether you prefer creating a mapper... You may also want to cache mappers and return it if it requested again.
Simple code samples are as follows
class Foo
{
public string Name { get; set; }
}
class Bar
{
public string Name { get; set; }
}
static void Main(string[] args)
{
//First approach usage
Bar _bar1 = MapperHelper.MapFrom<Bar>(new Foo() { Name = "bbbbb" });
//Second approach usage
IMyMapper _myMapper = MapperHelper.GetMapperFor<Foo, Bar>();
Bar _bar2 = _myMapper.MapFrom<Bar>(new Foo() { Name = "aaaAAA" });
//Third approach usage
Bar _bar3 = MapperHelper.Map<Bar, Foo>(new Foo() { Name = "cccc" });
}
public interface IMyMapper
{
T MapFrom<T>(object entity);
}
class MyMapper : IMyMapper
{
IMapper mapper;
public MyMapper(IMapper mapper)
{
this.mapper = mapper;
}
public T MapFrom<T>(object entity)
{
return mapper.Map<T>(entity);
}
}
public static class MapperHelper
{
static IMapper staticMapper;
static MapperHelper()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo, Bar>();
});
staticMapper = config.CreateMapper();
}
//First approach, create a mapper and use it from a static method
public static T MapFrom<T>(object entity)
{
return staticMapper.Map<T>(entity);
}
//Second approach (if users need to use their own types which are not known by this project)
//Create you own mapper interface ans return it
public static IMyMapper GetMapperFor<TSource, TDestination>()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<TSource, TDestination>();
});
var _mapper = config.CreateMapper();
return new MyMapper(_mapper);
}
//Third sample, create and use mapper inside a static helper method
//This is for mapping foreign types that this project does not
//include (e.g POCO or model types in other projects)
public static TDestination Map<TDestination, TSource>(TSource entity)
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<TSource, TDestination>();
});
var _mapper = config.CreateMapper();
return _mapper.Map<TDestination>(entity);
}
}
First one creates the configuration for known types and uses this mapper.
Second one creates a mapper and returns it in a wrapper class.
Third one creates and uses a mapper for mapping operation and only returns the mapped object.
Most people do not like static classes and methods since they cause strict unwanted dependencies, they can not be replaced easily. So, creating factory or utility classes, registering them to a dependency injection container and in jecting them where needed is preferred.

Related

.NET 5 Class Library xUnit Tests failing when comparing objects from AutoMapper

I'm currently working out of a .NET 5 class library project with no Startup methods.
The idea behind what I'm, trying to achieve is that a developer can tap into this library and pass in an object. This object will run through the method, AutoMapper will grab the properties that align with the properties in the FirstDTO, then return a DTO that can be used throughout any other projects.
I'm relatively new to the AutoMapper bits and found this article here: How to configure Auto mapper in class library project?
I've liked this approach and have leveraged it to map a dynamic object to a DTO:
Configuration.cs
public static class Configuration
{
private static readonly Lazy<IMapper> Lazy = new Lazy<IMapper>(() =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.AddProfile<MappingProfile>();
});
IMapper mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => Lazy.Value;
}
Almost verbatim approach.
I have my MappingProfile.cs class:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<dynamic, FirstDTO>();
CreateMap<dynamic, SecondDTO>();
}
}
When I call my base class I have the following method:
public class BaseLibraryClass : IBaseLibraryClass
{
public FirstDTO GetFirstObject(dynamic objectSentIn)
{
return Configuration.Mapper.Map<FirstDTO>(objectSentIn);
}
}
Which, in my though, should work.
Now when I write my xUnit unit tests, I'm having a failed Assert.Equal when comparing the FirstDTO with a built DTO:
private readonly IBaseLibraryClass baseLibraryClass = new BaseLibraryClass();
private readonly FirstDTOBuilder firstDTOBuilder = new FirstDTOBuilder();
[Fact]
public void TestSeparateObject()
{
// Arrange
FirstDTO firstDTO = firstDTOBuilder.DefaultDTO().Build();
// Act
FirstDTO result = baseLibraryClass.GetFirstObject(firstDTO);
// Assert
Assert.Equal(firstDTO, result);
}
What ends up happening when I debug this unit test, is that a DTO is built with the assigned properties via the Builder. It passes the DTO into GetFirstObject successfully with the populated properties, but when it hits the return, it returns a FirstDTO object type with properties that are all zeroed out, ultimately failing my unit test.
I feel like it's something glaringly obvious, but I cannot for the life of me figure out what's causing the properties to not map properly.
Any assistance would be greatly appreciated!
Automapper supports mapping from dynamic out of the box, no need to configure anything, so in your case removing profile from the configuration (or removing CreateMap's from the profile) should just work:
public static class Configuration
{
private static readonly Lazy<IMapper> Lazy = new Lazy<IMapper>(() =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
});
IMapper mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => Lazy.Value;
}

AutoMapper 5.2 how to configure

What is the correct way to configure AutoMapper for global use.
I want to set it once and then used though out the app.
i have a strong feeling this is wrong.
in fact i know this is wrong as this calls an new instance.
I want a global config and then how do you call it.
Can not find a good example!
this is what ive got: but its not what im wanting
public static class AutoMapperConfig
{
public static IMapper GetMapper()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<R_Logo, LogoDto>();
//lots more maps...?
});
IMapper mapper = config.CreateMapper();
return mapper;
}
}
and then usage:
var imapper = AutoMapperConfig.GetMapper();
var dest = imapper.Map<R_Logo, LogoDto>(logo);
UPDATE based on: pinkfloydx33
Call this once and then the config is done.
public static class AutoMapperConfig
{
public static void RegisterMappings()
{
AutoMapper.Mapper.Initialize(cfg => {
cfg.CreateMap<R_Logo, LogoDto>();
/* etc */
});
}
}
Here is the steps to configure the automapper in asp.net core mvc.
1. Create the mapping profile class which extends from Profile
public class ClientMappingProfile : Profile
{
public ClientMappingProfile ()
{
CreateMap<R_Logo, LogoDto>().ReverseMap();
}
}
2. Create the AutoMapper Configuration Class and add your mapping profile class here.
public class AutoMapperConfiguration
{
public MapperConfiguration Configure()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ClientMappingProfile>();
});
return config;
}
}
3. How we can use it.
var config = new AutoMapperConfiguration().Configure();
var iMapper = config.CreateMapper();
var dest = iMapper.Map<R_Logo, LogoDto>(logo);
Set this in your StartupConfig or StartUp file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
.....
MappingDTOModelToModel.Configure();
}
}
Configuration of Mappings,
public static class MappingDTOModelToModel
{
private static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<R_Logo, LogoDto>()
.ForMember(x => x.ID,
m => m.MapFrom(a => a.ID))
.ForMember(x => x.FirstName,
m => m.MapFrom(a => a.FirstName)).ReverseMap();
}
}
}
Calling it in a method,
public class MyService
{
public void MyMethod(var model)
{
var myModel = Mapper.Map<LogoDto, R_Logo>(model);
}
}
Hope this helps,
You can use the static mapper api as outlined here.
For example, somewhere in your application, probably during startup you would configure the static (global) mapper using something like:
AutoMapper.Mapper.Initialize(cfg => {
cfg.CreateMap<Type1, Type2>();
/* etc */
});
Then, any time you need to use your "globally" configured mapper, you access it via the static Mapper property (which is an IMapper):
Type1 objectOfType1 = new Type1();
var result = AutoMapper.Mapper.Map<Type2>(objectOfType1);
You then have one mapper that has been configured for all the types/configurations/profiles you provide for the duration of your application without needing to configure individual mapper instances.
In short, you configure it once (perhaps at application startup). The static mapper instance (the IMapper) is then available anywhere throughout your application by accessing it via AutoMapper.Mapper.
Access via this static property is what you refer to as "globally" in your comments. Anywhere you need it just use AutoMapper.Mapper.Map(...) So long as you've called Initialize once first.
Note that if you call Initialize more than once on the static instance, each subsequent call overwrites the existing configuration.
WARNING
In a previous release of AutoMapper, the static mapper was removed. It was later added back in and I don't know if they guarantee that it will remain in future versions. The recommendation is to use your own configured instances of a mapper. You can store it in a static property somewhere if you need it. Otherwise you can look into profiles, etc for easy ways to configure your mapper so that having your own instance isn't necessarily a "hassle".
Our solution to this problem was to first create a selection of attributes that can decorate a class as being "Mappable" (either To, From or Both). Then you can initialize the AutoMapper in a single location, usually post application initialization and use Reflection to dynamically create a map for each instance of the decorated classes.
Here's an example:
var types = _myTypeFinder.Find(type =>
type.IsDefined(typeof(AutoMapperAttribute)) ||
type.IsDefined(typeof(AutoMapperFromAttribute)) ||
type.IsDefined(typeof(AutoMapperToAttribute))
);
Mapper.Initialize(cfg =>
{
foreach (var type in types)
{
AutoMapperHelper.CreateMap(type, cfg);
}
});
I have find best solution for configuration auto mapper in .Net Core.
Multiple Profile.
Just use this:
services.AddSingleton(provider => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new sampleProfileMapper());
}).CreateMapper());

How to set up Automapper in ASP.NET Core

I'm relatively new at .NET, and I decided to tackle .NET Core instead of learning the "old ways". I found a detailed article about setting up AutoMapper for .NET Core here, but is there a more simple walkthrough for a newbie?
I figured it out! Here's the details:
Add the main AutoMapper Package to your solution via NuGet.
Add the AutoMapper Dependency Injection Package to your solution via NuGet.
Create a new class for a mapping profile. (I made a class in the main solution directory called MappingProfile.cs and add the following code.) I'll use a User and UserDto object as an example.
public class MappingProfile : Profile {
public MappingProfile() {
// Add as many of these lines as you need to map your objects
CreateMap<User, UserDto>();
CreateMap<UserDto, User>();
}
}
Then add the AutoMapperConfiguration in the Startup.cs as shown below:
public void ConfigureServices(IServiceCollection services) {
// .... Ignore code before this
// Auto Mapper Configurations
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddMvc();
}
To invoke the mapped object in code, do something like the following:
public class UserController : Controller {
// Create a field to store the mapper object
private readonly IMapper _mapper;
// Assign the object in the constructor for dependency injection
public UserController(IMapper mapper) {
_mapper = mapper;
}
public async Task<IActionResult> Edit(string id) {
// Instantiate source object
// (Get it from the database or whatever your code calls for)
var user = await _context.Users
.SingleOrDefaultAsync(u => u.Id == id);
// Instantiate the mapped data transfer object
// using the mapper you stored in the private field.
// The type of the source object is the first type argument
// and the type of the destination is the second.
// Pass the source object you just instantiated above
// as the argument to the _mapper.Map<>() method.
var model = _mapper.Map<UserDto>(user);
// .... Do whatever you want after that!
}
}
Step To Use AutoMapper with ASP.NET Core.
Step 1. Installing AutoMapper.Extensions.Microsoft.DependencyInjection from NuGet Package.
Step 2. Create a Folder in Solution to keep Mappings with Name "Mappings".
Step 3. After adding Mapping folder we have added a class with Name "MappingProfile" this name can anything unique and good to understand.
In this class, we are going to Maintain all Mappings.
Step 4. Initializing Mapper in Startup "ConfigureServices"
In Startup Class, we Need to Initialize Profile which we have created and also Register AutoMapper Service.
Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
services.AddAutoMapper();
Code Snippet to show ConfigureServices Method where we need to Initialize and Register AutoMapper.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// Start Registering and Initializing AutoMapper
Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
services.AddAutoMapper();
// End Registering and Initializing AutoMapper
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}}
Step 5. Get Output.
To Get Mapped result we need to call AutoMapper.Mapper.Map and pass Proper Destination and Source.
AutoMapper.Mapper.Map<Destination>(source);
CodeSnippet
[HttpPost]
public void Post([FromBody] SchemeMasterViewModel schemeMaster)
{
if (ModelState.IsValid)
{
var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
}
}
I want to extend #theutz's answers - namely this line :
// services.AddAutoMapper(typeof(Startup)); // <-- newer automapper version uses this signature.
There is a bug (probably) in AutoMapper.Extensions.Microsoft.DependencyInjection version 3.2.0. (I'm using .NET Core 2.0)
This is tackled in this GitHub issue. If your classes inheriting AutoMapper's Profile class exist outside of assembly where you Startup class is they will probably not be registered if your AutoMapper injection looks like this:
services.AddAutoMapper();
unless you explicitly specify which assemblies to search AutoMapper profiles for.
It can be done like this in your Startup.ConfigureServices:
services.AddAutoMapper(<assembies> or <type_in_assemblies>);
where "assemblies" and "type_in_assemblies" point to the assembly where Profile classes in your application are specified. E.g:
services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));
I suppose (and I put emphasis on this word) that due to following implementation of parameterless overload (source code from GitHub) :
public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}
we rely on CLR having already JITed assembly containing AutoMapper profiles which might be or might not be true as they are only jitted when needed (more details in this StackOverflow question).
theutz' answer here is very good, I just want to add this:
If you let your mapping profile inherit from MapperConfigurationExpression instead of Profile, you can very simply add a test to verify your mapping setup, which is always handy:
[Fact]
public void MappingProfile_VerifyMappings()
{
var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(mappingProfile);
var mapper = new Mapper(config);
(mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}
I solved it this way (similar to above but I feel like it's a cleaner solution) Works with .NET Core 3.x
Create MappingProfile.cs class and populate constructor with Maps (I plan on using a single class to hold all my mappings)
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source, Dest>().ReverseMap();
}
}
In Startup.cs, add below to add to DI (the assembly arg is for the class that holds your mapping configs, in my case, it's the MappingProfile class).
//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));
In Controller, use it like you would any other DI object
[Route("api/[controller]")]
[ApiController]
public class AnyController : ControllerBase
{
private readonly IMapper _mapper;
public AnyController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Get(int id)
{
var entity = repository.Get(id);
var dto = _mapper.Map<Dest>(entity);
return Ok(dto);
}
}
I like a lot of answers, particularly #saineshwar 's one. I'm using .net Core 3.0 with AutoMapper 9.0, so I feel it's time to update its answer.
What worked for me was in Startup.ConfigureServices(...) register the service in this way:
services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(),
AppDomain.CurrentDomain.GetAssemblies());
I think that rest of #saineshwar answer keeps perfect. But if anyone is interested my controller code is:
[HttpGet("{id}")]
public async Task<ActionResult> GetIic(int id)
{
// _context is a DB provider
var Iic = await _context.Find(id).ConfigureAwait(false);
if (Iic == null)
{
return NotFound();
}
var map = _mapper.Map<IicVM>(Iic);
return Ok(map);
}
And my mapping class:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Iic, IicVM>()
.ForMember(dest => dest.DepartmentName, o => o.MapFrom(src => src.Department.Name))
.ForMember(dest => dest.PortfolioTypeName, o => o.MapFrom(src => src.PortfolioType.Name));
//.ReverseMap();
}
}
----- EDIT -----
After reading the docs linked in the comments by Lucian Bargaoanu, I think it's better to change this answer a bit.
The parameterless services.AddAutoMapper() (that had the #saineshwar answer) doesn't work anymore (at least for me). But if you use the NuGet assembly AutoMapper.Extensions.Microsoft.DependencyInjection, the framework is able to inspect all the classes that extend AutoMapper.Profile (like mine, MappingProfile).
So, in my case, where the class belong to the same executing assembly, the service registration can be shortened to services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(A more elegant approach could be a parameterless extension with this coding).
Thanks, Lucian!
At the latest versions of asp.net core you should use the following initialization:
services.AddAutoMapper(typeof(YourMappingProfileClass));
In my Startup.cs (Core 2.2, Automapper 8.1.1)
services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });
In my data access project
namespace DAL
{
public class MapperProfile : Profile
{
// place holder for AddAutoMapper (to bring in the DAL assembly)
}
}
In my model definition
namespace DAL.Models
{
public class PositionProfile : Profile
{
public PositionProfile()
{
CreateMap<Position, PositionDto_v1>();
}
}
public class Position
{
...
}
For AutoMapper 9.0.0:
public static IEnumerable<Type> GetAutoMapperProfilesFromAllAssemblies()
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var aType in assembly.GetTypes())
{
if (aType.IsClass && !aType.IsAbstract && aType.IsSubclassOf(typeof(Profile)))
yield return aType;
}
}
}
MapperProfile:
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
CreateMap<Foo, FooDto>();
// Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
}
}
In your Startup:
services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
.ToArray());
In Controller or service:
Inject mapper:
private readonly IMapper _mapper;
Usage:
var obj = _mapper.Map<TDest>(sourceObject);
Need to install a package for setting up the automapper.
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
After the AddAutoMapper will be available in services.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup));
}
Create mapper from Employee class to EmployeeDTO.
using AutoMapper;
public class AutomapperProfile: Profile
{
public AutomapperProfile()
{
//Source to destination.
CreateMap<Employee,EmployeeDTO>();
}
}
EmployeeController maps from Employee to EmployeeDTo
using System.Collections.Generic;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[ApiController()]
public class EmployeeController : ControllerBase
{
private readonly IMapper _mapper;
public EmployeeController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet]
public IEnumerable<EmployeeDTO> GetEmployees()
{
/*
Assume it to be a service call/database call
it returns a list of employee, and now we will map it to EmployeeDTO
*/
var employees = Employee.SetupEmployee();
var employeeDTO = _mapper.Map<IEnumerable<EmployeeDTO>>(employees);
return employeeDTO;
}
}
Employee.cs for reference
using System.Collections.Generic;
public class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int Salary { get; set; }
public static IEnumerable<Employee> SetupEmployee()
{
return new List<Employee>()
{
new Employee(){EmployeeId = 1, EmployeeName ="First", Salary=10000},
new Employee(){EmployeeId = 2, EmployeeName ="Second", Salary=20000},
new Employee(){EmployeeId = 3, EmployeeName ="Third", Salary=30000},
new Employee(){EmployeeId = 4, EmployeeName ="Fourth", Salary=40000},
new Employee(){EmployeeId = 5, EmployeeName ="Fifth", Salary=50000}
};
}
}
EmployeeDTO.cs for reference
public class EmployeeDTO
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
}
I am using AutoMapper 6.1.1 and asp.net Core 1.1.2.
First of all, define Profile classes inherited by Profile Class of Automapper. I Created IProfile interface which is empty, the purpose is only to find the classes of this type.
public class UserProfile : Profile, IProfile
{
public UserProfile()
{
CreateMap<User, UserModel>();
CreateMap<UserModel, User>();
}
}
Now create a separate class e.g Mappings
public class Mappings
{
public static void RegisterMappings()
{
var all =
Assembly
.GetEntryAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.SelectMany(x => x.DefinedTypes)
.Where(type => typeof(IProfile).GetTypeInfo().IsAssignableFrom(type.AsType()));
foreach (var ti in all)
{
var t = ti.AsType();
if (t.Equals(typeof(IProfile)))
{
Mapper.Initialize(cfg =>
{
cfg.AddProfiles(t); // Initialise each Profile classe
});
}
}
}
}
Now in MVC Core web Project in Startup.cs file, in the constructor, call Mapping class which will initialize all mappings at the time of application
loading.
Mappings.RegisterMappings();
In .NET 6 you'll need to add the following to the Program.cs file:
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
For ASP.NET Core (tested using 2.0+ and 3.0), if you prefer to read the source documentation:
https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md
Otherwise following these 4 steps works:
Install AutoMapper.Extensions.Microsoft.DependancyInjection from nuget.
Simply add some profile classes.
Then add below to your startup.cs class.
services.AddAutoMapper(OneOfYourProfileClassNamesHere)
Then simply Inject IMapper in your controllers or wherever you need it:
public class EmployeesController {
private readonly IMapper _mapper;
public EmployeesController(IMapper mapper){
_mapper = mapper;
}
And if you want to use ProjectTo its now simply:
var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()
Let’s have a look at how to add Auto mapper into our .NET Core application.
step: 1
The first step is to install the corresponding NuGet package:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
step: 2
After installing the required package, the next step is to configure the services. Let’s do it in the Startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup));
services.AddControllersWithViews();
}
step: 3
Let’s start usage we have a domain object named User:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
In the UI layer, we would have a View Model to display the user information:
public class UserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
step: 4
A good way to organize our mapping configurations is with Profiles. We need to create classes that inherit from Profile class and put the configuration in the constructor:
public UserProfile()
{
CreateMap<User, UserViewModel>();
}
step: 5
Now, let’s define a Controller and use the Auto-Mapping capabilities that we just added:
public class UserController : Controller
{
private readonly IMapper _mapper;
public UserController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
// Populate the user details from DB
var user = GetUserDetails();
UserViewModel userViewModel = _mapper.Map<UserViewModel>(user);
return View(userViewModel);
}
}
First, we inject the mapper object into the controller. Then, we call the Map() method, which maps the User object to the UserViewModel object. Furthermore, pay attention to a local method GetUserDetails that we use for the local data storage.
You can find its implementation in our source code.
Asp.Net Core 2.2 with AutoMapper.Extensions.Microsoft.DependencyInjection.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Domain, DomainDto>();
}
}
In Startup.cs
services.AddAutoMapper(typeof(List.Handler));
services.AddAutoMapper(); didn't work for me. (I am using Asp.Net Core 2.0)
After configuring as below
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<ClientCustomer, Models.Customer>();
});
initialize the mapper
IMapper mapper = config.CreateMapper();
and add the mapper object to services as a singleton
services.AddSingleton(mapper);
this way I am able to add a DI to controller
private IMapper autoMapper = null;
public VerifyController(IMapper mapper)
{
autoMapper = mapper;
}
and I have used as below in my action methods
ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);
To add onto what Arve Systad mentioned for testing. If for whatever reason you're like me and want to maintain the inheritance structure provided in theutz solution, you can set up the MapperConfiguration like so:
var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);
I did this in NUnit.
For AutoMapper 11.0.1 using .NET 7 I started getting this exception:
System.ArgumentException: 'GenericArguments[0], 'System.DateTime', on 'T MaxInteger[T](System.Collections.Generic.IEnumerable`1[T])' violates the constraint of type 'T'.'
Inner Exception
VerificationException: Method System.Linq.Enumerable.MaxInteger: type argument 'System.DateTime' violates the constraint of type parameter 'T'.
See this question:
System.DateTime on 'T MaxInteger[T](System.Collections.Generic.IEnumerable`1[T])' violates the constraint of type T for .NET 7 using AutoMapper 11.0.1
This meant that I could no longer use services.AddAutoMapper(typeof(MappingProfile).Assembly); without an exception.
For AutoMapper.Extensions.Microsoft.DependencyInjection I solved it like this:
services.AddAutoMapper(cfg => cfg.Internal().MethodMappingEnabled = false, typeof(MappingProfile).Assembly);
For Blazor WebAssembly client the solution looked like this:
var mapperConfig = new MapperConfiguration(mc =>
{
//Needed for https://github.com/AutoMapper/AutoMapper/issues/3988
mc.Internal().MethodMappingEnabled = false;
mc.AddProfile(new MappingProfile());
});
//mapperConfig.AssertConfigurationIsValid();
IMapper mapper = mapperConfig.CreateMapper();
builder.Services.AddSingleton(mapper);
about theutz answer ,
there is no need to specify the IMapper mapper parrameter at the controllers constructor.
you can use the Mapper as it is a static member at any place of the code.
public class UserController : Controller {
public someMethod()
{
Mapper.Map<User, UserDto>(user);
}
}

Inject autommaper mapper using NInject

I want to inject a AutoMapper.IMapper single instance as a singleton using NInject.
Actually, I'm un/mapping from/to objects using the AutoMapper static API. It's turned out obsolete and I'm looking forward to taking advantage of the ocassion to inject it using NInject.
Currently, I'm using this code in order to create my IMapper instance:
AutoMapper.Mapper.AddProfile(new UI.Mappings.Profiles.DigitalResourceProfile());
AutoMapper.Mapper.AddProfile(new UI.Mappings.Profiles.DigitalInputProfile());
AutoMapper.Mapper.AddProfile(new UI.Mappings.Profiles.FollowUpActivityProfile());
AutoMapper.Mapper.AddProfile(new UI.Mappings.Profiles.ResourceProfile());
As you can see, I've some profiles to initialize as well.
How should I build all that?
Until now, I've only been able to create a Module but I don't know how to make the bindings up.
public class AutoMapperModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
this.Bind<AutoMapper.MapperConfiguration>().ToProvider<AutoMapperconfigurationProvider>().InSingletonScope();
this.Bind<AutoMapper.IMapper>().To<AutoMapper.Mapper>();
}
private class AutoMapperconfigurationProvider : IProvider<AutoMapper.MapperConfiguration>
{
public object Create(IContext context)
{
AutoMapper.MapperConfiguration instance = new AutoMapper.MapperConfiguration(
cfg =>
{
cfg.AddProfile(new UI.Mappings.Profiles.DigitalResourceProfile());
cfg.AddProfile(new UI.Mappings.Profiles.DigitalInputProfile());
cfg.AddProfile(new UI.Mappings.Profiles.FollowUpActivityProfile());
cfg.AddProfile(new UI.Mappings.Profiles.ResourceProfile());
}
);
return instance;
}
public Type Type
{
get { throw new NotImplementedException(); }
}
}
}
I'd like to write this sentence each time I need a IMapper to map objects:
IMapper mapper = kernel.Get<IMapper>();
Any ideas?
I investigated this.
And I found the following:
In documentation we can found that we can do something like:
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<SomeProfile>();
cfg.CreateMap<Source, Dest>();
});
var mapper = config.CreateMapper(); // option 1
// or
var mapper = new Mapper(config); // option 2
Your code would work with using the option 2, because you have binding for configuration and for mapper.
But here we have two problems.
1) You need to change your first binding to bind MapperConfiguration as an interface IConfigurationProvider because the constructor of Mapper needs it:
public Mapper(IConfigurationProvider configurationProvider)
: this(configurationProvider, configurationProvider.ServiceCtor)
{
}
But here we got the second problem.
2) In automapper version 4.2.1 (as I believe you downloaded from NuGet) the Mapper class has only internal constructors. It has a public constructors in documentation (which is weird) and I think will have in a future release.
Therefore, for now you need to modify Load method to use option 1:
public override void Load()
{
this.Bind<AutoMapper.MapperConfiguration>().ToProvider<AutoMapperconfigurationProvider>().InSingletonScope();
this.Bind<AutoMapper.IMapper>().ToMethod(context => context.Kernel.Get<MapperConfiguration>().CreateMapper());
}
And then you can call IMapper mapper = kernel.Get<IMapper>(); to get the mapper instance.
It will use public IMapper CreateMapper() => new Mapper(this); and will create the instance of IMapper. Note: you need to use MapperConfiguration (not IConfiguration provider) to call CreateMapper method, it has the same situation as with public/internal constructors of Mapper.
That should help.

Using Registry in StructureMap to create a pluggable architechture

I have a web project and I want it to work with ravendb and ravendb-embedded.
So this is how I think I should solve it.
Two projects, MvcRavendb and MvcRavendb-Embedded where the two projects references two different nuget packages, one for ravendb and one for ravendb embedded.
In my core project I have an interface IDocumentStoreInitializer which has one method, IDocumentStore InitializeDocumentStore()
MvcRavenDb and MvcRavenDb-Embedded has one class like this
public class RegisterRavenDb : IDocumentStoreInitializer {
public IDocumentStore InitializeDocumentStore() {
return new DocumentStore OR EmbeddableDocumentStore();
}
}
Then I have a class that registers the concrete implementation like this
public class RavenRegistry : Registry {
public RavenRegistry() {
For<IDocumentStoreInitializer>().Use<RegisterRavenDb>();
}
}
So far so good but then I have bootstrapper that configures structuremap like this
public class Bootstrapper {
public static IContainer Initialize() {
ObjectFactory.Initialize(x =>
{
// here I want to use the registered concrete implmentaiton of IDocumentStore
var documentStore = new DocumentStore { ConnectionStringName = "RavenDB" };
documentStore.Initialize();
}
}
}
So how can I tell structuremap to use InitializeDocumentStore from the RavenRegistry class?
Maybe I have missed something or I'm taking the wrong approach here
Just use the EmbeddableDocumentStore instance, using the connection string, you can control whatever it will be embedded or server/client.
This should do as you wish. In OnCreation the ConnectionString can be set, too.
ObjectFactory.Initialize(x =>
{
// here I want to use the registered concrete implmentaiton of IDocumentStore
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.AssembliesFromApplicationBaseDirectory();
scan.LookForRegistries();
});
x.For<IDocumentStore>().Use(c =>
c.GetInstance<IDocumentStoreInitializer>().
InitializeDocumentStore()).OnCreation<IDocumentStore>(z => z.Initialize());
});

Categories