Circular reference causing stack overflow with Automapper - c#

I'm using Automapper to map my NHibernate proxy objects (DTO) to my CSLA business objects
I'm using Fluent NHibernate to create the mappings - this is working fine
The problem I have is that the Order has a collection of OrderLines and each of these has a reference to Order.
public class OrderMapping : ClassMap<OrderDTO>
{
public OrderMapping()
{
// Standard properties
Id(x => x.OrderId);
Map(x => x.OrderDate);
Map(x => x.Address);
HasMany<OrderLineDTO>(x => x.OrderLines).KeyColumn("OrderId").Inverse();
Table("`Order`");
}
}
public class OrderDTO
{
// Standard properties
public virtual int OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
public virtual string Address { get; set; }
// Child collection properties
public virtual IList<OrderLineDTO> OrderLines { get; set; } <-- this refs the lines
}
and:
public class OrderLineMapping : ClassMap<OrderLineDTO>
{
public OrderLineMapping()
{
// Standard properties
Id(x => x.OrderLineId);
References<OrderDTO>(x => x.Order).Column("OrderId");
Map(x => x.Description);
Map(x => x.Amount);
Table("`OrderLine`");
}
}
public class OrderLineDTO
{
// Standard properties
public virtual int OrderLineId { get; set; }
public virtual string Description { get; set; }
public virtual decimal Amount { get; set; }
public virtual OrderDTO Order { get; set; } // <-- this refs the order
}
These DTO objects map to Order and OrderLines CSLA objects respectively
When auto-mapping to OrderLines a list of OrderLinesDTO is mapped. Auto mapper is then mapping the "Order" property on of the lines, which maps back to Order which then circularly maps back to OrderLine, then to Order and so on
Does anyone know if Automapper can avoid this circular reference?

In your Automapper configuration:
Mapper.Map<OrderLine, OrderLineDTO>()
.ForMember(m => m.Order, opt => opt.Ignore());
Mapper.Map<Order, OrderDTO>()
.AfterMap((src, dest) => {
foreach(var i in dest.OrderLines)
i.Order = dest;
});

I was having the same issue using EF 6 and AutoMapper 6. Apparently what Kenny Lucero posted led me to the solution. Here's an extract from AM site:
// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();
Adding PreserveReferences() to both models made it work.

I was having the same issue and solved it by downgrading to version 4.2.1.
apparently the checks for circular references was expensive so they made it default to not check.
Migrating to AutoMapper 5 - Circular references
Supposedly these are supposed to be the settings methods for v 5+ but it didn't work for my data model because we opt'd for complex dto relationships instead of single use dtos for each action.
// Self-referential mapping
cfg.CreateMap<Category, CategoryDto>().MaxDepth(3);
// Circular references between users and groups
cfg.CreateMap<User, UserDto>().PreserveReferences();
http://docs.automapper.org/en/stable/5.0-Upgrade-Guide.html#circular-references
Automapper is supposed to be able to statically determine if the circular reference settings in v6.1+, So if it doesn't work for you automatically in version v6.1+ contact the automapper team.

Since this is the #1 google search result, I think there might be some people, like me, coming here who don't get a stackoverflow exception, but find trouble when sending the object (via ASP.NET) to the client, and thus it being JSON serialized.
So I had the same structure in place, Invoices has multiple InvoiceLines, when I load an Invoice and use the Linq-to-SQL .Include(x => x.InvoiceLines) I get errors when I try to load the object from the Api because each InvoiceLine contains the same Invoice again.
To solve this, do the following in ASP.NET Core Startup class:
services.AddMvc().AddJsonOptions(o =>
{
o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
o.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
// ^^ IMPORTANT PART ^^
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
So include o.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; in your JsonConfiguration when adding MVC to your application.
JSON.Net is taking the extra step to setup each reference with an additional meta-property called “$id”. When JSON.Net encounters the same instance in another place in the object graph, it simply drops a reference to the original instance, instead of duplicating the data, and thus not causing circular reference issues!
Source: https://johnnycode.com/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/
So now I don't have to further edit my AutoMapper configuration.

If anyone using Mapster (a mapping library for C# same as AutoMapper)
TypeAdapterConfig<TSource, TDestination>
.NewConfig()
.PreserveReference(true);
need to be used for preventing stack overflow error.

Not sure if I should post it here:
I had the same error after doing an automapper.map in a method.
The answer of CularBytes got me thinking that the issue was not automapper related but json related.
I did:
Return ok(_service.getDataById(id));
instead of
Return ok(await _service.getDataById(id));
(I forgot to await an asyc call... rookie mistake I know)

Related

Automapper Mapping of simple object to more complex nested type

I am in the process of a refactor and utilizing AutoMapper (6.2.1) to help us in formatting of API returns that conform to a specific contract. The DTO objects we are using internally are meant to simplify our understanding of the data before returning the data in the more complex type.
The Issue:
I have a DTO with a List<T> where I need one of the properties of <T> to be mapped to the collection in the more complex type. This is actually pretty straight forward but the problem is, what if the collection I am trying to map to in the more complex type is in fact inside another "higher" collection. Essentially I am in a little bit of a collection inside a collection problem.
Ex: DTO
public class ItemDTO
{
List<ItemDescriptionDTO> ItemDescriptions { get; set; }
}
public class ItemDescriptionDTO
{
public string Description { get; set; }
}
More Complex object I need to map to and do not have control over
public class ComplexThing // This is the object I need (It's ugly, I hate it too)
{
public ComplexItemDescriptions { get; set; }
}
public class ComplexItemDescriptions
{
public List<ComplexItemDescription> ComplexItemDescription { get; set; }
}
public class ComplexItemDescription
{
public UnparsedItemDescriptions UnparsedItemDescriptions { get; set; }
}
public class UnparsedItemDescriptions
{
public List<UnparsedItemDescription> UnparsedItemDescription { get; set; }
}
public class UnparsedItemDescription
{
public string UnparsedItemDescription { get; set; }
}
In essence I need to take the Description in my simple ItemDescriptionDTOand map that through this awful chain of nested objects to set the UnparsedItemDescription
I am able to properly map from UnparsedItemDescription all the way to the ComplexItemDescription but going higher than that to the Complex thing is giving me some trouble.
This is the mapping I have so far:
config.CreateMap<ItemDescriptionDTO, UnparsedItemDescription>()
.ForMember(dest => dest.UnparsedItemDescription, map => map.MapFrom(src => src.Description));
config.CreateMap<ItemDTO, UnparsedItemDescriptions>()
.ForMember(dest => dest.UnparsedItemDescription, map => map.MapFrom(src => src.ItemDescritpions));
config.CreateMap<ItemDTO, ComplexItemDescription>()
.ForPath(dest => dest.UnparsedItemDescriptions.UnparsedItemDescription, map => map.MapFrom(src => src.ItemDescriptions));
// This is where we start failing (I think I am just not understanding something fundamental to how Automapper does things or I am up against some silly edge case
config.CreateMap<ItemDTO, ComplexItemDescriptions>()
.ForMember(dest => dest.ComplexItemDescription, map => map.MapFrom(src => src.ItemDescriptions));
I need the ComplexThing because there are other properties in that class that are returned so I can't just get away with returning say a ComplexItemDescription
I would appreciate an assistance you could give. I admittedly have a base understanding on how Automapper works (which I am in the process of trying to get better at) but this is really throwing me at the moment.

AutoMapper - Use method to update IEnumerable property with no setter?

I'm having some trouble using AutoMapper to map from a data transfer object to a database entity model. The entity has a few properties that are custom array types, derived from IEnumerable. There's no setter for those properties, but there is a method called SetFromString() that is available. I can't seem to configure my map properly to use it though. Does AutoMapper support this kind of thing? If anyone could point me in the right direction, I'd appreciate it.
Below is a boiled-down version of the key classes I'm working with. (The mapping works just fine going from entity to DTO, but I need it to work in the reverse direction as well.)
// The database entity
public class ContactEntity
{
public CustomArray<String> CustomerNumbers { get; }
}
// The data transfer object
public class ContactDto
{
public List<String> CustomerNumbers { get; set; }
}
// CustomArray definition
public abstract class CustomArray<DataType> : IEnumerable<DataType>, IDisposable
{
protected CustomArray();
public abstract void SetFromString(string Value);
}
My mapping profile is still pretty vanilla since I couldn't wrap my head around the proper ForMember syntax.
public class ContactMappingProfile : Profile
{
public ContactMappingProfile()
{
// This map works fine
CreateMap<ContactEntity, ContactDto>();
// Map from DTO to Entity
CreateMap<ContactDto, ContactEntity>()
.ForMember(dest => dest.CustomerNumbers,
opt => opt.ResolveUsing(src => src.CustomerNumbers));
}
}
Thanks again for any help you can provide!
You could use either UseDestinationValue or Ignore for destination entity CustomerNumbers member and perform the actual mapping in AfterMap:
cfg.CreateMap<ContactDto, ContactEntity>()
.ForMember(dest => dest.CustomerNumbers, opt => opt.Ignore())
.AfterMap((src, dest) => dest.CustomerNumbers.SetFromString(string.Join(",", src.CustomerNumbers)));

NHibernate programmatically mapping

I want to create a custom mapping standard so I don't have to create the map.cs file for all new classes in the project.
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
}
Usually I'd have this:
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Table("Person");
Id(p => p.PersonID, map =>
{
map.Column("PersonID");
map.Generator(Generators.Identity);
});
Property(p => p.Name, map => map.Column("Name"));
}
}
I'd like to dynamically create these mappings based on some standards using reflection.
public class GenericDAL<T> where T : class, new()
{
public GenericDAL()
{
Configuration hConfig = new Configuration();
hConfig.DatabaseIntegration(c =>
{
c.ConnectionStringName = "myConnectionStringName";
c.Dialect<MsSql2012Dialect>();
});
ModelMapper mapper = new ModelMapper();
//Dynamic Mapping here
ISessionFactory _sessionFactory = hConfig.BuildSessionFactory();
}
}
I don't know how I can create a new ClassMapping from my T, how can I do this?
#SteveLillis already answered the question in the comments that there are already solutions for this.
Both MappingByCode in NHibernate (see below) and FluentNHibernate support automapping with conventions and overrides.
Links for Mapping By Code copied from original Answer which is not available anymore
First impressions
Naming convention resembling Fluent
Property
Component
ManyToOne
inheritance
dynamic component
Set and Bag
OneToMany and other collection-based relation types
concurrency
OneToOne
Join
Any
List, Array, IdBag
Map
Id, NaturalId
composite identifiers
entity-level mappings
the summary
I was looking for the same thing and I found a great documentation on NHibernate official website.
Here we have all the links to "fabiomaulo.blogspot" website, there you will find what are you looking for, WITHOUT FluentNHibernate.
Good Luck

Shouldn't my nhibernate mapping take care of this? Error: not-null property reference a null or transient value

I have a database with 3 tables:
Project
Sponsor
ProjectSponsor
In my domain objects, i have an object Project which has a child property called Sponsors which is a list of ProjectSponsor objects, like this:
IList<ProjectSponsor> Sponsors;
here is the Nhibernate Mapping code:
ProductMap:
HasMany(x => x.Sponsors).AsBag().Inverse().Cascade.AllDeleteOrphan();
ProductSponsorMap:
References(x => x.Project).Not.Nullable();
References(x => x.Sponsor).Not.Nullable();
here is my Domain Objects:
public class Project
{
public virtual IList<ProjectSponsor> Sponsors { get; set; }
}
public class ProjectSponsor
{
public virtual Project Project { get; set; }
public virtual Sponsor Sponsor { get; set; }
}
ok . . now to my issue, when i try to add new Projects (with their associated projectsponsor) into the database.
Here is my code:
foreach (Project project in newProjects)
{
Repository.Save(project);
foreach (ProjectSponsor projectSponsor in project.Sponsors)
{
Repository.Save(projectSponsor);
}
}
I am getting an exception on this line:
Repository.Save(projectSponsor);
saying that not-null property reference a null or transient value is trying to be saved:
projectSponsor.Project is the property that its complaining about. I would have thought that would be set by default based on the nhibernate mapping.
do i really need to have an explicit piece of code that have this code:
projectSponsor.Project = project
or is there anything wrong with my mapping files ?
If you haven't set the projectSponsor.Project property (and no code here says you have) then it will have the default value of null which will violate the mapping condition you've placed.
NHibernate is just going to reflect the state of the objects and the state of the objects is - for want of a better word - broken as is. You could remove the Project property on the ProjectSponsor map and all would be well but you're losing functionality to do so because obviously the directionality of relationships in OO is the reverse of RDBM directionality.
I would expect something like:
public class ProjectSponsor
{
// This is how you instantiate me!
public ProjectSponsor(Project project, Sponsor sponsor)
{
this.Project = project;
this.Sponsor = sponsor;
}
protected ProjectSponsor()
{
// I exist for NHibernates benefit only!
}
public virtual Project Project { get; set; }
public virtual Sponsor Sponsor { get; set; }
}
You should map this as a many-to-many relationship. To do this, delete ProjectSponsor from the model and map the collections using HasManyToMany. For the project map:
HasManyToMany(X => x.Sponsors).Table("ProjectSponsor")
.ParentKeyColumn("ProjectId").ChildKeyColumn("SponsorId")
.AsBag().LazyLoad();
and similarly for Sponsor:
HasManyToMany(X => x.Projects).Table("ProjectSponsor")
.ParentKeyColumn("SponsorId").ChildKeyColumn("ProjectId")
.Inverse().AsBag().LazyLoad();
Note that I marked Sponsor as the inverse side of the relationship but it could be Project. With these changes you just need to add a Sponsor to the Project's Sponsor collection for it to be persisted. I recommend exposing an AddSponsor method rather than accessing the collection directly.
However, I think you've got another problem. It appears to me that you're following the anti-pattern of having your repository classes control the ISession. One of (the many) reasons this is a bad pattern is that your saves appear to be executed immediately instead of in a transaction.

Entity Framework + AutoMapper ( Entity to DTO and DTO to Entity )

I've got some problems using EF with AutoMapper. =/
for example :
I've got 2 related entities ( Customers and Orders )
and they're DTO classes :
class CustomerDTO
{
public string CustomerID {get;set;}
public string CustomerName {get;set;}
public IList< OrderDTO > Orders {get;set;}
}
class OrderDTO
{
public string OrderID {get;set;}
public string OrderDetails {get;set;}
public CustomerDTO Customers {get;set;}
}
//when mapping Entity to DTO the code works
Customers cust = getCustomer(id);
Mapper.CreateMap< Customers, CustomerDTO >();
Mapper.CreateMap< Orders, OrderDTO >();
CustomerDTO custDTO = Mapper.Map(cust);
//but when i try to map back from DTO to Entity it fails with AutoMapperMappingException.
Mapper.Reset();
Mapper.CreateMap< CustomerDTO , Customers >();
Mapper.CreateMap< OrderDTO , Orders >();
Customers customerModel = Mapper.Map< CustomerDTO ,Customers >(custDTO); // exception is thrown here
Am I doing something wrong?
Thanks in Advance !
The problem I had was related to updates to EntityCollection references. AutoMapper creates a new instance of the relation when mapping from the DTO to the Entity, and that doesn't please the EF.
What solved my problem was configuring AutoMapper to use the destination value for my EntityCollection properties. In your case:
Mapper.CreateMap< CustomerDTO , Customers >().ForMember(c => c.Orders, o => o.UseDestinationValue());
That way AM will not create a new EntityCollection instance, and will use that wich came with the original Customer entity.
I'm still working for a way to automate this, but for now it solves my problem.
Try mapping to an existing object:
entity = Mapper.Map<MyDTO, NyEntity>(dto, entity);
And keep the Ignore()'s in place.
http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1
Your problem is because Automapper loses the EntityKey associated with the record. As the EntityFramework does not by default handle POCO's (Plain Old CLR Object)
Jay Zimmerman has a good example here of how to handle this from is. gd /4NIcj
Also from Jaroslaw Kowalski (part of the EF team I believe ) has this example for using POCO's within EF, which may translate well to use with Automapper (I've not yet had a chance to try it) : http://blogs.msdn.com/jkowalski/archive/2008/09/09/persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx
I'm not sure what your problem is, but - when i wanted to use LINQToEntities (switched to NHibernate),
i managed to use automapper with success.
Take a look at code:
public class SimpleMapper<TFrom, TTo>
{
public static TTo Map(TFrom fromModel)
{
Mapper.CreateMap<TFrom, TTo>();
return Mapper.Map<TFrom, TTo>(fromModel);
}
public static IList<TTo> MapList(IList<TFrom> fromModel)
{
Mapper.CreateMap<TFrom, TTo>();
return Mapper.Map<IList<TFrom>, IList<TTo>>(fromModel);
}
}
public class RepositoryBase<TModel, TLINQModel>
{
public IList<TModel> Map<TCustom>(IList<TCustom> model)
{
return SimpleMapper<TCustom, TModel>.MapList(model);
}
public TModel Map(TLINQModel model)
{
return SimpleMapper<TLINQModel, TModel>.Map(model);
}
public TLINQModel Map(TModel model)
{
return SimpleMapper<TModel, TLINQModel>.Map(model);
}
public IList<TModel> Map(IList<TLINQModel> model)
{
return SimpleMapper<TLINQModel, TModel>.MapList(model);
}
public IList<TLINQModel> Map(IList<TModel> model)
{
return SimpleMapper<TModel, TLINQModel>.MapList(model);
}
}
It's quite cryptic, always recreates mappings, but it worked. I hope it helps somehow. :)
Now, with new version of AutoMapper, the recommended way is using Queryable-Extensions:
When using an ORM such as NHibernate or Entity Framework with
AutoMapper's standard Mapper.Map functions, you may notice that the
ORM will query all the fields of all the objects within a graph when
AutoMapper is attempting to map the results to a destination type.
If your ORM exposes IQueryables, you can use AutoMapper's
QueryableExtensions helper methods to address this key pain.
The .ProjectTo() will tell AutoMapper's mapping engine
to emit a select clause to the IQueryable that will inform entity
framework that it only needs to query the Name column of the Item
table, same as if you manually projected your IQueryable to an
OrderLineDTO with a Select clause.
Create a mapping:
Mapper.CreateMap<Customer, CustomerDto>();
And project query to dto:
var customerDto =
session.Query<Customer>().Where(customer => customer.Id == id)
.Project().To<CustomerDto>()
.Single();
AutoMapper is very expressive when it comes to mapping error. read the exception message carefully.
another important thing is to remember to call Mapper.AssertConfigurationIsValid(); after creating the mappings. it gives an error if the mapping is wrong, thus preventing an exception later in the application runtime.
You should ignore mapping of some entity properties like so:
Mapper.CreateMap<CustomerDto, Customer>()
.ForMember(dest => dest.EntityKey, opt => opt.Ignore())
.ForMember(dest => dest.Licenses, opt => opt.Ignore())
.ForMember(dest => dest.AccessCodes, opt => opt.Ignore());
If you examine the message from the exception thrown by Automapper, you should see the entity properties that cannot be mapped and ignore them as above.
As you can read here you need to do the following
You can update entities with AutoMapper. Here's how: pass both the DTO and the entity object to AutoMapper's Map method. That's what this code does:
custExisting = Mapper.Map(Of CustomerDTO, Customer)(custDTO, custExisting)
Also beware of mapping issues like the one described here
These tips worked for me.

Categories

Resources