0

I am trying to populate my own object from a XElement:

MidweekMeetingHistoryItem oItem = new MidweekMeetingHistoryItem
{
    Week = datHistoryItemWeekOfMeeting,
    Chairman = nodeHistoryItem.Element("Chairman")?.Value,
    AuxCounsellor1 = nodeHistoryItem.Element("AuxCounsellor1")?.Value,
    AuxCounsellor2 = nodeHistoryItem.Element("AuxCounsellor2")?.Value,
    TeachingMain = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 0).Value,
    TeachingAux1 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 1).Value,
    TeachingAux2 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 2).Value,
};

It has several more properties, all of which read fine. The issue is here:

TeachingMain = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 0).Value,
TeachingAux1 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 1).Value,
TeachingAux2 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 2).Value,

In my XML data, a week that has no Teaching items looks liek this:

<Teaching/>

If it has one class, it looks like this:

<Teaching>
    <Name Class="0">xxx</Name>
</Teaching>

If 2 classes:

<Teaching>
    <Name Class="0">xxx</Name>
    <Name Class="1">yyy</Name>
</Teaching>

And if three:

<Teaching>
    <Name Class="0">xxx</Name>
    <Name Class="1">yyy</Name>
    <Name Class="2">zzz</Name>
</Teaching>

I was hoping that the code:

TeachingMain = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 0).Value,
TeachingAux1 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 1).Value,
TeachingAux2 = nodeHistoryItem.Element("Teaching")?.Elements("Name")?.Single(x => (int?)x.Attribute("Class") == 2).Value,

Would simply set the name if it existed, else, set it as empty. But I am getting exceptions:

<LogEntry Date="2022-05-16 13:35:42" Severity="Exception" Source="MSAToolsLibrary.MSAToolsLibraryClass.ExtractFutureMidweekAssignments" ThreadId="1">
  <Exception Type="System.InvalidOperationException" Source="System.Linq.Enumerable.Single">
    <Message>Sequence contains no matching element</Message>
    <StackTrace>   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
   at MSAToolsLibrary.MSAToolsLibraryClass.ExtractFutureMidweekAssignments(DateTime datWeekOfMeeting, String strHistoryDatabase) in D:\My Programs\2022\MSAToolsLibrary\MSAToolsLibrary\MSAToolsLibraryClass.cs:line 1467</StackTrace>
  </Exception>
</LogEntry>

How do I fix that?


At the moment I have added some more verbose code, after the initial populating of the properties:

var oTeaching = nodeHistoryItem.Element("Teaching");
if(oTeaching != null)
{
    if(oTeaching.HasElements)
    {
        var oName = oTeaching.Elements("Name")
                 .Where(x => x.Attribute("Class").Value == "0").First();
        if (oName != null)
        {
            oItem.TeachingMain = oName.Value;
        }

        oName = oTeaching.Elements("Name")
                         .Where(x => x.Attribute("Class").Value == "1").First();
        if (oName != null)
        {
            oItem.TeachingAux1 = oName.Value;
        }

        oName = oTeaching.Elements("Name")
                         .Where(x => x.Attribute("Class").Value == "2").First();
        if (oName != null)
        {
            oItem.TeachingAux2 = oName.Value;
        }
    }
}

That works, but I was hoping to use something similar to what I was trying to do before.


The MidweekMeetingHistoryItem defined like this:

[Guid("~~~~")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class MidweekMeetingHistoryItem : IMidweekMeetingHistoryItem
{
    public DateTime Week { get; set; }
    public string Chairman { get; set; }
    public string AuxCounsellor1 { get; set; }
    public string AuxCounsellor2 { get; set; }
    public string OpeningPrayer { get; set; }
    public string Treasures1 { get; set; }
    public string Treasures2 { get; set; }
    public string TeachingMain { get; set; }
    public string TeachingAux1 { get; set; }
    public string TeachingAux2 { get; set; }
    public string Living1 { get; set; }
    public string Living2 { get; set; }
    public string Living3 { get; set; }
    public string CBSConductor { get; set; }
    public string CBSReader { get; set; }
    public string ConcludingPrayer { get; set; }
    public string Host { get; set; }
    public string Cohost { get; set; }
}
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 3
    `SingleOrDefault(x => (int?)x.Attribute("Class") == 2)?.Value` – Alexander Petrov May 16 '22 at 13:04
  • @AlexanderPetrov Thanks, for some reason that caused my parent C++ application that uses teh DLL to crash. At the moment I have stayed with the more verbose way of populating the values. – Andrew Truckle May 16 '22 at 13:22
  • @AlexanderPetrov I think the problem is when the Elements(“Name”) returns no records that breaks it. – Andrew Truckle May 16 '22 at 14:21
  • 1
    [`Single`](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.single) throws an exception if there isn't one and only one item in the source sequence that matches the given predicate. So, you are correct about `Elements("Name")` returning no records being the problem. Does the constructor of `MidweekMeetingHistoryItem` set the `Teaching*` properties to an empty string? I'm not sure why your verbose way of handling it would not cause an error but `SingleOrDefault` would, unless your C++ application is not expecting `null`. – Joshua Robinson May 16 '22 at 15:16
  • @JoshuaRobinson Actually, the `MidweekMeetingHistoryItem` does not have a constructor. See updated question. My C++ would expect a string, even if empty. but not `null`. – Andrew Truckle May 16 '22 at 16:29
  • 1
    @JoshuaRobinson That was it. 1. Change to `SingleOrDefault` and 2. Add an empty constructor to `MidweekMeetingHistoryItem`. – Andrew Truckle May 16 '22 at 17:20

0 Answers0