Исключение при использовании IMapper. Альтернативный способ - реализация своего маппера
Решил попробовать перейти с factory на automapper, т.к. это здорово сокращает количество кода.
public void ConfigureServices(IServiceCollection services)
{
// someCode
services.AddAutoMapper(typeof(GuestProfile));
// someCode
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// someCode
app.UseOwin(x => x.UseNancy()); //здесь исключение
// someCode
}
Исключение:
System.InvalidOperationException: "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details." TinyIoCResolutionException: Unable to resolve type: AutoMapper.IMapper
public class TestModule : BaseModule
{
private readonly IMapper _mapper;
public TestModule(IMapper mapper) : base()
{
_mapper= mapper;
}
}
public abstract class BaseModule : NancyModule
{
public BaseModule() : base("/") { }
}
GuestProfile:
public class GuestProfile : Profile
{
public GuestProfile()
{
CreateMap<GuestEntity, GuestModel>().ReverseMap();
}
}
GuestEntity:
public class GuestEntity : BaseEntity
{
public string Name { get; set; }
public int Rank { get; set; }
public virtual OrderEntity Order { get; set; }
}
BaseEntity:
public abstract class BaseEntity : IEntity
{
public Guid Id { get; set; }
public DateTime CreatedTime { get; set; } = DateTime.Now;
public Guid WaiterCreatedId { get; set; }
public DateTime? UpdateTime { get; set; }
public Guid WaiterUpdatedId { get; set; }
[DefaultValue("false")]
public bool IsDeleted { get; set; } = false;
}
GuestModel:
public class GuestModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
public DateTime CreatedDate { get; set; } = DateTime.Now;
public bool IsDeleted { get; set; } = false;
}
Пробовал так же в Configure менять на:
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new GuestProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
и на:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Ничего не меняется - ошибка всегда одна и та же.
Библиотеки, которые используются:
AutoMapper
AutoMapper.Extentions.Microsoft.DependencyInjection
Nancy
Microsoft.AspNetCore.Owin
Microsoft.AspNetCore.Server.Kestrel
Ответы (1 шт):
Создаём интерфейс:
public interface IMapper
{
TOut Map<TIn, TOut>(TIn source);
IEnumerable<TOut> Map<TIn, TOut>(IEnumerable<TIn> source);
}
Создаём реализацию интерфейса:
public sealed class Mapper : IMapper
{
public TOut Map<TIn, TOut>(TIn source)
{
var typeIn = typeof(TIn);
var typeOut = typeof(TOut);
var outInstanceConstructor = typeOut.GetConstructor(Array.Empty<Type>());
Func<TOut> _activator = outInstanceConstructor == null
? throw new KeyNotFoundException($"Default constructor for '{typeOut}' not found")
: Expression.Lambda<Func<TOut>>(Expression.New(outInstanceConstructor)).Compile();
var instanceOut = _activator();
var propertiesIn = typeIn.GetProperties();
var propertiesOut = typeOut.GetProperties().ToDictionary(x => x.Name);
foreach (var propertyIn in propertiesIn)
{
if (propertiesOut.TryGetValue(propertyIn.Name, out var outProperty))
{
var sourceValue = propertyIn.GetValue(source);
outProperty.SetValue(instanceOut, sourceValue);
}
}
return instanceOut;
}
public IEnumerable<TOut> Map<TIn, TOut>(IEnumerable<TIn> source)
{
List<TOut> resultList = new();
resultList.AddRange(from item in source
select Map<TIn, TOut>(item));
return resultList;
}
}
Идём в Startup (asp.net) и прописываем в методе ConfigureServices следующее:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMapper, Mapper>();
}
Готово. Теперь можем пользоваться маппером. Передавать его в следующем виде:
public class TestModule : BaseModule
{
private readonly IMapper _mapper;
public TestModule(IMapper mapper) : base()
{
_mapper = mapper;
}
}
Пример использования с базой данных:
public abstract class BaseService
{
protected IDbRepository DbRepository { get; }
protected IMapper Mapper { get; }
public BaseService(IDbRepository dbRepository, IMapper mapper)
{
DbRepository = dbRepository;
Mapper = mapper;
}
protected virtual async Task<Guid> Create<TModel, TEntity>(TModel model) where TEntity : class, IEntity
{
var entity = Mapper.Map<TModel, TEntity>(model);
entity.CreatedTime = DateTime.Now;
entity.WaiterCreatedId = ConnectEntity.Id;
entity.Version = 1;
entity.IsDeleted = false;
var result = await DbRepository.Add(entity);
await DbRepository.SaveChangesAsync();
return result;
}
}
(не забудьте создать интерфейс и привязать его по аналогии с IMapper в Startup. Здесь я этого не делаю, чтоб ещё больше не увеличивать ответ)
public class DiscountService : BaseService, IDiscountService
{
public DiscountService(IDbRepository dbRepository, IMapper mapper)
: base(dbRepository, mapper)
{
}
public async Task<Guid> Create(DiscountModel discount) =>
await base.Create<DiscountModel, DiscountEntity>(discount);
}
Интерфейс IEntity:
public interface IEntity
{
public Guid Id { get; set; }
public DateTime CreatedTime { get; set; }
public Guid WaiterCreatedId { get; set; }
public DateTime? UpdateTime { get; set; }
public Guid WaiterUpdatedId { get; set; }
public int Version { get; set; }
public bool IsDeleted { get; set; }
}
Обязательным условием является наличие конструктора у классов, которые пытаемся маппить:
DiscountModel:
public class DiscountModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal DiscountSum { get; set; }
public DateTime CreatedTime { get; set; } = DateTime.Now;
public bool IsDeleted { get; set; } = false;
public DiscountModel() { }
}
DiscountEntity:
public class DiscountEntity : BaseEntity
{
public string Name { get; set; }
public decimal DiscountSum { get; set; }
public DiscountEntity() { }
}
Надеюсь, помог кому-нибудь сэкономить полдня секса с гуглом в поисках ответа на вопрос и постоянной чисткой кэша нугета, т.к. в какой-то момент он перестаёт скачивать старые версии.