Hallo,
2 Fehlermeldungen und meine Frage vorweg:
Ist es möglich, dass der Stacktrace aus System.Linq auf einem Produktionssystem nicht ausgegeben wird. Wenn ja - unter welchen Bedingungen ist dies denkbar ?
Meldung Fremdsystem ( StackTrace unvollständig ??? )
Progr.: ExecuteModifyCommand
Context:
Ausnahme: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei ProjektName.ViewModel.ExecutePasteCommand(Object arg) in C:\ProjektName\ViewModels\ViewModel.cs:Zeile 3030.
bei Cinch.SimpleCommand`2.Execute(T2 parameter)
Meldung Eigensystem
Progr.: <ExecuteModifyCommand>b__1be
Context:
Ausnahme: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei ProjektName.ViewModel.<>c__DisplayClass1ce.<ExecuteModifyCommand>b__1be(ViewModel p) in D:\ProjektName\ViewModels\ViewModel.cs:Zeile 3029.
bei System.Linq.Enumerable.<>c__DisplayClass6_01.<CombinePredicates>b__0(TSource x) bei System.Linq.Enumerable.WhereEnumerableIterator
1.MoveNext()
bei ProjektName.ViewModel.ExecuteModifyCommand(Object arg) in D:\ProjektName\ViewModels\ViewModel.cs:Zeile 3030.
bei Cinch.SimpleCommand`2.Execute(T2 parameter) in D:\ProjektName\CinchV2\Commands\SimpleCommand.cs:Zeile 61.
der dazugehörige Quellcode:
2029: var x = X.Where(p => p.Nr == row.Nr);
3030: foreach (var item in x) // NULL-Ref-Exception
3031 { ... }
**
Ich habe einiges ausprobiert und konnte eine NullReferenceException nur dadurch erzwingen, dass "row" zu null gesetzt wird. Was im Programmablauf
durchaus eine realistische Annahme ist - jedoch nicht berücksichtigt wurde !**
Im Quellcode von .NET habe ich nachgesehen
https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}
Es wird effektiv immer ein Objekt zurückgegeben, was nicht null sein kann.
Weiter habe ich mit Tests wie diesem versucht die Ausnahme zu erzwingen was nicht gelang. Was mich nicht verwundert, wenn innerhalb von foreach die Referenz auf die zu durchlaufende Enumeration gehalten wird.
[TestMethod, ExpectedException(typeof(NullReferenceException))]
public void EnumerableNullReferenceException()
{
var _List = new List<object>();
_List.Add(null);
_List.Add(null);
var results = _List.Where(p => p == null);
foreach (var item in results)
{
results = null;
}
}
Herzlichen Dank !!!
Ein einfaches "JA" auf meine Eingangs gestellte Frage - würde mir weiterhelfen.
Du suchst den Fehler da, wo er nicht ist. 😉
Wenn row null ist, kannst du nicht auf Member der row zugreifen - machst du es doch, dann knallt es. Unanhängig von LinQ bzw. dem Where.
Danke ich nehme keine Pillen 😃
das würde einschließen, dass die PDB nicht zum EXE passt - das läßt sich leicht
provozieren und mit Glück erhalte ich einen Stacktrace ohne Angabe von Zeilen oder komplett Schrott.
Das Problem ist mir wohl bekannt und würde es zunächst einmal ausschließen.
Im Beitrag habe ich die Stacktraces etwas vereinfacht um für fremde Augen es lesbarer zu machen. Im realen Fall vergleiche ich schon die passenden Quellcodestände zur ausgelieferten Exe.
und auch da komme ich eben auf die Zeile
foreach (var item in x)
unmittelbar nach
var x = X.Where(p => p.Nr == row.Nr);
Im Normalfall sollte "row" nicht null sein - dieser Fall lässt sich aber häßlich reproduzieren - deswegen setze ich es im Code gleich selbst auf null !
Ergebnis: es knallt nicht in
var x = X.Where(p => p.Nr == row.Nr);
sondern wie im Fehler auch hier
foreach (var item in x)
Mir ist das auch logisch. Das was ich probiere und die Folge davon.
Aber was Du schreibst ist nicht ganz von der hand zu weisen, denn es gibt sehr wohl auch von anderen Produktionssystemen Fehlermeldungen die den Stacktrace bis System.Linq ausweisen - allerdings auch mal mit und mal ohne Zeilenangaben.
Fehlermeldung:
Ausnahme: System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable1 source, Func
2 selector)
at System.Linq.Enumerable.Min[TSource,TResult](IEnumerable1 source, Func
2 selector)
Hi RED-BARON,
der Fehler tritt an dieser Stelle auf:
p.Nr == row.Nr
Mit deiner Änderung verhinderst du nur, daß die Exception ausgelöst wird.
Ausgeführt wird der Code aber erst beim Durchlaufen der foreach
-Schleife. Da ist der StackTrace auch sehr eindeutig. Deine eigentliche Frage kann ich daher nicht nachvollziehen.
Weeks of programming can save you hours of planning
Abschließend sei noch gesagt, dass der von dir ausgedachte Testfall mit deinem Problem nichts gemein hat.
var results = _List.Where(p => p == null);
ist was komplett anderes als
var irgendeineKlasse = null;
var results = _List.Where(p => p == irgendeineKlasse.IrgendeinMember);
Im ersten Fall werden alle Elemente, die null
sind, ermittelt. Gibt es keine Treffer, hast du eine leere Ergebnismenge. Das foreach
macht dann einfach nix.
Im zweiten Fall soll auf einen Member einer Instanz zugegriffen werden, die null
ist. Das knallt natürlich.
Probiere das mal im Unit-Test aus, dann siehst du auch den "Zeilenversatz" in dem der Fehler auftritt.
Vllt solltest Du auch keine Klassen bauen die über 3000 LOC haben...
Ganz kurz, um es deutllich zu machen:
var x = X.Where(p => p.Nr == row.Nr);
Wird zunächst erstmal nicht ausgeführt. Es ist zu diesem Zeitpunkt erstmal eine Expression. Erst wenn auf die Liste tasächlich zugegriffen wird, wird die Liste materialisiert.
Das würde z.B. durch ein .ToList() oder .ToArray() passieren, oder aber durch ein Durchlaufen der Liste wie in deinem Beispiel z.B.:
foreach (var item in x) // NULL-Ref-Exception
Es macht also durchaus Sinn, dass der Fehler in dieser Zeile auftritt, da erst dort deine Expression von einer Zeile vorher ausgeführt wird. Da dort deine "row" null ist, bekommst du in der foreach Zeile einen Fehler.
Grüße
Vllt solltest Du auch keine Klassen bauen die über 3000 LOC haben...
nicht mein Code - Kollege hat den Fehler Anfang Februar als gefixt dokumentiert und gestern erst wieder - die einzige Änderung an der Datei war, dass mein Kommentar an der Zeile entfernt wurde !
Hi RED-BARON,
der Fehler tritt an dieser Stelle auf:
p.Nr == row.Nr
Mit deiner Änderung verhinderst du nur, daß die Exception ausgelöst wird.
Ausgeführt wird der Code aber erst beim Durchlaufen der
foreach
-Schleife. Da ist der StackTrace auch sehr eindeutig. Deine eigentliche Frage kann ich daher nicht nachvollziehen.
Mit der Änderung wollte ich erzwingen und nichts verhindern - ich vermute an der Stelle knallt es - einzig der Stacktrace fehlt mir um es "sehen" zu können.
Pille hat freilich Recht:
(p => p == null); hat nichts mit dem realen Fall zu tun. Im Test wollte ich sehen was passiert wenn:
foreach (var item in results)
{
results = null;
}
Habe mehrere testfälle erstellt. Und jetzt eine Konsolenanwendung die ich mal versuche am Produktionsrechner auszuführen und bei der "row" null ist. Wenn da System.Linq im Stacktrace vorkommt ... liegt der Fehler womöglich doch wo ganz anders.
Danke an alle die mitgehirnt haben 😃
@Geaz : 100% ( meine aktuelle Annahme ! ) .... der Beweis fehlt mir nur noch 😃 - da bin ich aber dran
Kannst du relativ einfach testen. Mach einfach:
var x = X.Where(p => p.Nr == row.Nr).ToList();
Dann wird es direkt in der Zeile knallen wenn row == null ist.
Wenn da System.Linq im Stacktrace vorkommt ... liegt der Fehler womöglich doch wo ganz anders.
Wenn da System.Linq im Stacktrace vorkommt (was es tun wird), liegt der Fehler trotzdem in der Expression bzw. daran, dass row null
ist. Linq ist hier nur Mittel zum Zweck, um den Fehler auszulösen. Es knallt auch, wenn du z.B.
var test = row.Nr;
schreibst. Wenn du über Linq darauf zugreifst, ist es normal, dass du das auch im Stacktrace siehst.
Ansonsten kannst du den Fehler auch ganz einfach an deinem Rechner nachstellen inkl. Stacktrace und Co. - dafür brauchst du nicht an einen anderen Rechner. Geht sogar ganz einfach beim Debuggen.
Ein Aufwand, um eine fehlende Prüfung auf null
hinzuzufügen.
da bin ich wieder, test.exe auf Produktionsrechner zeigt - kompletten - Stacktrace.
Wenn da System.Linq im Stacktrace vorkommt (was es tun wird), liegt der Fehler
trotzdem in der Expression
richtig. so isses ja auch ( gewollt gewesen ).
Warum die test.exe den kompletten erwarteten Stacktrace ausgibt - mit Enumerable.MoveNext ... aber die Anwendung.exe nicht - hm gute Frage.
Evtl. bei an die 4000LOC ... kommt vll. mal was durcheinander^^ und tatsächlich ist der Fehler in ganz anderer Zeile.
Es gibt kein vernünftigen Grund, warum System.Linq Symbole nur in einer kleinen
test.exe aufgelöst werden können sollen und in einer 10 MB.exe nicht.
Antwort Eingangsfrage: Nein. 😃
Zurück auf Start.
Zurück auf Start.
Hast du den Bug jetzt einfach behoben?