Automapper forsourcemember ignore not working - c#

I am pulling from an entity object and trying to map to a model. Here is my class information:
// entity class was auto-generated
**public partial class CategoryType
{
public CategoryType()
{
this.OutgoingCategories = new HashSet<OutgoingCategory>();
}
public int CategoryTypeID { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public virtual ICollection<OutgoingCategory> OutgoingCategories { get; set; }
}**
public class CategoryTypeModel
{
public int CategoryTypeID { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
}
My mapper looks like this:
Mapper.CreateMap<CategoryType, CategoryTypeModel>().ForSourceMember(a => a.OutgoingCategories, o => o.Ignore());
When I go to get the mapped list:
Mapper.Map <List<CategoryType>, List<CategoryTypeModel>>(dbo.CategoryTypes.ToList());
I get the error:
Missing type map configuration or unsupported mapping.
I'm new to using Automapper, so I'm not sure what I'm doing wrong. Any help would be great. Thanks.

Well, I said I was new to Automapper. So.... I guess it would help to call this in my Global.asax:
AutoMapperConfig.Configure();
That solved the problem.

Related

Automapper "ExplicitExpansion" not working when a constructor in parent DTO is declared

I am using the latest version of Automapper (12.0).
When applying a constructor on a parent DTO, "ExplicitExpansion" does not work.
Here My Model and DTOs:
class Maestro
{
public virtual Guid Id { get; set; }
public virtual string Nombre { get; set; }
public virtual IList<Alumno> Alumnos { get; set; }
}
class Alumno
{
public virtual Guid Id { get; set; }
public virtual string Nombre { get; set; }
}
class MaestroDto
{
public MaestroDto(System.Guid id, string nombre, List<AlumnoDto> alumnos)
{
this.Id = id;
this.Nombre = nombre;
this.Alumnos = alumnos;
}
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.Required()]
public System.Guid Id { get; set; }
[System.ComponentModel.DataAnnotations.Required()]
public string Nombre { get; set; }
public List<AlumnoDto> Alumnos { get; set; }
}
class AlumnoDto
{
public virtual Guid Id { get; set; }
public virtual string Nombre { get; set; }
}
Please note that MaestroDto has a constructor.
This is my mapping configuration:
var config = new MapperConfiguration(c =>
{
c.CreateMap<Maestro, MaestroDto>().ForMember(c => c.Alumnos, opt => opt.ExplicitExpansion());
c.CreateMap<Alumno, AlumnoDto>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
When making projections with Automapper in this way:
List<MaestroDto> resultMaestro = mapper.ProjectTo<MaestroDto>(maestros.AsQueryable()).ToList();
"Alumnos" is always loaded even when I have not specifically said I want it to be.
This is due to the constructor that has "MaestroDto", is this expected? I would expect that even having the constructor the property would be ignored if I wish, by not adding it in the query with the lambda expression.
Here you can find the Gist.
Thanks in advance.
Disabling mapping constructors solves the issue.
Adding cfg.DisableConstructorMapping() at the mapping initialization makes it work.

Automapper returning "default" instance of mapped entity

I'm trying to get automapper working for .NET core, and I seem to be missing something important.
I have my automapper set up as follows:
In Startup.cs in my ConfigureServices method, I have the following line:
services.AddAutoMapper(typeof(Startup), typeof(ApplicationDbContext));
I also have a class called MappingProfile which resembles this:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<CfGroup, GroupModel>()
.IgnoreAllUnmapped();
CreateMap<GroupModel, CfGroup>()
.IgnoreAllUnmapped();
...
}
}
The IgnoreAllUnmapped was suggested by another SO question, and it looks like this:
public static IMappingExpression<TSource, TDest> IgnoreAllUnmapped<TSource, TDest>(
this IMappingExpression<TSource, TDest> expression)
{
expression.ForAllMembers(opt => opt.Ignore());
return expression;
}
My CfGroup looks like this:
[Table("Groups")]
public class CfGroup
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(100)]
[Required]
public string Name { get; set; }
[DataType(DataType.ImageUrl)]
[StringLength(250)]
public string ImageUrl { get; set; }
[Required]
public int FounderId { get; set; }
[ForeignKey("FounderId")]
public virtual CfUser Founder { get; set; }
[Required]
public bool IsActive { get; set; }
[Required]
public DateTime CreatedTimestampUtc { get; set; }
[StringLength(3000)]
public string About { get; set; }
public virtual ISet<UserGroupMember> Members { get; set; }
public virtual ISet<UserGroupManager> Managers { get; set; }
}
... and my GroupModel looks like this:
public class GroupModel
{
public int Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string About { get; set; }
}
When calling my web api endpoint, I get a collection of CfGroup entities from the database just fine. I then run a command like so:
var dtos = entities.Select(_mapper.Map<GroupModel>);
...but my dtos are "empty"... IOW, the instance is created, but Id value is set to 0 and all the string values are set to null. That is definitely not what is in the entities variable.
I have also tried to explicitly convert it like so:
var dtos = entities.Select(x => _mapper.Map<GroupModel>(x));
and, postulating that it's an IEnumerable issue, tried making a List out of it:
var dtos = entities.Select(x => _mapper.Map<GroupModel>(x)).ToList();
...but no matter what, the DTO's remain instantiated, but with all properties set to their default values.
What am I doing wrong?
I suspect it may be because you are mapping inside your linq expression. try something like this:
var dtos = _mapper.Map<IEnumerable<GroupModel>>(entities.ToList());
So it turns out that the IgnoreAllUnmapped was not the correct way to go; in fact, the mapping seemed to work fine without it. I took out the extra "exception handling" code and it worked as I wanted it to. I was overthinking it.

Converting infinitely nested objects in .NET Core

EDIT: I originally worded this question very poorly, stating the problem was with JSON serialization. The problem actually happens when I'm converting from my base classes to my returned models using my custom mappings. I apologize for the confusion. :(
I'm using .NET Core 1.1.0, EF Core 1.1.0. I'm querying an interest and want to get its category from my DB. EF is querying the DB properly, no problems there. The issue is that the returned category has a collection with one interest, which has one parent category, which has a collection with one interest, etc. When I attempt to convert this from the base class to my return model, I'm getting a stack overflow because it's attempting to convert the infinite loop of objects. The only way I can get around this is to set that collection to null before I serialize the category.
Interest/category is an example, but this is happening with ALL of the entities I query. Some of them get very messy with the loops to set the relevant properties to null, such as posts/comments.
What is the best way to address this? Right now I'm using custom mappings that I wrote to convert between base classes and the returned models, but I'm open to using any other tools that may be helpful. (I know my custom mappings are the reason for the stack overflow, but surely there must be a more graceful way of handling this than setting everything to null before projecting from base class to model.)
Classes:
public class InterestCategory
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Interest> Interests { get; set; }
}
public class Interest
{
public long Id { get; set; }
public string Name { get; set; }
public long InterestCategoryId { get; set; }
public InterestCategory InterestCategory { get; set; }
}
Models:
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public long? InterestCategoryId { get; set; }
}
Mapping functions:
public static InterestCategoryModel ToModel(this InterestCategory category)
{
var m = new InterestCategoryModel
{
Name = category.Name,
Description = category.Description
};
if (category.Interests != null)
m.Interests = category.Interests.Select(i => i.ToModel()).ToList();
return m;
}
public static InterestModel ToModel(this Interest interest)
{
var m = new InterestModel
{
Name = interest.Name,
Description = interest.Description
};
if (interest.InterestCategory != null)
m.InterestCategory = interest.InterestCategory.ToModel();
return m;
}
This is returned by the query. (Sorry, needed to censor some things.)
This is not .NET Core related! JSON.NET is doing the serialization.
To disable it globally, just add this during configuration in Startup
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}));
edit:
Is it an option to remove the circular references form the model and have 2 distinct pair of models, depending on whether you want to show categories or interests?
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Note that each of the models has a nested class for it's child objects, but they have their back references removed, so there would be no infinite reference during deserialization?

Map string to a property in collection

I am trying to solve seemingly trivial task. How to map string to a property of a collection? Bellow I show my current situation. I am trying to map the DeliveryType.UrlTemplate property of DeliveryNoteTableDto class to each PackageDto.UrlTemplate property. I am using AutoMapper. Currently I am getting error:
Custom configuration for members is only supported for top-level individual members on a type.
when performing the mapping. Any help?
classes
public class DeliveryNoteTableDto
{
public List<PackageDto> Packages { get; set; } = new List<PackageDto>();
public DeliveryTypeDto DeliveryType { get; set; }
}
public class PackageDto
{
public virtual string DeliveryKey { get; set; }
public virtual string UrlTemplate { get; set; }
}
public class DeliveryTypeDto
{
public virtual string Transporter { get; set; }
public virtual string UrlTemplate { get; set; }
}
mapping
map.ForMember(x => x.Packages.Select(p => p.UrlTemplate), opt => opt.MapFrom(x => x.DeliveryType.UrlTemplate));

how to map from a domain model to a view model from MVC controller with AutoMapper

I am trying to figure out how AutoMapper works in creating a map from the domain model to the view model with a complex collection.
Within my domain model (Search.Domain) ,
I have the following:
namespace Search.Domain.Model
{
public class Result
{
public int SearchTime { get; set; }
public List<ResultDetails> Context { get; set; }
}
public class ResultDetails
{
public string Entity { get; set; }
public string Jurisdiction { get; set; }
public DateTime DateReported { get; set; }
public string Description { get; set; }
public DateTime DateEntered { get; set; }
public string AssociatedLink { get; set; }
public int Relevance { get; set; }
}
}
with the MVC project (Search.WebUI) I have the following:
namespace Search.WebUI.Models
{
public class ResultViewModel
{
public int SearchTime { get; set; }
public List<ResultDetails> Context { get; set; }
}
public class ResultDetails
{
public string Entity { get; set; }
public string Jurisdiction { get; set; }
public DateTime DateReported { get; set; }
public string Description { get; set; }
public DateTime DateEntered { get; set; }
public string AssociatedLink { get; set; }
public int Relevance { get; set; }
}
}
Within the controller (HomeController.cs)
namespace Search.WebUI.Controllers
{
public class HomeController : Controller
{
private ISearchResultManager sr = new ResultManager();
public ActionResult Index()
{
ResultViewModel searchresults;
var results = sr.GetSearchResults(5);
Mapper.CreateMap<Search.Domain.Model.Result, ResultViewModel>();
searchresults = Mapper.Map<Search.Domain.Model.Result, ResultViewModel>(results);
return View("Home", searchresults);
}
}
}
The error message that is being generated when run is:
Missing type map configuration or unsupported mapping.
Mapping types:
ResultDetails -> ResultDetails
Search.Domain.Model.ResultDetails -> Search.WebUI.Models.ResultDetails
Destination path:
ResultViewModel.Context.Context.Context0[0]
Source value:
Search.Domain.Model.ResultDetails
In looking at this it appears that the nested List<ResultDetails> is causing an issue but I don't know what I am supposed to do to handle this type of mapping.
Is it correct to go all the way back into the domain for reference to the type? This seems as if I am pulling the domain into the UI which I would not want to do?
Is there another option for mapping domain models to view models in the UI? Basically I was hoping to have a view model within the UI that I could extend beyond the domain model for UI purposes and not put a reference to the domain model.
I am new to autoMapper so this entire thing may be wrong? I would appreciate any suggestions or guidance.
AutoMapper doesn't look at all potential child mapping when mapping a containing class. You need to explicitly add a mapping for the ResultDetails as well:
Mapper.CreateMap<Search.Domain.Model.Result.ResultDetails,
Search.WebUI.Models.ResultDetails>();

Categories