Daedalus
Daedalus

Reputation: 537

C# Linq to XML query only returning 1 result

I have the following method which queries an XML file. I need it to return the information on ALL files which have the "System" element matching the 'sys' variable which i pass into my method.

It works OK, but only returns 1 result, when i know that there is more than 1 match in the XML file.

It's as if my Linq query just goes trhough the XML file until it finds a result then stops, whereas i need it to get a collection of ALL matches.

public ListViewItem getDMcollection(string sys)
        {

            XDocument doc = XDocument.Load(Form1.CSDBpath + Form1.projectName + "\\Data.xml");

            var dms = from dm in doc.Descendants("dataModule")
                      where dm.Descendants("system").First().Value == sys
                      select dm;

            foreach (var module in dms)
            {
                    ListViewItem item = new ListViewItem(new string[]
                {
                         module.Element("DMC").Value,
                         module.Element("techName").Value,
                         module.Element("infoName").Value,
                         module.Element("status").Value,
                         module.Element("currentUser").Value,
                         module.Element("validator").Value,
                         module.Element("notes").Value,
                         //dm.Element("size").Value + " kb",
                         //dm.Element("dateMod").Value
                 });

                    return item;

                }

                return null;

        }

This is a sample of the XML file:

<DMs>
  <dataModule>
    <DMC>DMC-AJ-A-29-13-54-00ZZZ-254Z-B_001-00.XML</DMC>
    <techName>Pressure switch</techName>
    <infoName>Clean mechanically</infoName>
    <system>72</system>
    <subsystem>13</subsystem>
    <subsubsystem>60</subsubsystem>
    <status>Checked Out</status>
    <notes>-</notes>
    <currentUser>JakeMemery</currentUser>
    <validator>-</validator>
    <dateMod>-</dateMod>
    <size>-</size>
  </dataModule>
  <dataModule>
    <DMC>DMC-AJ-A-30-15-62-00AAA-066A-D_001-00.XML</DMC>
    <techName>Pressure switch</techName>
    <infoName>Support equipment and tools data</infoName>
    <system>29</system>
    <subsystem>13</subsystem>
    <subsubsystem>54</subsubsystem>
    <status>Checked In</status>
    <notes>-</notes>
    <currentUser>-</currentUser>
    <validator>-</validator>
    <dateMod>-</dateMod>
    <size>-</size>
  </dataModule>
  <dataModule>
    <DMC>DMC-AJ-A-45-60-12-00AAA-420A-B_001-00.XML</DMC>
    <techName>Pressure switch</techName>
    <infoName>General fault isolation procedure</infoName>
    <system>29</system>
    <subsystem>20</subsystem>
    <subsubsystem>10</subsubsystem>
    <status>Checked In</status>
    <notes>-</notes>
    <currentUser>-</currentUser>
    <validator>-</validator>
    <dateMod>-</dateMod>
    <size>-</size>
  </dataModule>
</DMs>

So as an example, i might pass in the value of 29 to my method. And as you can see the XML file above contains two 'System' 29 matches, but my program only retuns 1 of them - the first one.

The method that calls the above and passes in the 'sys' variable is this:

public ListViewItem splitSNS(string fullSNSpath)
        {
            string sns = new String(fullSNSpath.ToCharArray().Where(c => Char.IsDigit(c)).ToArray());

            if (sns.Length.ToString() == "6")
            {
                string sys = sns.Substring(4, 2);
                string subsys = sns.Substring(2, 2);
                string unit = sns.Substring(0, 2);

               ListViewItem dms = getDMcollection(sys, subsys, unit);
               return dms;
            }
            else if (sns.Length.ToString() == "4")
            {
                string sys = sns.Substring(2, 2);
                string subsys = sns.Substring(0, 2);

                ListViewItem dms = getDMcollection(sys, subsys);
                return dms;

            }
            else if (sns.Length.ToString() == "2")
            {
                string sys = sns.Substring(0, 2);

                ListViewItem dms = getDMcollection(sys);
                return dms;
            }

            return null;  
        }

and an extract of the method which calls the above is

 ListViewItem dms = newFilter.splitSNS(fullSNSpath);

                    if (dms != null)
                    {

                       // showfilteredList(dms);
                        listView1.Items.Add(dms);

                        showStatus(dms);
                    }
                    else
                    {
                        MessageBox.Show("There are no DMs to be displayed");
                    }

Upvotes: 1

Views: 866

Answers (3)

the_virt
the_virt

Reputation: 717

As I can see your function returns only one element by design. You try to loop trough query but return item; statement will always return first element in the query. Maybe you need to change it's return type to IEnumerable<ListViewItem> and replace return item; to yield return item; for example?

For your case I would suggest the following change for getDMcollection function:

public IEnumerable<ListViewItem> getDMcollection(string sys)
{    
    XDocument doc = XDocument.Load(Form1.CSDBpath + Form1.projectName + "\\Data.xml");

    var dms = from dm in doc.Descendants("dataModule")
              where dm.Descendants("system").First().Value == sys
              select dm;

    foreach (var module in dms)
    {
        ListViewItem item = new ListViewItem(new string[]
        {
            module.Element("DMC").Value,
            module.Element("techName").Value,
            module.Element("infoName").Value,
            module.Element("status").Value,
            module.Element("currentUser").Value,
            module.Element("validator").Value,
            module.Element("notes").Value,
            //dm.Element("size").Value + " kb",
            //dm.Element("dateMod").Value
        });

        yield return item;    
    }
}

when you call to it, you should iterate it's result to add them to list box

public IEnumerable<ListViewItem> splitSNS(string fullSNSpath)
    {
        string sns = new String(fullSNSpath.ToCharArray().Where(c => Char.IsDigit(c)).ToArray());

        if (sns.Length.ToString() == "6")
        {
            string sys = sns.Substring(4, 2);
            string subsys = sns.Substring(2, 2);
            string unit = sns.Substring(0, 2);

           IEnumerable<ListViewItem> dms = getDMcollection(sys, subsys, unit);
           foreach(var d in dms)
               yield return d;
        }
        else if (sns.Length.ToString() == "4")
        {
            string sys = sns.Substring(2, 2);
            string subsys = sns.Substring(0, 2);

           IEnumerable<ListViewItem> dms = getDMcollection(sys, subsys);
           foreach(var d in dms)
               yield return d;

        }
        else if (sns.Length.ToString() == "2")
        {
            string sys = sns.Substring(0, 2);

           IEnumerable<ListViewItem> dms = getDMcollection(sys);
           foreach(var d in dms)
               yield return d;
        }
    }

And finally...

IEnumerable<ListViewItem> dms = newFilter.splitSNS(fullSNSpath);

                if (dms.Any())
                {

                   // showfilteredList(dms);
                   foreach(var d in dms)
                        listView1.Items.Add(d);

                    showStatus(dms);
                }
                else
                {
                    MessageBox.Show("There are no DMs to be displayed");
                }

Upvotes: 2

Ric
Ric

Reputation: 13248

As mentioned in my comment, remove the First() and you should be fine:

var dms = from dm in doc.Descendants("dataModule")
                  where dm.Element("system").Value == sys
                  select dm;

Upvotes: 3

Mathew Thompson
Mathew Thompson

Reputation: 56449

You want the system item that corresponds to each of the dataModules, not just the first one.

Try this:

var dms = from dm in doc.Descendants("dataModule")
                  where dm.Element("system").Value == sys
                  select dm;

Upvotes: 2

Related Questions