Manipulating existing XDocument (crash)

I got the following piece of code from a Silverlight application:

var messages = from message in XcurrentMsg.Descendants("message")
                               where    DateTime.Parse(message.Attribute("timestamp").Value).CompareTo(DateTime.Parse(MessageCache.Last_Cached())) > 0
                               select new
                               {
                                   ip = message.Attribute("ip").Value,
                                   timestamp = message.Attribute("timestamp").Value,
                                   text = message.Value,
                               };
                if (messages == null)
                    throw new SystemException("No new messages recorded. Application tried to access non existing resources!");

            foreach (var message in messages)
            {
                XElement temporaryElement = new XElement("message", message.text.ToString(), new XAttribute("ip", message.ip.ToString()), new XAttribute("timestamp", message.timestamp.ToString()));
                XcurrentMsg.Element("root").Element("messages").Add(temporaryElement);

                AddMessage(BuildMessage(message.ip, message.timestamp, message.text));
                msgCount++;
            }

            MessageCache.CacheXML(XcurrentMsg);
            MessageCache.Refresh();

      

XcurrentMsg is an XDocument retrieved from my server containing messages: Structure

<root>
    <messages>
          <message ip="" timestamp=""> Text </message>
    </messages>
</root>

      

I want to get all the "message" newer than the last time I cached XcurrentMsg. This works great as long as I cut out the "Temporary XElement" and "XcurrentMsg.Element ...." and just use the currentMsg string as output. But I want the "new messages" to be saved in my XcurrentMsg / Cache. Now, if I don't cut this part, my application is going insanely crazy. I think it writes endless XcurrentMsg items without stopping.

I can't figure out what the problem is.

Yours faithfully,

+2


a source to share


2 answers


This is the classic version of LINQ.

The variable messages

contains a link to IEnumerable<someAnonymousType>

. The mistake you are making is assuming that after the assignment messages

, that all Descendents

have been listed and the set has been created someAnonymousType

.

In reality, nothing has happened at this point. It is only when you start to enumerate the set with foreach that the Descendents

anonymous types are enumerated as well. Even at the point the entire set was Descendents

not enumerable, in fact only up to the first element that sets the clause condition where

, then only one projection (selection result) is created as the foreach goes through.

Hence the added messages are also included in the enumeration and because this generates other messages that you end up in an infinite loop, or at least an error complaining about your attempt to change the list that is being enumerated.



If you want to make sure the list of items to enumerate is fixed before the beniging loop adds ToList()

to the request, this will create List<T>

.

var messages = (from message in XcurrentMsg.Descendants("message")
                           where    DateTime.Parse(message.Attribute("timestamp").Value).CompareTo(DateTime.Parse(MessageCache.Last_Cached())) > 0
                           select new
                           {
                               ip = message.Attribute("ip").Value,
                               timestamp = message.Attribute("timestamp").Value,
                               text = message.Value,
                           }).ToList();

      

Please note: there is no need to test for messages

null anyway, it will not be null, it could be an empty enum or list.

+1


a source


Not sure what AddMessage does, but you should defer all additions to XcurrentMsg until the foreach is complete. So, do the following:

foreach (var message in messages)
{
    XElement temporaryElement = new XElement("message", 
        message.text.ToString(), new XAttribute("ip", message.ip.ToString()), 
        new XAttribute("timestamp", message.timestamp.ToString()));
    XcurrentMsg.Element("root").Element("messages").Add(temporaryElement);

    AddMessage(BuildMessage(message.ip, message.timestamp, message.text));
    msgCount++;
}

      

And do it like this:



List<XElement> elementsToAdd = new List<XElement>();
foreach (var message in messages)
{
    XElement temporaryElement = new XElement("message", 
        message.text.ToString(), new XAttribute("ip", message.ip.ToString()), 
        new XAttribute("timestamp", message.timestamp.ToString()));
    elementsToAdd.Add(temporaryElement);

    AddMessage(BuildMessage(message.ip, message.timestamp, message.text));
    msgCount++;
}

XcurrentMsg.Element("root").Element("messages").Add(elementsToAdd.ToArray());

      

Hope this helps!

+1


a source







All Articles