First of all, sorry my English :)
I am using AutoMapper to map between classes.
I have structure of classes like the below.
public class OrderDto {
int Id { get; set; }
}
public class OrderDtoList {
OrderDto[] Orders { get; set; }
}
public class Order {
int Id { get; set; }
}
My issue is that, I want to map OrderDtoList object to List using AutoMapping Profile.
public class OrderMappingProfile : Profile {
public OrderMappingProfile() {
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>(); // <<<<<<<< I cannot figure out this.
}
}
But I cannot figure out how to write CreateMap<>() function.
Please help.
You can create a class which implements ITypeConverter< OrderDtoList, List > and create the mapping using ConvertUsing method.
public class OrderDtoListMapper : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
Then, you can create your mapping like this:
public class MapperProfile : Profile
{
public MapperProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<OrderDtoListMapper>();
}
}
Hope this is what you were looking for!
It's a little difficult to get it from scratch, but it is possible. You have to create a ITypeConverter<,> and apply this to be used for the conversion.
Be aware, that the ConvertUsing() method has also an overload to simply add an inline function, but you need to have access to the mapper itself to call it for mapping the inner objects to the desired result objects and that's only available with the type converter interface.
public class OrderMappingProfile : Profile
{
public OrderMappingProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<CustomConverter>();
}
}
public class CustomConverter : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
With this in place, you can create the desired list right from the DTO:
public static class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<OrderMappingProfile>());
var mapper = config.CreateMapper();
var orderList = new OrderDtoList { Orders = Enumerable.Range(1, 10).Select(i => new OrderDto { Id = i }).ToArray() };
var orders = mapper.Map<List<Order>>(orderList);
}
}
As Lucian mentioned, there is an overload of ConvertUsing() that provides the context. So you could also inline this, instead of using an own class:
// Use lambda method
CreateMap<OrderDtoList, List<Order>>()
.ConvertUsing((source, _, context) => context.Mapper.Map<List<Order>>(source.Orders));
// Use static method
CreateMap<OrderDtoList, List<Order>>().ConvertUsing(ListConverter);
private static List<Order> ListConverter(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
below is a generic extension, if you have not used an extension before please observe that the GenericExtensions class is a static class
public static class GenericExtensions {
public static object Map<T>(this T source)
{
var fullName = source.GetType().FullName;
var sourceType = source.GetType();
var baseType = ObjectContext.GetObjectType(source.GetType());
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(sourceType, baseType));
var mapper = config.CreateMapper();
var entity = mapper.Map(source, sourceType, baseType);
return entity;
}
}
public static List<T> Map<T>(this List<T> original)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(T), typeof(T)));
var mapper = config.CreateMapper();
return original.Select(mapper.Map<T, T>).ToList();
}
Usage:
for single entity:
var originalObject = new Order();
originalObject.Id = 4;
var clonedObject = originalObject.Map();
for list of entities:
var objectList = db.ORDERS.ToList();
var clonedList = objectList.Map();
Hope this helps!
Related
I am using static class for mapping my entities. But if I use the following code, it is not working for converting lists and nested entities;
public static class MapperUtil<TSource, TDestination>
{
private static readonly Mapper _mapper = new Mapper(new MapperConfiguration(
cfg =>
{
cfg.CreateMap<TDestination,TSource>().ReverseMap();
}));
public static TDestination Map(TSource source)
{
return _mapper.Map<TSource,TDestination>(source);
}
}
But if I use the following code it works well.
var mapper = new Mapper(new MapperConfiguration(cfg =>
{
cfg.CreateMap<List<User>, List<UserDto>>().ReverseMap();
}));
List<UserDto> userDto = mapper.Map<List<User>,List<UserDto>> (users);
Can anyone help me? (I am newbie).
And is it good idea using static class for mapping? What is your solution for mapping as static class?
You should remove List in CreateMap method and create map for your types:
var mapper = new Mapper(new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>().ReverseMap();
}));
Finally:
List<UserDto> userDto = mapper.Map<List<UserDto>>(users);
if you are using generic type for mapping, try below code
public class Source<T> {
public T Value { get; set; }
}
public class Destination<T> {
public T Value { get; set; }
}
// Create the mapping
var configuration = new MapperConfiguration(cfg => cfg.CreateMap(typeof(Source<>), typeof(Destination<>)));
In my application, I need to interact with two databases. I have two domain classes which are located in two different databases. I also have a generic repository pattern which accepts an UoW in its constructor. I am looking a way to inject appropriate UoW based on Domain class. I do not want to write second generic repository for the second database.. Is there any neat solution?
public interface IEntity
{
int Id { get; set; }
}
Located in Database A
public class Team: IEntity
{
public int Id { get; set; }
public string Name{ get; set; }
}
Located in Database B
public class Player: IEntity
{
public int Id { get; set; }
public string FullName { get; set; }
}
I also have a generic repository pattern with UoW
public interface IUnitOfWork
{
IList<IEntity> Set<T>();
void SaveChanges();
}
public class DbADbContext : IUnitOfWork
{
public IList<IEntity> Set<T>()
{
return new IEntity[] { new User() { Id = 10, FullName = "Eric Cantona" } };
}
public void SaveChanges()
{
}
}
public class DbBDataContext: IUnitOfWork
{
public IList<IEntity> Set<T>()
{
return new IEntity[] { new Tender() { Id = 1, Title = "Manchester United" } };
}
public void SaveChanges()
{
}
public interface IRepository<TEntity> where TEntity: class, IEntity
{
IList<IEntity> Table();
}
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
protected readonly IUnitOfWork Context;
public BaseRepository(IUnitOfWork context)
{
Context = context;
}
IList<IEntity> IRepository<TEntity>.Table()
{
return Context.Set<TEntity>();
}
}
I've already found articles saying that Autofac overrides the registration with the last value. I know my problem is how DbContexts are registered.
var builder = new ContainerBuilder();
// problem is here
builder.RegisterType<DbADbContext >().As<IUnitOfWork>()
builder.RegisterType<DbBDbContext >().As<IUnitOfWork>()
builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>));
var container = builder.Build();
I inspired from #tdragon's answer.
The first step is registering Named DbContext
builder.RegisterType<Database1>()
.Keyed<IUnitOfWork>(DbName.Db1)
.Keyed<DbContext>(DbName.Db1).AsSelf().InstancePerRequest();
builder.RegisterType<Database2>()
.Keyed<IUnitOfWork>(DbName.Db2)
.Keyed<DbContext>(DbName.Db2).AsSelf().InstancePerRequest();
Please note that DbName is just an enum.
The following code scans the data access layer assembly for finding Domain classes. Then, it registers ReadOnlyRepository and BaseRepository. the place of this code is in DIConfig
Type entityType = typeof(IEntity);
var entityTypes = Assembly.GetAssembly(typeof(IEntity))
.DefinedTypes.Where(t => t.ImplementedInterfaces.Contains(entityType));
var baseRepoType = typeof(BaseRepository<>);
var readOnlyRepoType = typeof(ReadOnlyRepository<>);
var baseRepoInterfaceType = typeof(IRepository<>);
var readOnlyRepoInterfaceType = typeof(IReadOnlyRepository<>);
var dbContextResolver = typeof(DbContextResolverHelper).GetMethod("ResolveDbContext");
foreach (var domainType in entityTypes)
{
var baseRepositoryMaker = baseRepoType.MakeGenericType(domainType);
var readonlyRepositoryMarker = readOnlyRepoType.MakeGenericType(domainType);
var registerAsForBaseRepositoryTypes = baseRepoInterfaceType.MakeGenericType(domainType);
var registerAsForReadOnlyRepositoryTypes = readOnlyRepoInterfaceType.MakeGenericType(domainType);
var dbResolver = dbContextResolver.MakeGenericMethod(domainType);
// register BaseRepository
builder.Register(c => Activator.CreateInstance(baseRepositoryMaker, dbResolver.Invoke(null, new object[] { c }))
).As(registerAsForBaseRepositoryTypes).InstancePerRequest(jobTag);
//register readonly repositories
builder.Register(c => Activator.CreateInstance(readonlyRepositoryMarker, dbResolver.Invoke(null, new object[] { c })))
.As(registerAsForReadOnlyRepositoryTypes).InstancePerRequest(jobTag);
}
The following methods try to find DbSet in each DbContext in order to find out the Domain Classes belongs to which DataContext/Database.
public class DbContextResolverHelper
{
private static readonly ConcurrentDictionary<Type, DbName> TypeDictionary = new ConcurrentDictionary<Type, DbName>();
public static DbContext ResolveDbContext<TEntity>(IComponentContext c) where TEntity : class, IEntity
{
var type = typeof(DbSet<TEntity>);
var dbName = TypeDictionary.GetOrAdd(type, t =>
{
var typeOfDatabase1 = typeof(Database1);
var entityInDatabase1 = typeOfDatabase1 .GetProperties().FirstOrDefault(p => p.PropertyType == type);
return entityInDatabase1 != null ? DbName.Db1: DbName.Db2;
});
return c.ResolveKeyed<DbContext>(dbName);
}
}
What about this:
builder.RegisterType<DbContextBase>().As<IUnitOfWork>()
And
DbADataContext: DbContextBase,IUnitOfWork
DbBDataContext: DbContextBase,IUnitOfWork
Or in your registration you can just do something like :
containerBuilder.RegisterGeneric(typeof(DbADataContext<>)).Named("DbADataContext", typeof(IUnitOfWork<>));
containerBuilder.RegisterGeneric(typeof(DbBDataContext<>)).Named("DbBDataContext", typeof(IUnitOfWork<>));
If you want to keep single BaseRepository and its interface, you have to somehow configure, with entity would be handled by which DbContext. It could be done in registration part of application, but in that case you cannot register your BaseRepostory<T> as open generic, but be explicit in your registrations, like this:
containerBuilder.RegisterType<DbADataContext>().Named<IUnitOfWork>("A");
containerBuilder.RegisterType<DbBDataContext>().Named<IUnitOfWork>("B");
containerBuilder.Register(c => new BaseRepository<Team>(c.ResolveNamed<IUnitOfWork>("A")).As<IRepostory<Team>>();
containerBuilder.Register(c => new BaseRepository<Player>(c.ResolveNamed<IUnitOfWork>("B")).As<IRepository<Player>>();
(just proof of concept, code not tested)
Autofac is not smart enough to know "automatically" which unit of work you want to use in each of your repository.
Is there a way to access a mapper in type converter. Right now, which is bad, I have to do ObjectFactory.GetInstance to get instance of the session from StructureMap. If I was able to get reference to mapper I could use that to get instance of the entity, like mapper.Map and avoid having static method call in type converter.
So this code
public class AddBaseProductCommandConverter : TypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
protected override AddBaseProductCommand ConvertCore(AddBaseProductService.AddBaseProduct source)
{
var session = ObjectFactory.GetNamedInstance<ISession>("PRDProd");
var marketingPlan = session.Get<MarketingPlan>(source.BaseProductInfo.MarketingPlanId);
var baseProductTemplate = session.Get<BaseProductTemplate>(source.BaseProductInfo.Code);
var benefitPeriod = session.Get<BenefitPeriod>(source.BaseProductInfo.BenefitPeriodCode);
var insuranceServiceType = session.Get<InsuranceServiceType>(source.BaseProductInfo.ServiceTypeCode);
var command = new AddBaseProductCommand
{
MarketingPlan = marketingPlan,
BaseProductTemplate = baseProductTemplate,
BenefitPeriod = benefitPeriod,
InsuranceServiceType = insuranceServiceType
};
return command;
}
}
Could be rewritten like this (I build a map for each persistent type that NHIbernate recognizes, during configuration phase)
public class AddBaseProductCommandConverter : TypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
protected override AddBaseProductCommand ConvertCore(AddBaseProductService.AddBaseProduct source)
{
var mapper = ????
var marketingPlan = mapper.Map<int,MarketingPlan(source.BaseProductInfo.MarketingPlanId);
var baseProductTemplate = mapper.Map<string,BaseProductTemplate>(source.BaseProductInfo.Code);
... and so on ...
return command;
}
}
You can implement ITypeConverter instead of TypeConverter, then use the argument context to get the mapper using context.Engine.Mapper, something like this:
public class AddBaseProductCommandConverter : ITypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
public AddBaseProductCommand Convert(ResolutionContext context)
{
if (context.SourceValue == null || !(context.SourceValue is AddBaseProductService.AddBaseProduct))
throw new AutoMapperMappingException();
var mapper = context.Engine.Mapper;
...
}
}
For anyone stumbling on this who is using a a more recent version of Automapper (e.g. 8), the ITypeConverter and ResolutionContext definitions have changed a bit from the previous answer. You can access the mapper through the Mapper member on the context. e.g.
public class AddBaseProductCommandConverter : ITypeConverter<AddBaseProductService.AddBaseProduct, AddBaseProductCommand>
{
public AddBaseProductCommand Convert(AddBaseProductService.AddBaseProduct source, AddBaseProductCommand destination, ResolutionContext context)
{
var mapper = context.Mapper;
...
}
}
I have a ASP.NET 5 (running on 4.6.2, not Core) application. I wanted to use the ProjectTo<>() method of AutoMapper to project the results from the database to my viewmodels.
I've tried alot of tests, but it seems that the map solely cannot be found when using the ProjectTo<>(). Using mapper.Map<>() on different locations with the same model and viewmodel perfectly works.
I guess there is something wrong with how AutoMapper works with my DI (Autofac), but I can't figure out what.
Anyway, the code:
Startup.Cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
(...)
// Autofac DI
AutofacContainer = AutofacLoader.Configure(services).Build();
return AutofacContainer.Resolve<IServiceProvider>();
}
AutofacLoader.cs
public static ContainerBuilder Configure(IServiceCollection services)
{
var builder = new ContainerBuilder();
(...)
// AutoMapper
builder.RegisterModule<AutoMapperModule>();
if (services != null)
{
builder.Populate(services);
}
return builder;
}
AutoMapperModule.cs
public class AutoMapperModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var mapping = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new Core.Mappings.AutoMapperProfileConfiguration());
cfg.AddProfile(new Dieet.Core.Mappings.AutoMapperProfileConfiguration());
});
builder.RegisterInstance(mapping.CreateMapper()).As<IMapper>().AutoActivate();
}
}
The test that fails with 'Missing map from Patient to PatientViewModel. Create using Mapper.CreateMap'.
[Fact]
public async void InfohosServiceReturnsPatientViewModels()
{
var db = _container.Resolve<IInfohosDb>();
var search = new PaginatedSearchBase();
search.OrderBy = "Naam";
var mapper = _container.Resolve<IMapper>();
var result = await search.PagedResultAsAsync<Patient,PatientViewModel >(null,db.Patienten,mapper);
}
PaginatedSearchBase
public class PaginatedSearchBase
{
public string OrderBy { get; set; }
public bool OrderDescending { get; set; }
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
And finally the extension that calls the ProjectTo
public static class PagedResultExtensions
{
public static async Task<PagedResult<T>> PagedResultAsync<T>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<T>
{
Results = await query.ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
public static async Task<PagedResult<TAs>> PagedResultAsAsync<T, TAs>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context, IMapper mapper) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<TAs>
{
----------> Results = await query.ProjectTo<TAs>(mapper).ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
private static IQueryable<T> PrepareQuery<T>(PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context,
out int totalCount) where T : class
{
var query = context.AsQueryable();
if (whereCollection != null)
{
foreach (var w in whereCollection)
{
if (w != null)
{
query = query.Where(w);
}
}
}
// Order by
query = query.OrderBy($"{vm.OrderBy} {(vm.OrderDescending ? "DESC" : "ASC")}");
// Total rows
totalCount = query.Count();
// Paging
query = query.Skip((vm.Page - 1)*vm.PageSize).Take(vm.PageSize);
return query;
}
}
For information, I'm using versions:
"Autofac": "4.0.0-rc1-177"
"Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177"
"AutoMapper": "4.2.1"
Edit:
A new test that I did to check if the mappings really work:
var mapper = _container.Resolve<IMapper>();
var p = new Patient();
p.Naam = "Test";
var vm = mapper.Map<PatientViewModel>(p);
vm.Naam.ShouldBeEquivalentTo("Test");
This test passes
Edit 2:
When I use the Map<> in a Select() instead, it works too, so it's really the ProjectTo<>() that fails:
var results = await query.ToListAsync();
return new PagedResult<TAs>
{
Results = results.Select(mapper.Map<TAs>).ToList(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
This works, but it requires the mapper to be included and not be injected and it doesn't use the ProjectTo that Automapper has for database access...
I ran into the same issue but managed to get it working. This is happening due to the recent move, by Automapper, away from having the entire API use static methods. Now that everything is instance-based, the static extension methods no longer know about the mapping configuration and they now have to be passed into the method. I ended up registering an instance of the MapperConfiguration as IConfigurationProvider (I'm using Unity)
container.RegisterInstance(typeof (IConfigurationProvider), config);
This is injected into my query handler:
[Dependency]
public IConfigurationProvider MapperConfigurationProvider { get; set; }
Finally, the MapperConfigurationProvider is passed to the call to ProjectTo:
.ProjectTo<Payment>(MapperConfigurationProvider);
Hope this helps.
You don't have to specifically add ConfigurationProvider to DI. If you already added the IMapper to DI, than you can read ConfigurationProvider from the Mapper itself. Example: I had the same problem and created a base class containing IMapper that got injected:
public abstract class ServiceBase
{
public IMapper Mapper { get; set; }
}
This class was inherited in all my Services that used AutoMapper. Now every time any of my services needed to Map something, they did so:
return context.SomeEntity
.Where(e => e.Id == filter.Id)
.ProjectTo<EntityDto>(Mapper.ConfigurationProvider).ToList();
With Mapper being injected.
As long as you put the completely configured Mapper in DI, you're ok.
container.Register(Component.For<IMapper>().UsingFactoryMethod(x =>
{
return new AutoMapperConfig().ConfigureMapper();
})
);
I have a class say 'AllInvoices', the structure of which is as below :
public class ActiveInvoices
{
public string InvoiceId { get; set; }
public string InvoiceIssueDate { get; set; }
public string InvoiceTransactionDate { get; set; }
public string InvoiceExpiryDate { get; set; }
}
The mapping class for Entity ActiveInvoices is
public class ActiveInvoicesMap : ClassMap<ActiveInvoices>
{
public ActiveInvoicesMap()
{
Id(x => x.InvoiceId);
Map(x => x.InvoiceIssueDate);
Map(x => x.InvoiceTransactionDate);
Map(x => x.InvoiceExpiryDate);
}
}
Now with this entity I search for Active Invoices in database with the following class
public class SearchInvoices
{
public readonly IRepository<ActiveInvoices> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<ActiveInvoices> activeInvoicesRepository)
{
latestActiveInvoicesRepository = activeInvoicesRepository;
}
public List<ActiveInvoices> GetActiveInvoices()
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
To Search Active Invoices I call the Search Class method 'GetActiveInvoices()' from a workflow class which looks like below :
public class CurrentWorkFlow
{
public void GetActiveInvoices()
{
var invoiceSearch = new SearchInvoices(IRepository <ActiveInvoices> repository);
}
}
Now the issue in hand is that I need to make class 'SearchInvoices' generic to support all other possible types that i would create like 'ExpiredInvoices', 'ArchivedInvoices', 'FutureInvoices' etc and not just only for type 'ActiveInvoices'.
These new types may or may not have the same structure as 'ActiveInvoices'.
I have tried to use dynamic but thought of asking experts around here if they have any better ideas to implement the required functionality
in most optimized generic manner.
Regrets for being very detailed and lengthy in asking but i thought to include as many details as i can. Hope it goes well with you folks.
Couldn't you make a generic repository like this? -
interface IDomain{
}
class ExpiredInvoices: IDomain{
}
class ActiveInvoices: IDomain{
}
interface IRepository{
}
class Repsoitory: IRepository {
public static IList<T> Get<T>() where T: IDomain //default one
{
using (ISession session = OpenEngineSession())
{
return session.Query<T>().ToList();
}
}
public static IList<T> Get<T>(Expression<Func<T, bool>> expression) where T: IDomain // overloaded get with linq predicate
{
using (ISession session = OpenEngineSession())
{
return session.Query<T>().Where(expression).ToList();
}
}
}
Then use it like -
var repo = // get IRepository
var activeInvoices = repo.Get<ActiveInvoices>();
var expiredInvoices = repo.Get<ExpiredInvoices>();
EDIT: As Repository cannot be changed, suggested by OP
If you cannot change the repository, then I would suggest making the search service interface dependent, rather than concrete class -
interface IInvoice{
}
class ExpiredInvoices: IInvoice{
}
class ActiveInvoices: IInvoice{
}
public class SearchInvoices
{
public readonly IRepository<IInvoice> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<IInvoice> activeInvoicesRepository)
{
latestInvoicesRepository = activeInvoicesRepository;
}
public List<T> GetActiveInvoices<T>() where T: IInvoice
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
Then call like -
var ss = new SearchService(IRepository <ActiveInvoices> repository);
var items = ss.GetActiveInvoices<ActiveInvoices>();
Or,
public class SearchInvoices<T> where T: IInvoice
{
public readonly IRepository<T> latestActiveInvoicesRepository;
public SearchInvoices(IRepository<T> activeInvoicesRepository)
{
latestInvoicesRepository = activeInvoicesRepository;
}
public List<T> GetActiveInvoices()
{
var listOfActiveInvoices = latestActiveInvoicesRepository.GetAll();
return listOfActiveInvoices;
}
}
then call like -
var ss = new SearchService<ActiveInvoices>(IRepository <ActiveInvoices> repository);
var items = ss.GetActiveInvoices();
Whichever suits you.