How can I avoid recursive Automapper calls with a self referencing object? - c#

I have a class Employee:
public partial class Employee
{
public Employee()
{
this.Employees1 = new HashSet<Employee>();
}
public int Id { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public Nullable<int> ReportsToId { get; set; }
public virtual ICollection<Employee> Employees1 { get; set; }
public virtual Employee ReportsTo { get; set; }
}
And a view model for this:
public class EmployeeEditModel : MappedViewModel<Employee>
{
public int Id { get; set; }
public string Surname { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
public int? ReportsTo { get; set; }
}
MappedViewModel<T> declares a method:
public class MappedViewModel<TEntity>: ViewModel
{
public virtual TEntity MapToEntity()
{
return (TEntity)Mapper.Map(this, GetType(), typeof(TEntity));
}
}
When I call MapToEntity in an action method, as such
I get an exception from AutoMapper with the message:
Missing type map configuration or unsupported mapping.
Mapping types: Int32 -> Employee System.Int32 -> Leave.Data.Employee
Destination path: Employee.ReportsTo.ReportsTo
Source value: 5
My mappings are defined as follows:
public static void RegisterMaps()
{
Mapper.CreateMap<Employee, EmployeeCreateModel>();
Mapper.CreateMap<EmployeeCreateModel, Employee>();
}
The double reference Employee.ReportsTo.ReportsTo screams out to me that this exception is somehow caused by some sort of cyclical mapping quirk, but I what can I do about this? At very least I would like omit ReportsTo from the mapping and simply do this manually after calling MapToEntity. How can I even to this, and rather, what should I do for this pattern of AutoMapper problem?

You need to do more than simple Mapper.CreateMap as it won't magically figured out how to convert between EmployeeViewModel.ReportsTo (int?) and Employee.ReportsTo (Employee).
Either create a mapping between int and Employee or use the ForMember method to tell AutoMapper how to convert the ReportsTo property.

Related

AutoMapper postfix not recognized

I'm using AutoMapper v9, and wish to have a postfix on my DTO property names of Dto.
In my profile I add:
RecognizeDestinationPostfixes("Dto");
I validate my mapping with config.AssertConfigurationIsValid(); and immediately it throws with
Unmapped properties:
AccountConnectionIdDto
CreatedDateDto
ModifiedDateDto
IdentityIdDto
AccountIdDto
This is what my DTO looks like:
public class AccountConnectionDto
{
public int AccountConnectionIdDto { get; set; }
public DateTime CreatedDateDto { get; set; }
public DateTime ModifiedDateDto { get; set; }
public Guid IdentityIdDto { get; set; }
public int AccountIdDto { get; set; }
public AccountDto AccountDto { get; set; }
}
Removing the Dto from my model names, and it all works. I feel like the postifx isn't being recognized or utilized at all, am I doing it the correct way?

EF Core Many To Many. Error returning entities

I'm implementing an WebAPI using dotnet core (2) and EF Core. The application uses UnitOfWork/Repository-pattern. I've come to a point where I need to implement a "Many-To-Many"-relation but I'm having some trouble. This is what I've got sofar:
Entities:
public class Team : DataEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TeamId { get; set; }
public int OrganizationID { get; set; }
public string Name { get; set; }
public string URL { get; set; }
public virtual ICollection<Season> Seasons { get; set; }
public ICollection<TeamMember> TeamMember { get; set; }
}
public class Member : DataEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MemberId { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobilePhone { get; set; }
public string Email { get; set; }
public int RelatedTo { get; set; }
public int TeamRole { get; set; }
public ICollection<TeamMember> TeamMember { get; set; }
}
public class TeamMember
{
public int TeamId { get; set; }
public Team Team { get; set; }
public int MemberId { get; set; }
public Member Member { get; set; }
}
In my DbContext-class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TeamMember>()
.HasKey(t => new { t.TeamId, t.MemberId });
modelBuilder.Entity<TeamMember>()
.HasOne(tm => tm.Team)
.WithMany(t => t.TeamMember)
.HasForeignKey(tm => tm.TeamId);
modelBuilder.Entity<TeamMember>()
.HasOne(tm => tm.Member)
.WithMany(t => t.TeamMember)
.HasForeignKey(tm => tm.MemberId);
}
On my repository I have en extension for IncludeAll:
public static IQueryable<Team> IncludeAll(this IGenericRepository<Team> repository)
{
return repository
.AsQueryable()
.Include(s => s.Seasons)
.Include(tm => tm.TeamMember);
}
Everything builds as expected but when trying to invoke the controller-action that's fetching a Team (which I expect to include all members) or a Member (which I expect to include all the members teams - code not included above). SwaggerUI returns: TypeError: Failed to fetch. If I try to invoke the controller-action directly in chrome (http://localhost/api/Team/1) i get an incomplete result, but never the less...a result :) :
{
"value":
{
"teamId":1,
"organizationID":1,
"name":"Team1",
"url":"http://www.team1.com",
"seasons":[
{
"id":1,
"teamID":1,
"name":"PK4",
"description":null,
"startDate":"2017-09-01T00:00:00",
"endDate":"2018-12-31T00:00:00",
"created":"2017-12-01T00:00:00",
"updated":"2017-12-27T00:00:00",
"createdBy":"magnus",
"updatedBy":"magnus"
}],
"teamMember":[
{
"teamId":1
Am I missing something obvious?
Might be related to some circular reference issue?!
your teamMember property has an Team property which again has a teamMember properties and so on... When trying to serialize, a never ending loop would be created.
Maybe you can set the teamMember.Team property to null. I think you would also have to set the teamMember.Member.teamMember property to null.
You have a reference loop in your data structure (team -> team member -> team), and the JSON encoder can't deal with this.
I can think of two ways to solve this:
Configure the JSON encoder to ignore reference loops
In Startup.cs, add this:
services
.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
From the documentation:
ReferenceLoopHandling.Ignore: Json.NET will ignore objects in reference loops and not serialize them. The first time an object is encountered it will be serialized as usual but if the object is encountered as a child object of itself the serializer will skip serializing it.
Add the [JsonIgnore] attribute to the property you want to ignore
For example:
public class TeamMember
{
public int TeamId { get; set; }
[JsonIgnore]
public Team Team { get; set; }
}

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?

Unable to cast location to ILocation

I am getting a rather weird casting error. Here is the code:
public class OrganizationLocation : IOrganizationLocation
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public interface IOrganizationLocation
{
string Name { get; }
}
public class Organization : IOrganization
{
public Guid Id { get; set; }
public string Alias { get; set; }
public virtual ICollection<OrganizationLocation> Location { get; set; }
public ICollection<IOrganizationLocation> Locations
{
get
{
return (ICollection<IOrganizationLocation>) Location;
}
}
}
public interface IOrganization
{
string Alias { get; }
ICollection<IOrganizationLocation> Locations { get; }
}
now when I try to run this via a service (backend data layer is EF6), the "Location" variable has all the values, however, the "Locations" variable fails to cast. If I try to do a safe cast, it comes back as null every time.
I'm not understanding why would the cast fail? It has same fields, both are ICollection Type, so why do I get a HashSet?
The reason why I am doing it this way is because for EF6 framework to be able to treat this class as a table it needs to have a concrete type and a public get/set. However, I do not wish to expose that, so I use interface instead and then inject the class when the interface is called. This way I only expose get method and on top of it I only expose interface layers.
Unable to cast object of type 'System.Collections.Generic.HashSet`1[Namespace.OrganizationLocation]' to type 'System.Collections.Generic.ICollection`1[Namespace2.IOrganizationLocation]'.
I was able to resolve this by changing the code to the following:
public class OrganizationLocation : IOrganizationLocation
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public interface IOrganizationLocation
{
string Name { get; }
}
public class Organization : IOrganization
{
public Guid Id { get; set; }
public string Alias { get; set; }
public virtual ICollection<OrganizationLocation> Location { get; set; }
public IEnumerable<IOrganizationLocation> Locations => Location
}
public interface IOrganization
{
string Alias { get; }
IEnumerable<IOrganizationLocation> Locations { get; }
}

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