C# Linq get descendants on a subquery - c#

I've been banging my head on the desktop for the past couple of hours trying to decipher this issue.
I'm trying to query an XML file with Linq, the xml has the following format:
<MRLGroups>
<MRLGroup>
<MarketID>6084</MarketID>
<MarketName>European Union</MarketName>
<ActiveIngredientID>28307</ActiveIngredientID>
<ActiveIngredientName>2,4-DB</ActiveIngredientName>
<IndexCommodityID>59916</IndexCommodityID>
<IndexCommodityName>Cucumber</IndexCommodityName>
<ScientificName>Cucumis sativus</ScientificName>
<MRLs>
<MRL>
<PublishedCommodityID>60625</PublishedCommodityID>
<PublishedCommodityName>Cucumbers</PublishedCommodityName>
<MRLTypeID>238</MRLTypeID>
<MRLTypeName>General</MRLTypeName>
<DeferredToMarketID>6084</DeferredToMarketID>
<DeferredToMarketName>European Union</DeferredToMarketName>
<UndefinedCommodityLinkInd>false</UndefinedCommodityLinkInd>
<MRLValueInPPM>0.0100</MRLValueInPPM>
<ResidueDefinition>2,4-DB</ResidueDefinition>
<AdditionalRegulationNotes>Comments.</AdditionalRegulationNotes>
<ExpiryDate xsi:nil="true" />
<PrimaryInd>true</PrimaryInd>
<ExemptInd>false</ExemptInd>
</MRL>
<MRL>
<PublishedCommodityID>60626</PublishedCommodityID>
<PublishedCommodityName>Gherkins</PublishedCommodityName>
<MRLTypeID>238</MRLTypeID>
<MRLTypeName>General</MRLTypeName>
<DeferredToMarketID>6084</DeferredToMarketID>
<DeferredToMarketName>European Union</DeferredToMarketName>
<UndefinedCommodityLinkInd>false</UndefinedCommodityLinkInd>
<MRLValueInPPM>0.0100</MRLValueInPPM>
<ResidueDefinition>2,4-DB</ResidueDefinition>
<AdditionalRegulationNotes>More Comments.</AdditionalRegulationNotes>
<ExpiryDate xsi:nil="true" />
<PrimaryInd>false</PrimaryInd>
<ExemptInd>false</ExemptInd>
</MRL>
</MRLs>
</MRLGroup>
So far i've created classes for the "MRLGroup" section of the file
var queryMarket = from market in doc.Descendants("MRLGroup")
select new xMarketID
{
MarketID = Convert.ToString(market.Element("MarketID").Value),
MarketName = Convert.ToString(market.Element("MarketName").Value)
};
List<xMarketID> markets = queryMarket.Distinct().ToList();
var queryIngredient = from ingredient in doc.Descendants("MRLGroup")
select new xActiveIngredients
{
ActiveIngredientID = Convert.ToString(ingredient.Element("ActiveIngredientID").Value),
ActiveIngredientName = Convert.ToString(ingredient.Element("ActiveIngredientName").Value)
};
List<xActiveIngredients> ingredientes = queryIngredient.Distinct().ToList();
var queryCommodities = from commodity in doc.Descendants("MRLGroup")
select new xCommodities {
IndexCommodityID = Convert.ToString(commodity.Element("IndexCommodityID").Value),
IndexCommodityName = Convert.ToString(commodity.Element("IndexCommodityName").Value),
ScientificName = Convert.ToString(commodity.Element("ScientificName").Value)
};
List<xCommodities> commodities = queryCommodities.Distinct().ToList();
After i got the "catalogues" I'm trying to query the document against the catalogues to achieve some sort of "groups", after all this, i'm going to send this data to the database, the issue here is that the xml files are around 600MB each and i get the everyday, so my approach is to create catalogues and just send the MRLs to the database joined to the "header" table that contains the Catalogues IDs, here's what i've done so far but failed miserably:
//markets
foreach (xMarketID market in markets) {
//ingredients
foreach (xActiveIngredients ingredient in ingredientes) {
//commodities
foreach (xCommodities commodity in commodities) {
var mrls = from m in doc.Descendants("MRLGroup")
where Convert.ToString(m.Element("MarketID").Value) == market.MarketID
&& Convert.ToString(m.Element("ActiveIngredientID").Value) == ingredient.ActiveIngredientID
&& Convert.ToString(m.Element("IndexCommodityID").Value) == commodity.IndexCommodityID
select new
{
ms = new List<xMRLIndividial>(from a in m.Element("MRLs").Descendants()
select new xMRLIndividial{
publishedCommodityID = string.IsNullOrEmpty(a.Element("PublishedCommodityID").Value) ? "" : a.Element("PublishedCommodityID").Value,
publishedCommodityName = a.Element("PublishedCommodityName").Value,
mrlTypeId = a.Element("MRLTypeID").Value,
mrlTypeName = a.Element("MRLTypeName").Value,
deferredToMarketId = a.Element("DeferredToMarketID").Value,
deferredToMarketName = a.Element("DeferredToMarketName").Value,
undefinedCommodityLinkId = a.Element("UndefinedCommodityLinkInd").Value,
mrlValueInPPM = a.Element("MRLValueInPPM").Value,
residueDefinition = a.Element("ResidueDefinition").Value,
additionalRegulationNotes = a.Element("AdditionalRegulationNotes").Value,
expiryDate = a.Element("ExpiryDate").Value,
primaryInd = a.Element("PrimaryInd").Value,
exemptInd = a.Element("ExemptInd").Value
})
};
foreach (var item in mrls)
{
Console.WriteLine(item.ToString());
}
}
}
}
If you notice i'm trying to get just the MRLs descendants but i got this error:
All i can reach on the "a" variable is the very first node of MRLs->MRL not all of them, what is going on?
If you guys could lend me a hand would be super!
Thanks in advance.

With this line...
from a in m.Element("MRLs").Descendants()
...will iterate through all sub-elements, including children of children. Hence your error, since your <PublishedCommodityID> element does not have a child element.
Unless you want to specifically return all child elements of all levels, always use the Element and Elements axis instead of Descendant and Descendants:
from a in m.Element("MRLs").Elements()
That should solve your problem.
However, your query is also difficult to read with the nested foreach loops and the multiple tests for the IDs. You can simplify it with a combination of LINQ and XPath:
var mrls =
from market in markets
from ingredient in ingredientes
from commodity in commodities
let xpath = $"/MRLGroups/MRLGroup[{market.MarketId}]" +
$"[ActiveIngredientID={ingredient.ActiveIngredientId}]" +
$"[IndexCommodityID={commodity.IndexCommodityID}]/MRLs/MRL"
select new {
ms =
(from a in doc.XPathSelectElements(xpath)
select new xMRLIndividial {
publishedCommodityID = string.IsNullOrEmpty(a.Element("PublishedCommodityID").Value) ? "" : a.Element("PublishedCommodityID").Value,
publishedCommodityName = a.Element("PublishedCommodityName").Value,
mrlTypeId = a.Element("MRLTypeID").Value,
mrlTypeName = a.Element("MRLTypeName").Value,
deferredToMarketId = a.Element("DeferredToMarketID").Value,
deferredToMarketName = a.Element("DeferredToMarketName").Value,
undefinedCommodityLinkId = a.Element("UndefinedCommodityLinkInd").Value,
mrlValueInPPM = a.Element("MRLValueInPPM").Value,
residueDefinition = a.Element("ResidueDefinition").Value,
additionalRegulationNotes = a.Element("AdditionalRegulationNotes").Value,
expiryDate = a.Element("ExpiryDate").Value,
primaryInd = a.Element("PrimaryInd").Value,
exemptInd = a.Element("ExemptInd").Value
}).ToList()
};

Related

Multiple table same fields LINQ

I have to take the same values from multiple source and so I used Concat but I have large number of fields and couple of more sources too.
IEnumerable<Parts> partsList = (from parts in xml.XPathSelectElements("//APS/P")
select new WindchillPart
{
Code = (string)parts.Element("Number"),
Part = (string)parts.Element("KYZ"),
Name = (string)parts.Element("Name"),
})
.Concat(from uparts in xml.XPathSelectElements("//APS/U")
select new WindchillPart
{
Code = (string)uparts.Element("Number"),
Part = (string)uparts.Element("KYZ"),
Name = (string)uparts.Element("Name"),
});
I almost have 15 fields and 5 sources. So is there anyway to make the fields as common and just add the sources somewhere to work and simplify this?
You could create an array of all your pathes, and use SelectMany to get the elements. In the end, you call Select just once:
string[] pathes = new string[] { "//APS/P", "//APS/U" };
IEnumerable<Parts> partsList = pathes.SelectMany(path => xml.XPathSelectElements(path)).
Select(uparts => new WindchillPart
{
Code = (string)uparts.Element("Number"),
Part = (string)uparts.Element("KYZ"),
Name = (string)uparts.Element("Name"),
});

select from two list different

i have two list list content listearticle and this the code of this list:
model = (
from article in db.Article
select new
{
ID = article.ID,
ARTICLE = article.CODEARTICLE,
PRIX= article.PRIX,
STOCK=article.STOCK,
IMAGE = article.Image,
DESCRIPTION= article.REFERENCE,
});
and another content list convention and this is the code :
var query = (
from article in db.convention
select new
{
ID = article.ID,
ARTICLE = article.CODEARTICLE,
PRIX = article.Prix,
});
i want to have a list listarticleconvention like this:
foreach (dynamic aa in model)
{
foreach (dynamic aa1 in list1)
{
if (aa.ARTICLE == aa1.ARTICLE)
{
aa.PRIXVHT = aa1.PRIXVHT;
}
}
}
Can someone help me to edit PRIXVHT when this article exist in list1 and thank you for your help
the error apperead is Additional information: Property or indexer '<>f__AnonymousType3.PRIXVHT' cannot be assigned to -- it is read only
i know that's just read read only but i need to edit it how can i do it
N.B/i have to use a form like that i mean something like foreach in this two list
You can join db.Article to db.convention and then select article.PRIX if query.PRIX is null.
from articleA in db.Article
join articleC in db.convention on articleC.ID equals articleA.ID into temp
from query in temp.DefaultIfEmpty()
select new
{
ID = articleA.ID,
ARTICLE = articleA.CODEARTICLE,
PRIX = (query== null ? articleA.PRIX : query.PRIX),
STOCK = articleA.STOCK,
IMAGE = articleA.Image,
DESCRIPTION = articleA.REFERENCE,
});

Neo4j: How to return multiple paths from different starting nodes

I have a question similar to
this question but I am using the c# with the neo4jClient instead of the Java.
I can get the parent path of a given node with the following code but it becomes a performance bottle neck when trying to find the parent path of many nodes. What I would like is a way to call the graph database once with a list of node keys and get back a list of parent paths. So that I can return a dictionary of results instead of a single list.
Any help accomplishing this would be greatly appreciated! Also if my original cypher query can be improved I'm open to that as well.
public IEnumerable<IGenericEntity> GetPath(string entityCompositeKey, GraphRelationship relationship)
{
var entity = new GenericEntity();
entity.setCompositeKey(entityCompositeKey);
var pathToRoot = new List<GenericEntity>(){ entity };
var query = new CypherFluentQuery(graphClient)
.Match("p = (current)-[r:" + relationship.Name + "*0..]->()")
.Where((IGenericEntity current) => current.CompositeKey == entityCompositeKey)
.Return(() => Return.As<IEnumerable<GenericEntity>>("nodes(p)"))
.OrderByDescending("length(p)")
.Limit(10);
var queryText = query.Query.QueryText;
var paramText = query.Query.QueryParameters;
if (query.Results != null)
{
var graphResults = query.Results.FirstOrDefault();
if (graphResults != null && graphResults.ToList().Count > 0)
{
pathToRoot = graphResults.ToList();
}
}
return pathToRoot;
}
There are a few things I'm not sure of - and it's most likely how my test DB is setup.
To answer the initial question of how to pass in multiple start nodes - that's probably best approached using the UNWIND operator, which in Neo4jClient is used like so:
var enumerable = new string[] { "a", "b" }
client.Unwind(enumerable, "item"). /*The rest*/
Obvs, if you place that in the top of your current query you'll get a monster set of nodes back, and you won't know which Root entity refers to which, soo... let's do some projecting...
To project, we must have something to project into:
public class Result {
public GenericEntity Root { get; set; }
public List<GenericEntity> Nodes { get; set; }
public int Length { get; set; }
}
This will contain the Root node, and the path to it, now to fill.
public IEnumerable<Result> GetPath(IEnumerable<string> rootKeys, GraphRelationship relationship)
{
var query = new CypherFluentQuery(Client)
.Unwind(rootKeys, "entityRootKey")
.Match(string.Format("p = (root)-[r:{0}*0..]->()", relationship.Name))
.Where("root.CompositeKey = entityRootKey")
.With("{Root:root, Nodes: nodes(p), Length: length(p)} as res")
.Return((res) => res.As<Result>())
.OrderByDescending("res.Length")
.Limit(10);
var results = query.Results;
return results;
}
I'm not using .Where with a parameter creating Func<T> this is because the parameter is created in the .Unwind statement.
Usage wise - something like this:
var res = GetPath(new[] {"a", "b"}, new GraphRelationship {Name = "RELATED"});
foreach (var result in res)
{
Console.WriteLine($"{result.Root.CompositeKey} => {result.Length}");
foreach (var node in result.Nodes)
Console.WriteLine($"\t{node.CompositeKey}");
}

List not populating correctly

Every time I run this code, certlist reads in the first set of values and writes them successfully to the list. When it runs through the loop again the next set of values overwrites the first one and creates a second one. The end result is two identically values inside the list.
Any help with why it would overwrite the first value and how to fix it would be great.
foreach (var certcard in xdoc.Root.Element("Diver").Element("Certifications").Elements("Certification_Card"))
{
cert.Level = certcard.Element("Level").Value;
cert.Agency = certcard.Element("Agency").Value;
cert.Number = certcard.Element("Number").Value;
cert.Date = Convert.ToDateTime(certcard.Element("Date").Value);
certlist.Add(cert);
}
Your original code was only missing the declaration of cert:
foreach (var certcard in xdoc.Root.Element("Diver").Element("Certifications")
.Elements("Certification_Card"))
{
var cert = new Cert();
cert.Level = certcard.Element("Level").Value;
cert.Agency = certcard.Element("Agency").Value;
cert.Number = certcard.Element("Number").Value;
cert.Date = Convert.ToDateTime(certcard.Element("Date").Value);
certlist.Add(cert);
}
Similarly, you could do this without a loop using Linq:
certlist.AddRange(xdoc.Root.Element("Diver")
.Element("Certifications")
.Elements("Certification_Card")
.Select(c => new Cert
{
Level = c.Element("Level").Value,
Agency = c.Element("Agency").Value,
Number = c.Element("Number").Value,
Date = Convert.ToDateTime(c.Element("Date").Value)
}));
Try this :
foreach (var certcard in xdoc.Root.Element("Diver").Element("Certifications")
.Elements("Certification_Card"))
{
certlist.Add(new Cert()
{
Level = certcard.Element("Level").Value,
Agency = certcard.Element("Agency").Value,
Number = certcard.Element("Number").Value,
Date = Convert.ToDateTime(certcard.Element("Date").Value)
});
}

Returning multiple xml Children with same name on single node using linq c#

I have the following XML and want to return all "schools" children but I only get the first one. (jeffersion/08.36) I looked high and low and banged my head. What am I missing?
<users>
<user>
<role>janitor</role>
<schools>
<school_name>jefferson</school_name>
<keycode>80.36</keycode>
<school_name>mainline</school_name>
<keycode>64.36</keycode>
<school_name>south side</school_name>
<keycode>31</keycode>
</schools>
</user>
</users>
This is only returning the first record.
var results= from schools in myXmlDoc.Descendants("schools")
select new
{
SchoolName = schools.Element("school_name").Value,
KeyCode = schools.Element("keycode").Value
};
I've also tried:
var results= (from schools in myXmlDoc.Descendants("schools")
select new
{
SchoolName = schools.Element("school_name").Value,
KeyCode = schools.Element("keycode").Value
}.ToList();
This gets the values BUT only for the first school:
var schools = (from c in xml.Descendants("user")
select new
{
Name = c.Element("role").Value,
Fields = c.Elements("schools")
.Select(f => new
{
SchoolName = f.Element("school_name").Value,
Keycode = f.Element("keycode").Value
}).ToArray()
}).ToList();
You only have one <schools> element in your source, which is why only one entry is being returned. The XML isn't particularly nicely structured - it would be good to have a <school> element containing each school_name/keycode pair. But assuming you have to live with it, the following should work:
var results= from school in myXmlDoc.Descendants("school_name")
select new
{
SchoolName = school.Value,
KeyCode = school.ElementsAfterSelf("keycode").First().Value
};
This may be helpful:
var result = from c in XElement.Load("Student.xml").Elements("schools")
select c ;
// Execute the query
foreach (var students in result )
{
//do something
}

Categories

Resources