LINQ and XML for compact framework

L

luowan

Hi

I am currently building an application on .NetCF 3.5 in C#. The
application needs to process xml files. I am thinking about using LINQ
to process XML. I wrote a piece of code to modify the specified
element's value.


XmlReaderSettings settings = new
XmlReaderSettings();
settings.ConformanceLevel =
ConformanceLevel.Fragment;
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(inboxPath,
settings);

XElement root = XElement.Load(reader);
IEnumerable<XElement> msgs = from el in
root.Descendants("Message")
where el.Element("MessageID").Value ==
MsgID
select el;

foreach (XElement el in msgs)
{

el.Element("MessageStaus").SetValue(newStatus.ToString());
}
root.Save(inboxPath);

Here is the error message that I received :

msgs.System.Collections.Generic.IEnumerator<TSource>.Current 'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>'
does not contain a definition for 'System' and no extension method
'System' accepting a first argument of type
'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>'
could be found (are you missing a using directive or an assembly
reference?)

Could someone please explain to me why I get such error message? By
the way I have refference all the necessary dlls. (System.linq;
System.xml.linq)

Is it because I am using compact framework? I've tested this code on
a normal framework. It is working fine.
 
W

Wan

That's very strange indeed - what line does the error message point at?

Could you post a short but complete program which demonstrates the
problem?

Seehttp://www.pobox.com/~skeet/csharp/complete.htmlfor details of
what I mean by that.

--
Jon Skeet - <[email protected]>
Web site:http://www.pobox.com/~skeet 
Blog:http://www.msmvps.com/jon.skeet
C# in Depth:http://csharpindepth.com

Actually it is not error, my program is still running. However, if I
place a break point at
IEnumerable<XElement> msgs = from el in
root.Descendants("Message")
where el.Element("MessageID").Value ==
MsgID
select el;





After the statement is excuted, if I look up 'msgs' 's value, I will
get such error message.
 
J

Jon Skeet [C# MVP]

Wan said:
Actually it is not error, my program is still running. However, if I
place a break point at
IEnumerable<XElement> msgs = from el in
root.Descendants("Message")
where el.Element("MessageID").Value ==
MsgID
select el;


After the statement is excuted, if I look up 'msgs' 's value, I will
get such error message.

In that case it sounds like a debugger problem rather than anything
else.
 
J

Jon Skeet [C# MVP]

Wan said:
I've tested on two PCs, both the same.
Can some one post a tested working code of query the XML by using LINQ
with compact framework?

Well hang on - you've posted an error which you look in the debugger.
You've said the program is still running. What's it doing that you
don't expect it to?

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.
 
W

Wan

Well hang on - you've posted an error which you look in the debugger.
You've said the program is still running. What's it doing that you
don't expect it to?

Could you post a short but complete program which demonstrates the
problem?

Seehttp://www.pobox.com/~skeet/csharp/complete.htmlfor details of
what I mean by that.

--
Jon Skeet - <[email protected]>
Web site:http://www.pobox.com/~skeet 
Blog:http://www.msmvps.com/jon.skeet
C# in Depth:http://csharpindepth.com

Here is the full sample code. with the XML sample.




Code Snippetusing System;

using System.Linq;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Xml.Linq;

using System.Xml;

namespace xmlCFtest

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

updateMsg("14722", 5);

}

private void updateMsg(string MsgID, int newStatus)

{

try

{

XmlReaderSettings settings = new XmlReaderSettings();

settings.ConformanceLevel = ConformanceLevel.Fragment;

settings.IgnoreWhitespace = true;

settings.IgnoreComments = true;

XmlReader reader = XmlReader.Create(@"Program Files\messageclient\XML
\Inbox.xml", settings);

XElement root = XElement.Load(reader);

IEnumerable<XElement> msgs = from el in root.Descendants("Message")

where el.Element("MessageID").Value == MsgID

select el;

foreach (XElement el in msgs)

{

el.Element("MessageStaus").SetValue(newStatus.ToString());

}

root.Save(@"Program Files\messageclient\XML\Inbox.xml");

}

catch (Exception e) { }

}

}

}











XML Sample:




Code Snippet<?xml version="1.0"?>
<ArrayOfMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<IMEI xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">35340401169569600</IMEI>
<MessageText xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">hi</MessageText>
<MessageID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">14722</MessageID>
<UserID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">12984</UserID>
<SenderName xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">HardCode Test </SenderName>
<TimeStamp xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">2008-04-29T16:26:36.097</TimeStamp>
<MessageStaus xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">3</MessageStaus>
</Message>

</ArrayOfMessage>


Hi Jon
When I said the program was running, I mean the code was compiled.
However, the code does not do the job. The project I created was
based on Windows Mobile 6 SDK and Compact Framework 3.5.

Regards,
Wan
 
J

Jon Skeet [C# MVP]

Here is the full sample code. with the XML sample.

Well, it's not exactly a full sample - it doesn't contain a Main
method, for instance. It's not even a complete class - where's the
implementation of InitializeComponent?
Hint: console apps are generally a lot shorter.

See http://pobox.com/~skeet/csharp/incomplete.html
When I said the program was running, I mean the code was compiled.
However, the code does not do the job. The project I created was
based on Windows Mobile 6 SDK and Compact Framework 3.5.

When you say "the code does not do the job" what exactly do you mean?
What does it do, and what do you want it to do?

Jon
 
W

Wan

Well, it's not exactly a full sample - it doesn't contain a Main
method, for instance. It's not even a complete class - where's the
implementation of InitializeComponent?
Hint: console apps are generally a lot shorter.

Seehttp://pobox.com/~skeet/csharp/incomplete.html


When you say "the code does not do the job" what exactly do you mean?
What does it do, and what do you want it to do?

Jon

Hi Jon

I think it is complete, Please create a windows application project
with smart device. Put the code under Form.cs. I don't think you
need to implement a 'Main'.

Regards,
Wan
 
W

Wan

Well, it's not exactly a full sample - it doesn't contain a Main
method, for instance. It's not even a complete class - where's the
implementation of InitializeComponent?
Hint: console apps are generally a lot shorter.

Seehttp://pobox.com/~skeet/csharp/incomplete.html


When you say "the code does not do the job" what exactly do you mean?
What does it do, and what do you want it to do?

Jon

'InitializeComponent' method is generated when the project is
created. Since VS 2005, it is seperated from the form class.

Regards,
Wan
 
J

Jon Skeet [C# MVP]

I think it is complete,

No, it's definitely not.
Please create a windows application project
with smart device. Put the code under Form.cs. I don't think you
need to implement a 'Main'.

Only because Visual Studio creates another file for you that *does*
contain Main. Likewise the Form1.Designer.cs file contains
InitializeComponent. Without that, it's not complete.

In short, I can't cut and paste the code into a text editor *on its
own* and compile from the command line, which is what I tend to do
with newsgroup posts. Again, please read
http://pobox.com/~skeet/csharp/incomplete.html

You also *still* haven't said how it behaves differently to how you
expect it to.

Jon
 
W

Wan

No, it's definitely not.


Only because Visual Studio creates another file for you that *does*
contain Main. Likewise the Form1.Designer.cs file contains
InitializeComponent. Without that, it's not complete.

In short, I can't cut and paste the code into a text editor *on its
own* and compile from the command line, which is what I tend to do
with newsgroup posts. Again, please readhttp://pobox.com/~skeet/csharp/incomplete.html

You also *still* haven't said how it behaves differently to how you
expect it to.

Jon

Ok, I am not sure if you complie it from command line will help to see
the problem. As the program was complied with no errors.

I expect this piece of code can go to the XML I pasted, and find the
element as well as changing the value. However, it didn't changed
value. even though it found the XML file, loaded XML, ran the LINQ
statement. Finally when I check the value of "IEnumerable<XElement>
msgs" by watching this variable. It will give me such error message:


msgs.System.Collections.Generic.IEnumerator<TSource>.Current
'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>'
does not contain a definition for 'System' and no extension method
'System' accepting a first argument of type
'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>'
could be found (are you missing a using directive or an assembly
reference?)

I assume, it might be problem of 'System.Collections.Generic' for
compact framework, does not support the XElement type.
 
W

Wan

No, it's definitely not.


Only because Visual Studio creates another file for you that *does*
contain Main. Likewise the Form1.Designer.cs file contains
InitializeComponent. Without that, it's not complete.

In short, I can't cut and paste the code into a text editor *on its
own* and compile from the command line, which is what I tend to do
with newsgroup posts. Again, please readhttp://pobox.com/~skeet/csharp/incomplete.html

You also *still* haven't said how it behaves differently to how you
expect it to.

Jon

The Program Has three files

1. Program.cs
using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;

namespace xmlCFtest
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[MTAThread]
static void Main()
{
Application.Run(new Form1());
}
}
}


2. Form1.cs
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml;

namespace xmlCFtest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
updateMsg("14722", 5);
}

private void updateMsg(string MsgID, int newStatus)
{
try
{
lock ("Hello")
{
XmlReaderSettings settings = new
XmlReaderSettings();
settings.ConformanceLevel =
ConformanceLevel.Fragment;
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(@"Program Files
\messageclient\XML\Inbox.xml", settings);

XElement root = XElement.Load(reader);
IEnumerable<XElement> msgs = from el in
root.Descendants("Message")
where
el.Element("MessageID").Value == MsgID
select el;

foreach (XElement el in msgs)
{

el.Element("MessageStaus").SetValue(newStatus.ToString());
}

root.Save(@"Program Files\messageclient\XML
\Inbox.xml");
}
}
catch (Exception e) { }

}
}
}


3. Form1.Designer.cs
namespace xmlCFtest
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.MainMenu mainMenu1;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should
be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.Menu = this.mainMenu1;
this.components = new System.ComponentModel.Container();
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Dpi;
this.Text = "Form1";
this.AutoScroll = true;
}

#endregion
}
}

Regards,
Wan
 
J

Jon Skeet [C# MVP]

Ok, I am not sure if you complie it from command line will help to see
the problem.

It would if you would tell us how the program behaved differently to
how you expected - without referring to the debugger.
As the program was complied with no errors.

Yes, many short but complete programs do.
I expect this piece of code can go to the XML I pasted, and find the
element as well as changing the value. However, it didn't changed
value. even though it found the XML file, loaded XML, ran the LINQ
statement. Finally when I check the value of "IEnumerable<XElement>
msgs" by watching this variable. It will give me such error message:

Yes, so you've said. That's in the debugger. Now, can you show a
program which produces output X when you expect output Y, without
having to break and look at the debugger? If it's *only* an issue with
the debugger, I'm unlikely to be able to help. If the program isn't
working as expected however, then please specify in what way that is
the case.

Here's an example of what I mean:

<sample post>
Problem: Addition with large numbers appears not to work.

I'm having trouble with addition when it comes to large integers

Program:
using System;

class Test
{
static void Main()
{
int i = 2147483647;
Console.WriteLine("i={0}", i);
Console.WriteLine("i+1={0}", i+1);
}
}

Expected output:
i=2147483647
i+1=2147483648

Actual output:
i=2147483647
i+1=-2147483648

Note that the result is negative. Why?
</sample post>

At this point:
a) I can easily compile and run the code
b) I can understand the problem
c I can verify that I see the problem too
d) I can easily change the code to try to fix it

Now obviously that's a trivial example - but it's a good model. While
you can only express what's wrong in terms of watching a variable in
the debugger, I'm inclined to put the issue down to a debugger
problem.

Jon
 
W

Wan

It would if you would tell us how the program behaved differently to
how you expected - without referring to the debugger.


Yes, many short but complete programs do.


Yes, so you've said. That's in the debugger. Now, can you show a
program which produces output X when you expect output Y, without
having to break and look at the debugger? If it's *only* an issue with
the debugger, I'm unlikely to be able to help. If the program isn't
working as expected however, then please specify in what way that is
the case.

Here's an example of what I mean:

<sample post>
Problem: Addition with large numbers appears not to work.

I'm having trouble with addition when it comes to large integers

Program:
using System;

class Test
{
    static void Main()
    {
        int i = 2147483647;
        Console.WriteLine("i={0}", i);
        Console.WriteLine("i+1={0}", i+1);
    }

}

Expected output:
i=2147483647
i+1=2147483648

Actual output:
i=2147483647
i+1=-2147483648

Note that the result is negative. Why?
</sample post>

At this point:
a) I can easily compile and run the code
b) I can understand the problem
c I can verify that I see the problem too
d) I can easily change the code to try to fix it

Now obviously that's a trivial example - but it's a good model. While
you can only express what's wrong in terms of watching a variable in
the debugger, I'm inclined to put the issue down to a debugger
problem.

Jon

The original XML file:

<?xml version="1.0"?>
<ArrayOfMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<IMEI xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">35340401169569600</IMEI>
<MessageText xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">hi</MessageText>
<MessageID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">14722</MessageID>
<UserID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">12984</UserID>
<SenderName xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">HardCode Test </SenderName>
<TimeStamp xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">2008-04-29T16:26:36.097</TimeStamp>
<MessageStaus xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">3</MessageStaus>
</Message>
</ArrayOfMessage>



After run the program I wrote, I expect the XML to become:
<?xml version="1.0"?>
<ArrayOfMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<IMEI xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">35340401169569600</IMEI>
<MessageText xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">hi</MessageText>
<MessageID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">14722</MessageID>
<UserID xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">12984</UserID>
<SenderName xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">HardCode Test </SenderName>
<TimeStamp xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">2008-04-29T16:26:36.097</TimeStamp>
<MessageStaus xmlns="http://FleetMatics.com/WCFMessageService/
Message.xsd">5</MessageStaus>
</Message>
</ArrayOfMessage>

The last element <MessageStaus></MessageStaus> should change its
value.

Actual Output has nothing changed. However, if I created a normal
framework console application project. And copy, paste this code, it
will do the job. That's why I want to ask how to use LINQ to modify
XML in compact framework.


Wan
 
J

Jon Skeet [C# MVP]

Actual Output has nothing changed. However, if I created a normal
framework console application project. And copy, paste this code, it
will do the job. That's why I want to ask how to use LINQ to modify
XML in compact framework.

Okay, now we're getting somewhere. I can see three possible reasons to
start with:

1) It might not be finding the MessageStaus element
2) It might be finding the MessageStaus element but failing to change
the value properly
3) It might not be able to save the file back to disk
4) It might be throwing an exception, which you're silently swallowing

Try putting some kind of alert (e.g. a MessageBox.Show or
Console.WriteLine) in the foreach loop, to check for condition 1.
Try saving something totally different (e.g. a new XElement) to disk
to check for condition 3.
Try putting an alert in your catch block to check for condition 4.

Jon
 
W

Wan

Okay, now we're getting somewhere. I can see three possible reasons to
start with:

1) It might not be finding the MessageStaus element
2) It might be finding the MessageStaus element but failing to change
the value properly
3) It might not be able to save the file back to disk
4) It might be throwing an exception, which you're silently swallowing

Try putting some kind of alert (e.g. a MessageBox.Show or
Console.WriteLine) in the foreach loop, to check for condition 1.
Try saving something totally different (e.g. a new XElement) to disk
to check for condition 3.
Try putting an alert in your catch block to check for condition 4.

Jon

First of all, thanks for your suggestion. I put some alerts as you
told, I can see that the

IEnumerable<XElement> msgs = from el in root.Descendants("Message")
where
el.Element("MessageID").Value == MsgID
select el;

foreach (XElement el in msgs)
{
el.Element("MessageStaus").SetValue(newStatus.ToString());
}

'msgs' is empty. There is two possiblities here.
1) from el in root.Descendants("Message")
where el.Element("MessageID").Value == MsgID
select el;
This LINQ statement is not working.

2) After the LINQ found the records, it fail to copy the value to
IEnumerable<XElement> msgs

As I said, I have tested this code and same LINQ statement on a
desktop console application. It works fine.


Regards,
Wan
 
J

Jon Skeet [C# MVP]

'msgs' is empty. There is two possiblities here.
1) from el in root.Descendants("Message")
where el.Element("MessageID").Value == MsgID
select el;
This LINQ statement is not working.

Right. What happens if you remove the where clause? Is it successfully
finding the messages themselves when unfiltered? That would also
verify that it's managed to load the document appropriately.
2) After the LINQ found the records, it fail to copy the value to
IEnumerable<XElement> msgs

That's just assignment - pretty unlikely to fail.
As I said, I have tested this code and same LINQ statement on a
desktop console application. It works fine.

Sure. The trick is narrowing down where the difference is :)

Jon
 
W

Wan

Right. What happens if you remove the where clause? Is it successfully
finding the messages themselves when unfiltered? That would also
verify that it's managed to load the document appropriately.


That's just assignment - pretty unlikely to fail.


Sure. The trick is narrowing down where the difference is :)

Jon

Hi Jon

Check this out:
http://farm3.static.flickr.com/2188/2457050340_5840ee808b_o.jpg
I have got a screent shot here.

I've removed the where clause, it is still the same.

Regards,
Wan
 
J

Jon Skeet [C# MVP]


That's focusing on the debugger side of things, which I believe is a
red herring. Concentrate on diagnostics which don't require the
debugger.
I've removed the where clause, it is still the same.

So if you put a call to MessageBox.Show in the foreach loop, having
removed the where clause, you're still not seeing anything? That's
worrying.

When you a call to MessageBox.Show in the exception handler, did it
show any exceptions being thrown?

If you put a call to MessageBox.Show after the call to Save(), did it
show the code getting there successfully?

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top