Stackoverflow Exception on AutoMapMembers
I didn't actually hit a stackoverflow exception after running this code for like an hour. I assume eventually it will occur. Something is causing a circular dependency. I spent some trying to debug with no luck. If you provide me some guidance, I can create a full repro. In the meantime, here is just a snapshot of the stacktrace while it just keeps growing.
at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMapMembers(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMapConstructorParameters(ClassMap map, Configuration configuration, LinkedList`1 mapParents, Int32 indexStart)
at CsvHelper.Configuration.ClassMap.AutoMap(Configuration configuration)
at CsvHelper.Configuration.Configuration.AutoMap(Type type)
at CsvHelper.CsvWriter.WriteHeader(Type type)
at CsvHelper.CsvWriter.WriteRecords[T](IEnumerable`1 records)
Can you provide the class structure that it's trying to map?
@JoshClose I'm having a really difficult time creating a repro... this is a true heisenbug!
The model that I'm trying to write has a single ctor that accepts a single object.
This "object" has property that links to another object....which links to another object, etc. Just your standard entity framework models, nothing really special, no crazy reflection etc.
Every time I attempt to narrow down the chain, and eliminate every property that is causing the infinite loop, I just inevitably end up at the start of the chain again. I realize this is probably an awful explanation, and I'm sorry for that.
Anyway, if you have any suggestions for how to narrow this down or if I should attempt to log certain lines in csvhelper, let me know.
Do you want to email me an example so it isn't public?
Meaning, the full class structure that causes the failure.
@JoshClose have you found anything on this issue yet?
We've recently begun hitting SIGSEGV faults that appear to be related to the inclusion of a ClassMap when deserializing. When we comment out the registration of the ClassMap, we don't get the issue, but when we include it, we typically do.
It appears to be something that's timing related, as there isn't a solid repro case, and the frequency of hitting it increases as we spin up more threads doing simultaneous parsing. For example, running 12 threads right now repeatedly, parsing the same files over and over, I'll hit the SIGSEGV approximately 75% of the runs.
I'm trying to better info, but unfortunately .NET Core doesn't throw a StackOverflowException - just hands back the dump, which has precious little info!
Can you reproduce it while debugging? If so, the PDB files are on NuGet. You can set your symbols server and debug into the source code.
https://docs.microsoft.com/en-us/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger?view=vs-2017
@JoshClose yes, I just pulled in the entire repo and dumped a ton of logs in there. I'm not knowledgeable enough about the code to know what I should be looking for though. All I know is that method kept cycling in a loop, albeit over a sequence of different reflected types.
Do you have some code you could zip up and email me so I can reproduce it?
For my part, I have not yet been able to reproduce it consistently. I don't believe there are any shared objects that I'm using across the threads, yet the number of threads running at once (each operating on separate CSV's) seems to have a significant impact on the likelihood of hitting the SIGSEGV.
I'm feeling like the issue I'm chasing here isn't related to the one originally reported by @bradmarder. His is happening on writes, mine on reads. It also doesn't seem related to using a ClassMap any longer - I've been able to hit it without that.
@JoshClose just for my sanity - is the library thread-safe at a module level? Meaning, no shared state across instances (statics/globals), etc.?
@philipflesher I would have to go through and do an audit. There should be locks in places where there are statics, but it's possible I've added something at some point and forgot it.
Could you point me to any specific places in code where you might suspect a recursive dive? Or maybe some of the static/shared variables I could put specific watches on?
This is a place when auto mapping that it could happen. https://github.com/JoshClose/CsvHelper/blob/6b339704fe22eb8c90b2d3da6e8470561d7c708a/src/CsvHelper/Configuration/ClassMap.cs#L145
There is a CheckForCircularReference method that is called to ensure that doesn't happen. There could be a bug there though.
The ObjectResolver has a static. https://github.com/JoshClose/CsvHelper/blob/6b339704fe22eb8c90b2d3da6e8470561d7c708a/src/CsvHelper/ObjectResolver.cs#L20
ReflectionHelper https://github.com/JoshClose/CsvHelper/blob/6b339704fe22eb8c90b2d3da6e8470561d7c708a/src/CsvHelper/ReflectionHelper.cs#L51
We've just come across this same one whilst writing, and we've resolved it by adding in a parameterless public constructor to our class for the fields and it solved the issue.
If you remove the Public Sub New() then you it still fail with the exception
We ran it through with the CsvHelpersource code and it's in the automap constructor that then gets caught in a loop doing the AutoMapMembers
Private Class ExportItem
Public Property StartDate As String
Public Property Duration As Double
Public Property MediaId As Integer
Public Property Title As String
Public Property ArtistName As String
Public Property StationId As Integer
Public Property StationName As String
Public Property Location As String
Public Property LocationNetworkName As String
Public Property Login As String
Public Property OnAir As String
Public Property PlayMode As String
Public Property Preview As Boolean
Public Property Reference As String
Public Property Player As Integer
Public Property Hardware As String
Public Sub New()
End Sub
Public Sub New(playlogVm As PlayLogItemViewModel)
With playlogVm
Me.StartDate = .PlayLog.StartDateTimeUtc.ToString("o")
Me.Duration = .PlayLog.PlayedDuration.TotalSeconds
Me.MediaId = .PlayLog.MediaId
Me.Title = .ItemTitle
Me.ArtistName = .FirstArtistName
Me.StationId = .PlayLog.StationId
Me.StationName = .StationName
Me.Location = .LocationName
Me.LocationNetworkName = .LocationNetworkName
Me.Login = .PlayLog.MyriadLoginName
Me.OnAir = .OnAirCaption
Me.PlayMode = .PlayLog.PlayMode.ToString()
Me.Preview = .PlayLog.PreviewMode
Me.Reference = .PlayLog.ExtSchedulerReference
Me.Player = .PlayLog.PlayerNumber
End With
End Sub
End Class
Public Sub ExportToCSV
Using writer As TextWriter = File.CreateText(filename)
Using csvWriter As New CsvHelper.CsvWriter(writer)
With csvWriter.Configuration
.HasHeaderRecord = True
End With
csvWriter.WriteHeader(GetType(ExportItem))
Await csvWriter.NextRecordAsync()
For Each item In .Results
Try
csvWriter.WriteRecord(New ExportItem(item))
Await csvWriter.NextRecordAsync()
Catch ex As Exception
End Try
Next
End Using
writer.Close()
End Using
End Sub
What is the definition for PlayLogItemViewModel?
Are you able to create a unit test that fails?
Hello
Same issue: I have a class with 20 properties (only int, string and DateTime) A csv.WriteRecords(..) call and it crashes with stackoverflow.
I tried a mapper class (and registered it in the configuration) with only having a AutoMap() in the constructor: => it crashes with stackoverflow during the AutoMap
Then I mapped every single property ("Map(m => m.Name")) in the mapper class instead of the AutoMap. => it works
Next try (like Mostlypyjamas said): Instead of the mapper class, I only added a parameterless constructor to the data class => it works
Greetings Robert
I'm running into this with this class: https://gist.github.com/eltiare/54b0bf78ec16adb9027c7f85a1ae1368
Everything works fine until I go to write an instance of OrderExport.
Found a way of creating a stack overflow with the following code (and using AutoMap):
public class BiOperation
{
private readonly Operation _operation;
public BiOperation(Operation operation)
{
_operation = operation;
}
public string Resource => _operation.Resource;
}
Stackoverflow exception on self referencing class with property of other class:
private class C
{
public string Id { get; set; }
}
private class SelfCircularZ
{
public C Other { get; set; }
public SelfCircularZ Circular { get; set; }
}
[TestMethod]
public void SelfCircularZ()
{
var config = new CsvHelper.Configuration.Configuration();
config.AutoMap<SelfCircularZ>();
}
Field Other of custom class C causes stackoverflow, if property is simple type (like string) - no problems.
This line has 0 mapParents and CheckForCircularReference is not skipping processing:
https://github.com/JoshClose/CsvHelper/blob/2914f6856febbf7488c53ed65948a00a39f95a22/src/CsvHelper/Configuration/ClassMap.cs#L297
I've also run into this problem recently. When trying to AutoMap() a class with complex dependencies (ex: Entity Framework model class), it would go deep down until StackOverflowException is thrown at:
CsvHelper/src/CsvHelper/Configuration/ClassMap.cs
with indexStart=1786260400
My test code is something like this:
public class MyCsvMap : ClassMap<MyViewModel>
{
public MyCsvMap()
{
AutoMap();
Map(e => e.object1).Ignore();
// Ignore other fields ...
}
}
//Test block
var csvConfig = new CsvHelper.Configuration.Configuration();
csvConfig.RegisterClassMap<MyCsvMap>();
Also having this issue. Whilst tracing it, I found a typo (paramter)...
if (CheckForCircularReference(parameter.ParameterType, mapParents))
{
throw new InvalidOperationException($"A circular reference was detected in constructor paramter '{parameter.Name}'." +
"Since all parameters must be supplied for a constructor, this parameter can't be skipped.");
}
I think the issue is simple to reproduce by having a property that is typed as a Uri. I've worked around it by temporarily using a string instead.
I'm getting this on AutoMap() and I'm thinking due to the comments above that it is due to the presence of an Exception member, albeit with an [Ignore] attribute, on the class in question. There could potentially be infinite recursion on the InnerException field, but I'm not sure why it ignores the [Ignore].
I'm getting this StackOverflow on WriteRecords as well.
Simplest class:
public class ReconcileRow
{
public int RegistrationId => Registration.Id;
[Ignore]
public Registration Registration { get; set; }
}
The Registration object has properties that have references to the Registration. Regardless of whether I use the [Ignore] attribute, or a ClassMap with Map(x => x.Registration).Ignore(), it causes a StackOverflow.
The confusing part is this version of the class ALSO causes a StackOverflow:
public class ReconcileRow
{
public ReconcileRow(Registration reg)
{
RegistrationId = reg.Id;
}
public int RegistrationId { get; set; }
}
I don't see why it would need to reflect into the Registration class in order to write these records! Very bizarre to me..
Changing the writing to be like this:
var csv = new CsvWriter(writer);
csv.Configuration.IgnoreReferences = true;
csv.WriteRecords(result);
Does work - but of course with this I can't use any reference types.
Parameterless constructor seemed to resolve this issue for me.
Auto mapping without reference
var csv = new CsvWriter(writer);
csv.Configuration.IgnoreReferences = true;
csv.WriteRecords(result);
Plus Parameterless constructor helped me solve the issue.
Thanks to e4stwood and DanielStout5.
Similar to @zii-dmg the following xUnit test gives a stack overflow when it's trying to auto map properties.
Parameterless constructor doesn't help.
If you comment out one of the reference properties in Person the auto mapping will work.
The only workaround is to setup a ClassMap and manually map each property - ie don't call AutoMap.
using CsvHelper;
using System.Globalization;
using Xunit;
namespace CsvHelperTest
{
public class DeviceInfo
{
public DeviceInfo() { }
public int Id2 { get; set; }
}
public class Person
{
public Person() { }
public int Id { get; set; }
// If you comment out either of these 2 properties the Automapping works.
public DeviceInfo DeviceInformation { get; set; }
public Person AssociatePerson { get; set; }
}
public class UnitTest1
{
[Fact]
public void ShouldMap_ButStackOverflows()
{
var l = new List<Person>
{
new Person { Id = 1 },
new Person { Id = 2 }
};
using (var writer = new StreamWriter("d:\\temp\\csvhelper_file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(l);
}
}
}
}