Reputation: 38094
I've already seen this question, this and this.
And I've got this exception when I move from Automapper 3.3.1.0
to Automapper 6.0.1.0
. This code throws no exception in Automapper 3.3.1.0
.
I have 2 class :
Class 1 : (Domain)
public class Movie:IEntityBase
{
public Movie()
{
Stocks = new List<Stock>();
}
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public int GenreId { get; set; }
public virtual Genre Genre { get; set; }
public string Director { get; set; }
public string Writer { get; set; }
public string Producer { get; set; }
public DateTime ReleaseDate { get; set; }
public byte Rating { get; set; }
public string TrailerURI { get; set; }
public virtual ICollection<Stock> Stocks { get; set; }
}
Class 2 :
public class MovieViewModel : IValidatableObject
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public string Genre { get; set; }
public int GenreId { get; set; }
public string Director { get; set; }
public string Writer { get; set; }
public string Producer { get; set; }
public DateTime ReleaseDate { get; set; }
public byte Rating { get; set; }
public string TrailerURI { get; set; }
public bool IsAvailable { get; set; }
public int NumberOfStocks { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var validator = new MovieViewModelValidator();
var result = validator.Validate(this);
return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
}
}
I've tried to map in constructor of a class cause Automapper 6.0.1.0
does not have method Configure()
to override:
public class DomainToViewModelMappingProfile : Profile
{
public override string ProfileName
{
get { return "DomainToViewModelMappings"; }
}
public DomainToViewModelMappingProfile()
{
Mapper.Initialize(cfg => {
cfg.CreateMap<Genre, GenreViewModel>()
.ForMember(vm => vm.NumberOfMovies, map => map.MapFrom(g => g.Movies.Count()));
cfg.CreateMap<Movie, MovieViewModel>()
.ForMember(vm => vm.Genre, map => map.MapFrom(m => m.Genre.Name))
.ForMember(vm => vm.GenreId, map => map.MapFrom(m => m.Genre.ID))
.ForMember(vm => vm.IsAvailable, map => map.MapFrom(m => m.Stocks.Any(s => s.IsAvailable)))
.ForMember(vm => vm.NumberOfStocks, map => map.MapFrom(m => m.Stocks.Count))
.ForMember(vm => vm.Image, map => map.MapFrom(m => string.IsNullOrEmpty(m.Image) == true ? "unknown.jpg" : m.Image));
});
Mapper.AssertConfigurationIsValid();
}
}
And I've got the following exception:
Missing type map configuration or unsupported mapping.
Mapping types: Movie -> MovieViewModel HomeCinema.Entities.Movie -> HomeCinema.Web.Models.MovieViewModel
I've tried to figure out what property is a reason of this exception through Ignore()
method, however, I still get the above exception.
cfg.CreateMap<Genre, GenreViewModel>()
.ForMember(vm => vm.NumberOfMovies, map => map.MapFrom(g => g.Movies.Count()));
cfg.CreateMap<Movie, MovieViewModel>()
.ForMember(vm => vm.Genre, map => map.Ignore())
.ForMember(vm => vm.GenreId, map => map.Ignore())
.ForMember(vm => vm.IsAvailable, map => map.Ignore())
.ForMember(vm => vm.NumberOfStocks, map => map.Ignore())
.ForMember(vm => vm.Image, map => map.Ignore());
Forgot to say that I am calling the following method in Global.asax.cs
:
AutoMapperConfiguration.Configure();
Could you tell me what I am doing wrong? Any help would be greatly appreciated.
The most interesting point that this code is valid in AutoMapper 3.3.1.0
.
Update:
It works!:) Thanks to all you, guys, and special thanks to @AndriiLitvinov. The work code:
public class DomainToViewModelMappingProfile : Profile
{
public override string ProfileName
{
get { return "DomainToViewModelMappings"; }
}
public DomainToViewModelMappingProfile()
{
CreateMap<Genre, GenreViewModel>()
.ForMember(vm => vm.NumberOfMovies, map => map.MapFrom(g => g.Movies.Count()));
CreateMap<Movie, MovieViewModel>()
.ForMember(vm => vm.Genre, map => map.MapFrom(m => m.Genre.Name))
.ForMember(vm => vm.GenreId, map => map.MapFrom(m => m.Genre.ID))
.ForMember(vm => vm.IsAvailable, map => map.MapFrom(m => m.Stocks.Any(s => s.IsAvailable)))
.ForMember(vm => vm.NumberOfStocks, map => map.MapFrom(m => m.Stocks.Count))
.ForMember(vm => vm.Image, map => map.MapFrom(m => string.IsNullOrEmpty(m.Image) == true ? "unknown.jpg" : m.Image));
}
}
and call Initialize()
just one time:
//Global.asax.cs
protected void Application_Start()
{
var config = GlobalConfiguration.Configuration;
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(config);
Bootstrapper.Run();
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.EnsureInitialized();
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<DomainToViewModelMappingProfile>();
});
}
}
public class Bootstrapper
{
public static void `Run()
{
//Configure Autofac
AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
//Configure Automapper
AutoMapperConfiguration.Configure();
}
}
Upvotes: 3
Views: 5224
Reputation: 13182
I have tried to implement such mapping locally and it all worked. I assume that you have many profiles that all call Mapper.Initialize
as far as I remember it is meant to be called only once at application startup and all the profiles should call CreateMap method:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Foo, Bar>();
}
}
Upvotes: 6
Reputation: 3693
When you call Mapper.AssertConfigurationIsValid();
method you must make sure that each and every property has a valid source and target to map. If you do not have valid source and target for each property you get an error. If you look at the error message in fact it will tell you in detail. I just skimmed through your code but for example public virtual ICollection<Stock> Stocks { get; set; }
does not have a mapping.
Upvotes: 1