Retrieve value from xml using linq - linq

I have this XML data
<Address Location="ABC">
<Add Location="XYZ1" street="street1" />
<Add Location="VZC" street="street1" />
</Address>
I want to find out the value of <add> --> street which is street1
I have tried it as below, not getting the result
var q = from res in xmlDoc.Descendants("Address")
where res.Attribute("Location").Value == "ABC"
&& res.Element("Add").Attribute("Location").Value == "VZC"
select new
{
streetadd= res.Element("Add").Attribute("street").Value,
};
Please someone suggest how to check the condition for child element in this case.

Try this:
var query = from res in xmlDoc.Descendants("Address")
where res.Attribute("Location").Value == "ABC"
from child in res.Elements("Add")
where child.Attribute("Location").Value == "VZC"
select new
{
streetadd = child.Attribute("street").Value
};

Related

EF Core 6, GroupBy with inner collection Sum

I want to create a query which will group a set by some criteria, and it will create a result set witch will contain the sum of some inner list.
This is my query, which fails:
var invoices = await _dbContext.Beneficiaries
.Where(dbEntry => dbEntry.Id == beneficiaryId && dbEntry.ProviderId == providerId)
.SelectMany(dbEntry => dbEntry.Invoices)
.GroupBy(dbEntry => dbEntry.IssueDate.Month)
.Select(dbEntry => new
{
IssueMonth = dbEntry.Key,
VAT = dbEntry.Max(invoice => invoice.VAT),
TotalPay = dbEntry.Select(invoice => invoice.InvoiceEntries.Sum(entry => entry.DelegateHourlyRate)).Max(),
TotalSell = dbEntry.Select(invoice => invoice.InvoiceEntries.Sum(entry => entry.BeneficiaryHourlyRate)).Max(),
})
.Where(group => group.IssueMonth <= _todayDate.UtcNow.Month && group.IssueMonth >= _todayDate.UtcNow.Month - (int)by)
.ToListAsync();
Following is the class hierarchy
public class Beneficiary
{
public ICollection<Invoice> Invoices { get; set; }
}
public class Invoice
{
public ICollection<InvoiceEntry> InvoiceEntries { get; set; }
}
public class InvoiceEntry
{
public decimal DelegateHourlyRate { get; set; }
public decimal BeneficiaryHourlyRate { get; set; }
}
This is the exception I'm getting with EF version 5.0.9.
The LINQ expression 'GroupByShaperExpression: KeySelector:
b.IssueDate, ElementSelector:EntityShaperExpression: EntityType:
Invoice ValueBufferExpression: ProjectionBindingExpression:
EmptyProjectionMember IsNullable: True .Max(invoice =>
invoice.InvoiceEntries.Count)' could not be translated. Either rewrite
the query in a form that can be translated, or switch to client
evaluation explicitly by inserting a call to 'AsEnumerable',
'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See
https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
This is the exception I'm getting with EF version 6.0.8.
SqlException: Cannot perform an aggregate function on an expression
containing an aggregate or a subquery. Cannot perform an aggregate
function on an expression containing an aggregate or a subquery.
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" />
</ItemGroup>
Another variation
var invoices2 = await _dbContext.Beneficiaries
.Where(dbEntry => dbEntry.Id == beneficiaryId && dbEntry.ProviderId == providerId)
.SelectMany(dbEntry => dbEntry.Invoices
.GroupBy(dbEntry => dbEntry.IssueDate.Month)
.Select(dbEntry => new
{
IssueMonth = dbEntry.Key,
VAT = dbEntry.Sum(invoice => invoice.VAT),
TotalPay = dbEntry.Sum(invoice => invoice.InvoiceEntries.Sum(entry => entry.DelegateHourlyRate)),
TotalSell = dbEntry.Sum(invoice => invoice.InvoiceEntries.Sum(entry => entry.BeneficiaryHourlyRate))
}))
.Where(group => group.IssueMonth <= _todayDate.UtcNow.Month && group.IssueMonth >= _todayDate.UtcNow.Month - (int)by)
.ToListAsync();
Which results in
SqlException: Cannot perform an aggregate function on an expression
containing an aggregate or a subquery. Cannot perform an aggregate
function on an expression containing an aggregate or a subquery.
Cannot perform an aggregate function on an expression containing an
aggregate or a subquery. Cannot perform an aggregate function on an
expression containing an aggregate or a subquery.
If I understand your query correctly, it should be another SelectMany. Rewritten query in Query Syntax for readability and change time range condition for using table indexes, if they are exist for sure.
var current = _todayDate.UtcNow;
var prev = current.Date.AddMonths(-1);
var query =
from b in _dbContext.Beneficiaries
from invoice in b.Invoices
where invoice.IssueDate <= current && invoice.IssueDate >= prev
from invoiceEntry in invoice.InvoiceEntries
group new { invoice, invoiceEntry } by new { invoice.IssueDate.Year, invoice.IssueDate.Month } into g
select new
{
IssueMonth = g.Key.Month,
VAT = g.Max(x => x.invoice.VAT),
TotalPay = g.Sum(x => x.invoiceEntry.DelegateHourlyRate),
TotalSell = g.Sum(x => x.invoiceEntry.BeneficiaryHourlyRate)
};
var invoices = query.ToList();

Folders first in paged results using CamlQuery and ListItemCollectionPosition

I'm implementing paging using CamlQuery and ListItemCollection, sorting results by the name field. I would like folders to come first, as they do in UI, e.g. "folder A2, folder B2, file A1, file B1", but instead I get "file A1, folder A2, file B1, folder B2".
What is the best way to accomplish such sorting and paging? Note that for paging I have to specify the value of the sorting field which will be the first record of the page – I've considered adding two sorting fields into CAML, but I'm not sure whether I can use two fields in ListItemCollectionPosition.PagingInfo.
The code I'm currently using is like this:
var queryText = #"
<View>
<Query>
<OrderBy Override='TRUE'>
<FieldRef Name='FileLeafRef' Ascending='True' />
</OrderBy>
</Query>
...
<RowLimit>
10
</RowLimit>
</View>";
var camlQuery = new CamlQuery();
camlQuery.ViewXml = queryText;
camlQuery.ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = "Paged=TRUE&p_ID=1002&p_FileLeafRef=A1"
};
var items = list.GetItems(camlQuery);
For getting results sorted by object type you could utilize FSObjType property, for example the following CAML expressions tells server to return folder items first and then file items:
<OrderBy Override='TRUE'>
<FieldRef Name='FSObjType' Ascending='False' />
</OrderBy>
Regarding ListItemCollectionPosition.PagingInfo property, the following expression tells to return items that come after the item with ID specified via p_ID parameter and sorted by object type:
var camlQuery = new CamlQuery();
camlQuery.ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = "Paged=TRUE&p_FSObjType=1&p_ID=200"
};
Example
The following example returns 200 items:
with with ID starting from 100
and sorted by object type (folder items comes first)
Code:
var itemsCount = 200;
var startItemId = 100;
var queryText = #"
<View>
<Query>
<OrderBy Override='TRUE'>
<FieldRef Name='FSObjType' Ascending='False' />
</OrderBy>
</Query>
<RowLimit>
{0}
</RowLimit>
</View>";
var camlQuery = new CamlQuery
{
ViewXml = string.Format(queryText, itemsCount),
ListItemCollectionPosition = new ListItemCollectionPosition
{
PagingInfo = $"Paged=TRUE&p_FSObjType=1&p_ID={startItemId - 1}"
}
};
var items = list.GetItems(camlQuery);
ctx.Load(items);
ctx.ExecuteQuery();
Update
The example of PagingInfo expression for items to be sorted by type first and then by name:
Paged=TRUE&p_FSObjType=1&p_FileLeafRef=B2&p_ID=100

Multiple LINQ to XML query

I am trying to figure out a multiple where clause that is derived from two different elements. Basically, I want to be able to filter based on name attributes of DataType & Service elements. Appreciate any feedback. Thanks Jay
var services = from dt in doc.Descendants("DataType")
where (string)dt.Attribute("name") == "WELL_INDUSTRY" && (string)dt.Elements("Service").Attributes == "Well_Industry"
from service in dt.Elements("Services").Elements("Service").Elements("Layers").Elements("Layer")
select new
{
Name = (string)service.Attribute("name"),
};
XML:
<DataTypes>
<DataType name="WELL_INDUSTRY">
<Spatial>
<Services>
<Service name="Well_Industry" group="Well" status="Primary" >
<Layers>
<layer name="Bottom Hole Wells" ></layer>
<layer name="Bottom Hole Wells2" ></layer>
</Layers>
I think you're looking for something like that:
var services = from dt in doc.Descendants("DataType")
where (string)dt.Attribute("name") == "WELL_INDUSTRY"
from service in dt.Element("Spatial")
.Elements("Services")
.Elements("Service")
where (string)service.Attribute("name") == "Well_Industry"
from layer in service.Element("Layers")
.Elements("Layer")
select new
{
ServiceName = (string)service.Attribute("name"),
Layers = layer.Select(x => (string)x).ToList()
};
You can add another where after from service ....
You are still able to use service variable within select part of the query.
However, querying for ServiceName when there is ServiceName == "myName" check before seems to be useless. In case you need names of layers, use following select:
select new
{
Name = (string)layes.Attribute("name")
};
var services = from dt in doc.Descendants("DataType")
where (string)dt.Attribute("name") == "WELL_INDUSTRY"
from s in dt.Descendants("Service")
where (string)s.Attribute("name") == "Well_Industry"
from l in s.Descendants("Layer")
select new {
Name = (string)l.Attribute("name")
};
Same can be achieved with XPath:
var xpath = "//DataType[#name='WELL_INDUSTRY']//Service[#name='Well_Industry']//layer";
var services = from l in doc.XPathSelectElements(xpath)
select new {
Name = (string)l.Attribute("name")
};

parameterized oracle query with enterprise library

My .cs file contains this code
DbCommand dbc =
db.GetStoredProcCommand(
string.Format(ConfigurationSettings.AppSettings["INSERT_TBLREPORTTYPE"]
, rtype, zoneid, name));
and my query in web.cofig is:
<add key="INSERT_TBLREPORTTYPE"
value="INSERT INTO TBLREPORT(ID,TYPE,RELATIONID,ISACTIVE,NAME)
VALUES(SEQ_REPORT.NEXTVAL,{0},{1},0,{2}) "/>
How to add parameter so that I can prevent sql injection on my site?
I tried
db.AddInParameter(dbc, "NAME", DbType.String, name);
db.AddInParameter(dbc, "RELATIONID", DbType.Int32, zoneid);
db.AddInParameter(dbc, "TYPE", DbType.String, rtype);
and also
dbc.Parameters[0].DbType = DbType.String;
dbc.Parameters[0].Value = name;
dbc.Parameters[1].DbType = DbType.Int32;
dbc.Parameters[1].Value = zoneid;
dbc.Parameters[2].DbType = DbType.String;
dbc.Parameters[2].Value = rtype;
None of them is working. Can anyone give me suggestions regarding this????
The parameter prefix for Oracle is :. So you should change your SQL to:
<add key="INSERT_TBLREPORTTYPE"
value="INSERT INTO TBLREPORT(ID, TYPE, RELATIONID, ISACTIVE, NAME)
VALUES(SEQ_REPORT.NEXTVAL, :TYPE, :RELATIONID, 0, :NAME) "/>
Then you can add the parameters using:
db.AddInParameter(dbc, ":NAME", DbType.String, name);

Dealing with a null datetime element within xml using linq

HI
I have an example document that looks like
<ItemEntry>
<PurchaseDate>2010-03-18T20:36:32.81108+13:00</PurchaseDate>
<StoreGUID>0a0324ad-5f99-486a-a2d0-870bc6991e9f</StoreGUID>
<ExpiryDate />
<CardID>111111</CardID>
<PurchaseAmount>0</PurchaseAmount>
<RedeemedAmount />
<EntryType>1</EntryType>
<RedeemedDate />
<SalesAssistantID>0</SalesAssistantID>
</ItemEntry>
As you can see there are couple of elements ExpiryDate and RedeemedDate are are empty.
var q = from c in xml.Elements("ItemEntry")
select new mdDetail {
PurchaseDate = (DateTime)c.Element("PurchaseDate"),
StoreGUID = (Guid)c.Element("StoreGUID"),
ExpiryDate = (DateTime?)c.Element("ExpiryDate")??DateTime.MinValue,
CardID = (int)c.Element("CardID"),
PurchaseAmount = (double)c.Element("PurchaseAmount"),
RedeemedAmount = (double?)c.Element("RedeemedAmount"),
EntryType = (int)c.Element("EntryType"),
RedeemedDate = (DateTime?)c.Element("RedeemedDate") ??DateTime.MinValue,
SalesAssistantID = (int)c.Element("SalesAssistantID"),
}
;
foreach (var item in q)
{
}
I am not sure how to deal with the null element value,
I have tried ??DateTime.MinValue and ??null however both give me a "
String was not recognized as a valid DateTime." error.
Any suggestions?
Thank you
ExpiryDate = String.IsNullOrEmpty((string)c.Element("ExpiryDate"))?
DateTime.MinValue : DateTime.Parse((string)c.Element("ExpiryDate"))
"You could also use null instead of DateTime.MinValue if ExpireyDate is
declared to be nullable"
#Gabe, you can't just use null - you need to use (DateTime?)null because the compiler won't know how to convert null into a DateTime object
So if you want the value to just be a blank (null) this would be the final code:
ExpiryDate = String.IsNullOrEmpty(c.Element("ExpiryDate").Value)?
(DateTime?)null : DateTime.Parse(c.Element("ExpiryDate").Value)
Assuming DateTime is a declared a nullable (DateTime?)

Resources