Chris Chris - 3 months ago 24
C# Question

How to Join two IEnumerables with anonymous types?

I'm using Linq-to-Xml to create two

IEnumerables
but I want to combine the results of the second list with the first list. When I use the
.Concat()
I get the following error:


'IQueryable<>' does not contain a definition for 'Concat' and the best extension method overload 'ParallelEnumerable.Concat<>(ParallelQuery<>, IEnumerable<>)' requires a receiver of type 'ParallelQuery<>'


The two lists I'm trying to
.Concat()
are here:

var findingDetails = from f in element.Elements(ns + "Group")
select new
{
testID = (string)f.Attribute("id").Value,
title = (string)f.Element(ns + "title"),
idrefFD = (string)f.Element(ns + "Rule").Attribute("id").Value,
severity = (string)f.Element(ns + "Rule").Attribute("severity").Value,
description = (string)f.Element(ns + "Rule").Element(ns + "description"),
fixText = (string)f.Element(ns + "Rule").Element(ns + "fixtext")
};
var getStatus = from gs in element.Descendants(ns + "rule-result")
select new
{
idrefStatus = (string)gs.Attribute("idref").Value,
result = (string)gs.Element(ns + "result"),
dateTime = (string)gs.Attribute("time")
};


The error is generated on the
findingDetails
usage here:

var combined = findingDetails.Concat(getStatus);


What I'm trying to get is a
var
with that has everything from
findingDetails
+
getStatus.result
and
getStatus.dateTime
, in one place. This why I only have to iterate over one
foreach()
loop to access all of the values instead of using a nested
foreach()
loop.

Sample XML Document:

<?xml version="1.0" encoding="UTF-8"?>

<cdf:Benchmark style="SCAP_1.1" resolved="1" id="RHEL_6_STIG" xsi:schemaLocation="http://checklists.nist.gov/xccdf/1.1 http://nvd.nist.gov/schema/xccdf-1.1.4.xsd http://cpe.mitre.org/dictionary/2.0 http://scap.nist.gov/schema/cpe/2.2/cpe-dictionary_2.2.xsd" xmlns:cdf="http://checklists.nist.gov/xccdf/1.1" xmlns:cpe="http://cpe.mitre.org/dictionary/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<cdf:status date="2016-04-22">accepted</cdf:status>
<cdf:title>Red Hat Enterprise Linux 6 Security Technical Implementation Guide</cdf:title>
<cdf:description>The Red Hat Enterprise Linux 6 Security Technical Implementation Guide (STIG) is published as a tool to improve the security of Department of Defense (DoD) information systems. Comments or proposed revisions to this document should be sent via e-mail to the following address: disa.stig_spt@mail.mil.</cdf:description>
<cdf:notice id="terms-of-use"></cdf:notice>
<cdf:reference href="http://iase.disa.mil">
<dc:publisher>DISA</dc:publisher>
<dc:source>STIG.DOD.MIL</dc:source>
</cdf:reference>
<cdf:plain-text id="release-info">Release: 11 Benchmark Date: 22 Apr 2016</cdf:plain-text>
<cdf:platform idref="cpe:/o:redhat:enterprise_linux:6"></cdf:platform>
<cdf:version>1</cdf:version>
<cdf:Profile id="MAC-1_Classified">
<cdf:title>I - Mission Critical Classified</cdf:title>
</cdf:Profile>
<cdf:Value id="var_umask_for_daemons">
<cdf:title>daemon umask</cdf:title>
<cdf:description>Enter umask for daemons</cdf:description>
<cdf:value>022</cdf:value>
<cdf:value selector="022">022</cdf:value>
<cdf:value selector="027">027</cdf:value>
</cdf:Value>
<cdf:Group id="V-7055">
<cdf:title>APPNET0031 No Strong Name Verification</cdf:title>
<cdf:description>&lt;GroupDescription&gt;&lt;/GroupDescription&gt;</cdf:description>
<cdf:Rule weight="10.0" id="SV-7438r2_rule" severity="medium">
<cdf:version>APPNET0031</cdf:version>
<cdf:title>Digital signatures assigned to strongly named assemblies must be verified.</cdf:title>
<cdf:description>&lt;VulnDiscussion&gt;A strong name consists of the assembly's identity, simple text name, version number, and culture information (if provided)—plus a public key and a digital signature. Strong names serve to identify the author of the code. If digital signatures used to sign strong name assemblies are not verified, any self signed code can be impersonated. This can lead to a loss of system integrity. &lt;/VulnDiscussion&gt;&lt;FalsePositives&gt;&lt;/FalsePositives&gt;&lt;FalseNegatives&gt;&lt;/FalseNegatives&gt;&lt;Documentable&gt;false&lt;/Documentable&gt;&lt;Mitigations&gt;&lt;/Mitigations&gt;&lt;SeverityOverrideGuidance&gt;&lt;/SeverityOverrideGuidance&gt;&lt;PotentialImpacts&gt;&lt;/PotentialImpacts&gt;&lt;ThirdPartyTools&gt;&lt;/ThirdPartyTools&gt;&lt;MitigationControl&gt;&lt;/MitigationControl&gt;&lt;Responsibility&gt;System Administrator&lt;/Responsibility&gt;&lt;IAControls&gt;DCSL-1&lt;/IAControls&gt;</cdf:description>
<cdf:reference>
<dc:publisher>DISA</dc:publisher>
<dc:identifier>2030</dc:identifier>
<dc:type>DPMS Target</dc:type>
</cdf:reference>
<cdf:fixtext fixref="F-12596r7_fix">Use regedit to remove the values stored in Windows registry key hKLM\Software\Microsoft\StrongName\Verification. There should be no assemblies or hash values listed under this registry key. All assemblies must require strong name verification in a production environment. Strong name assemblies that do not require verification in a development or test environment must have documented approvals from the IAO.</cdf:fixtext>
<cdf:fix id="F-12596r7_fix"></cdf:fix>
<cdf:check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<cdf:check-content-ref href="U_Microsoft_DotNet_Framework_4_V1R4_STIG_SCAP_1-1_Benchmark-oval.xml" name="oval:mil.disa.fso.dotnet:def:2"></cdf:check-content-ref>
</cdf:check>
</cdf:Rule>
</cdf:Group>
<cdf:TestResult start-time="2016-07-20T11:59:59" version="1" test-system="cpe:/a:spawar:scc:4.1" end-time="2016-07-20T12:00:50" id="U_Microsoft_DotNet_Framework_4_V1R4_STIG_SCAP_1-1_Benchmark-xccdf.xml---MAC-2_Classified-1">
<cdf:benchmark href="U_Microsoft_DotNet_Framework_4_V1R4_STIG_SCAP_1-1_Benchmark-xccdf.xml"></cdf:benchmark>
<cdf:organization>SPAWAR Systems Center Atlantic</cdf:organization>
<cdf:identity privileged="true" authenticated="true">CCSAdmin</cdf:identity>
<cdf:profile idref="MAC-2_Classified"></cdf:profile>
<cdf:target>hostname</cdf:target>
<cdf:target-address>192.168.1.000</cdf:target-address>
<cdf:target-facts>
<cdf:fact name="urn:scap:fact:asset:identifier:host_name" type="string">hostname</cdf:fact>
<cdf:fact name="urn:scap:fact:asset:identifier:domain" type="string">.net</cdf:fact>
<cdf:fact name="urn:scap:fact:asset:identifier:fqdn" type="string">hostname.net</cdf:fact>
<cdf:fact name="urn:scap:fact:asset:identifier:os_name" type="string">Windows 7 Enterprise</cdf:fact>
<cdf:fact name="urn:scap:fact:asset:identifier:os_service_pack" type="string">Service Pack 1</cdf:fact>
<cdf:fact name="urn:scap:fact:asset:identifier:ipv4" type="string">192.168.1.000</cdf:fact>
</cdf:target-facts>
<cdf:platform idref="cpe:/a:microsoft:.net_framework:4.0"></cdf:platform>
<cdf:rule-result version="APPNET0031" time="2016-07-20T11:59:59" idref="SV-7438r2_rule" weight="10.0" severity="medium">
<cdf:result>pass</cdf:result>
<cdf:check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<cdf:check-content-ref href="U_Microsoft_DotNet_Framework_4_V1R4_STIG_SCAP_1-1_Benchmark-oval.xml" name="oval:mil.disa.fso.dotnet:def:2"></cdf:check-content-ref>
</cdf:check>
</cdf:rule-result>
<cdf:rule-result version="APPNET0046" time="2016-07-20T11:59:59" idref="SV-7444r3_rule" weight="10.0" severity="medium">
<cdf:result>fail</cdf:result>
<cdf:fix id="F-12602r12_fix"></cdf:fix>
<cdf:check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
<cdf:check-content-ref href="U_Microsoft_DotNet_Framework_4_V1R4_STIG_SCAP_1-1_Benchmark-oval.xml" name="oval:mil.disa.fso.dotnet:def:15"></cdf:check-content-ref>
</cdf:check>
</cdf:rule-result>
</cdf:TestResult>
</cdf:Benchmark>


I changed the queries and now both look like this:

var findingDetails = from f in element.Elements(ns + "Group")
select new
{
idrefStatus = (string)"",
result = (string)"",
dateTime = (string)"",
testID = (string)f.Attribute("id").Value,
title = (string)f.Element(ns + "title"),
idref = (string)f.Element(ns + "Rule").Attribute("id").Value,
severity = (string)f.Element(ns + "Rule").Attribute("severity").Value,
description = (string)f.Element(ns + "Rule").Element(ns + "description"),
fixText = (string)f.Element(ns + "Rule").Element(ns + "fixtext")
};
var getStatus = from gs in element.Descendants(ns + "rule-result")
select new
{
idrefStatus = (string)gs.Attribute("idref").Value,
result = (string)gs.Element(ns + "result"),
dateTime = (string)gs.Attribute("time"),
testID = (string)"",
title = (string)"",
idref = (string)"",
severity = (string)"",
description = (string)"",
fixText = (string)""
};

var combined = findingDetails.Concat(getStatus);


But now the output looks like this

foreach (var cf in combined)
{
Console.WriteLine(cf.idref + ", " + cf.result);
}

SV-7438r2_rule,
SV-7444r3_rule,
SV-40966r1_rule,
SV-41075r1_rule,
, pass
, fail
, pass
, pass


Whereas, I'm trying to get something like this:

SV-7438r2_rule, pass
SV-7444r3_rule, fail
SV-40966r1_rule, pass
SV-41075r1_rule, pass

Answer

From your expected output ("Whereas, I'm trying to get something like this:...") and data you added it seems like what you are looking for is a join and not to concat the lists (concat will give you n*m records)

This is what it seems you are looking for:

 XNamespace ns = "http://checklists.nist.gov/xccdf/1.1";
 var findingDetails = (from f in XDocument.Load("data.xml").Descendants(ns + "Group")
                       select new
                       {
                          testID = (string)f.Attribute("id").Value,
                          title = (string)f.Element(ns + "title"),
                          idref = (string)f.Element(ns + "Rule").Attribute("id").Value,
                          severity = (string)f.Element(ns + "Rule").Attribute("severity").Value,
                          description = (string)f.Element(ns + "Rule").Element(ns + "description"),
                          fixText = (string)f.Element(ns + "Rule").Element(ns + "fixtext")
                       }).ToList();
 var getStatus = (from gs in XDocument.Load("data.xml").Descendants(ns + "rule-result")
                  select new
                  {
                     idrefStatus = (string)gs.Attribute("idref").Value,
                     result = (string)gs.Element(ns + "result"),
                     dateTime = (string)gs.Attribute("time"),
                  }).ToList();

 var result = (from d in findingDetails
               join f in getStatus on d.idref equals f.idrefStatus
               select new { d, f }).ToList();

If you might have Groups that do not have a matching rule-result then use left join instead

Comments