4
votes

Im doing something like that:

var results = from d in myDocument.Elements().Descendants("myName") select d;

foreach (var result in results){
   if (sth...){
      result.replace(myXElement);
   }
}

The problem is that when I replace an element I cant iterate to the next element cause there is null reference. (var result in results).

Is there any other way to replace that element with another one and still be able to iterate through that document ?

thanks for help

the stacktrace is:

System.NullReferenceException was unhandled
  Message="Odwołanie do obiektu nie zostało ustawione na wystąpienie obiektu."

Source="System.Xml.Linq"
  StackTrace:
       w System.Xml.Linq.XContainer.d__a.MoveNext()
       w System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
       w DocSorter.Merge.MergeFiles(String contentFilePath, String directoryPath) w D:\DocSorter\DocSorter\Merge.cs:wiersz 39
       w DocSorter.MainBox.btnMergeFiles_Click(Object sender, EventArgs e) w D:\DocSorter\DocSorter\MainBox.cs:wiersz 85
       w System.Windows.Forms.Control.OnClick(EventArgs e)
       w System.Windows.Forms.Button.OnClick(EventArgs e)
       w System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
       w System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       w System.Windows.Forms.Control.WndProc(Message& m)
       w System.Windows.Forms.ButtonBase.WndProc(Message& m)
       w System.Windows.Forms.Button.WndProc(Message& m)
       w System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       w System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       w System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       w System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       w System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       w System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       w System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       w System.Windows.Forms.Application.Run(Form mainForm)
       w DocSorter.Program.Main() w D:\DocSorter\DocSorter\Program.cs:wiersz 18
       w System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       w System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       w Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       w System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       w System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       w System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
2
var results = myDocument.Descendants("myName");SLaks
What is the call stack of the exception?SLaks
I edited my question, filled it with call stackgruber

2 Answers

3
votes

You cannot modify an IEnumerable while looping through it.

Since LINQ to XML uses deferred execution, it's trying to find the descendants as you modify the XML.

To fix this, you need to put the elements in an array before looping through them, like this:

var results = myDocument.Descendants("myName").ToArray();

foreach (var result in results){
   if (sth...){
      result.replace(myXElement);
   }
}

By calling ToArray(), you force it to enumerate through the elements immediately instead of finding them as you loop through them.

0
votes

It appears you have rolled your own Extension Method replace(myXElement). But it seems that XNode.ReplaceWith Method (Object) would serve your need in this situation.

Here's Microsoft's example for ReplaceWith(Object) taken from its documentation:

== Code ==

XElement xmlTree = new XElement("Root",
    new XElement("Child1", "child1 content"),
    new XElement("Child2", "child2 content"),
    new XElement("Child3", "child3 content"),
    new XElement("Child4", "child4 content"),
    new XElement("Child5", "child5 content")
);

XElement child3 = xmlTree.Element("Child3");

child3.ReplaceWith(
    new XElement("NewChild", "new content")
);

Console.WriteLine(xmlTree);

== Expected Output ==

<Root>
  <Child1>child1 content</Child1>
  <Child2>child2 content</Child2>
  <NewChild>new content</NewChild>
  <Child4>child4 content</Child4>
  <Child5>child5 content</Child5>
</Root>