Get Xml field from database, then get value from a particular element - c#

I would like to store a particular element's values from xml documents that I retrieve from the database (if and only if the xml document has it) into a list of ints.
I have started my attempt to do so like this (messages.MessageXML is an Xml type in an SQL Server database):
List<int> messageXml = (from messages in dbContext.Messages
join transactions in dbContext.Transactions
on messages.TransactionID equals transactions.TransactionID
where transactions.CreatedOn >= StartDate
&& transactions.CreatedOn <= EndDate
select messages.MessageXML
).ToList();
Is what I am wanting to do possible in one LINQ query, or do I need to place messages.MessageXML into a List of XmlDocuments / Strings and then try to query for it that way? I can do that, but I would prefer just to have it all done in one query.
I tried to do this based off of some other questions that I have seen:
select messages.MessageXML.Element("IDThatIWant")
But I keep getting an error that states that 'string' does not contain a definition for Element. If it is a namespace that I'm missing, I'm not sure which one it is.
Any help is appreciated!

You might create and dereference the XML-Linq on the fly in the select clause, like this:
select XElement.Parse(messages.MessageXML).Element("IDThatIWant")
However, to separate SQL and XML and including the restriction (only if element has attribute) it would be something like:
var messageXml = from messages in dbContext.Messages
join transactions in dbContext.Transactions
on messages.TransactionID equals transactions.TransactionID
where transactions.CreatedOn >= StartDate
&& transactions.CreatedOn <= EndDate
select messages.MessageXML;
var messages = from m in messageXml select XElement.Parse(m);
var ids = (from msg in messages
let id = msg.Attribute("IDThatIWant")
where !String.IsNullOrEmpty(id)
select Convert.ToInt32(id)).ToList<int>();

Related

Group data and retrieve every line of the grouping with Entity Framework

I was thinking that maybe, once the grouped data are retrieved in the C# part, I would be able loop through the list of items that were grouped.
var res = db.Commandes.Where(t => t.idMatiere == mod.idMatiereChoisie).GroupBy(t => t.UA_idCa);
foreach(var group in res)
{
foreach(var groupedLines in group)
{
// Always a single line, this loop is useless
}
}
It seems the logic applied here is more like SQL than C#: the grouping result in a single line and you won't see all the grouped items.
It's not a problem that I can't overcome
Tactic I will use: instead of grouping, I'll just query all the lines, and then, while looping, I will verify if UA_idCa is different form the previous data and that will means the next "group" has been reached.
But I wonder... How does someone normally do this cleanly, if it's possible?
Do you have to query again to retrieve a group's content?
Or is the "Tactic I will use" closer to what's best?
This problem is a matter of the combination of SQL server AND Entity Framework.
Seems like one of the value in the grouped part (a value that is different for all the line inside the group) must be marked as not null.
Because when looking for what could be a key, entity doesn't give a damn about nullable values : they could be unique, they could be never null, EF won't even check that.
Once it is marked as NOT NULL in the sql part, EF suddenly understand that there could multiple different unique values in the grouped part...
So basically This :
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,sc.id, 0 as idSousCahier /* THIS IS WHAT I COULD NOT COMPLETELY RETRIEVE
because it could be null ? */
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO
As been changed to this:
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,isnull(sc.id, 0) as idSousCahier /* WOW, NOW EF UNDERSTAND
THERE COULD BE MULTIPLE DIFFERENTS VALUES ONCE DATA ARE GROUPED*/
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO

Linq correlated subquery to same table on multiple columns

I've looked at several other questions related to correlated subqueries but it's still not clear to me how to accomplish what I need. I'm using Entity Framework and C#, and have a table called STEWARDSHIP with the following columns:
STEWARDSHIP_ID (the primary key)
SITE_ID
VISIT_DATE
VISIT_TYPE_ID
I need to identify cases where the same combination of SITE_ID, VISIT_DATE, VISIT_TYPE_ID exists more than once because it could represent a duplicate entry made by end users in error, and then I need to report on the details of these entries. In SQL I would do this by joining to the temporary result of a GROUP BY/HAVING like so:
SELECT * FROM stewardship AS s2,
(SELECT site_id, visit_type_id, CAST(visit_date AS DATE) AS visit_date
FROM stewardship
GROUP BY site_id, visit_type_id, CAST(visit_date AS DATE)
HAVING COUNT(*) > 1) AS s
WHERE s2.site_id = s.site_id
AND s2.visit_type_id = s.visit_type_id
AND CAST(s2.visit_date AS DATE) = s.visit_date
What's the best way to accomplish this in Linq?
Since you're open to a different approach that should be more performant, here is the new SQL to get what I think you're after.
select distinct s1.*
from stewardship s1
inner join stewardship s2 on
s1.stewardship_id <> s2.stewardship_id and
s1.site_id = s2.site_id and
s1.visit_type_id = s2.visit_type_id and
cast(s1.visit_date as date) = cast(s2.visit_date as date)
order by s1.site_id, s1.visit_type_id
Now, to translate that to LINQ, you can use the following statement.
var duplicates = (
from s in Stewardships
join s2 in Stewardships
on new { s.Site_id, s.Visit_type_id, s.Visit_date.Date } equals new { s2.Site_id, s2.Visit_type_id, s2.Visit_date.Date }
where s.Stewardship_id != s2.Stewardship_id
select s)
.Distinct()
.OrderBy(s => s.Site_id)
.ThenBy(s => s.Visit_type_id)
Note that you cannot use anything other than an equijoin for expression joins, so I had to put the non-equijoin (ensuring our matches aren't on the same record via PK) in the where expression. You could also accomplish this with lambdas via the Except() extension method.
The order by is there for readability of the results and to match the SQL statement above.
I hope this helps!
It would be fairly similar to what you've already got.
from s in context.stewardships
group s by new {s.site_id, s.visit_type_id, visit_date} into g
where g.Count() > 1
select g;
This would give you groups of stewardships with similar values. You could "flatten" those results with a SelectMany afterward, but you might find them more useful to work with in groups.
Note that you may need to use SqlFunctions or something to do the equivalent of the cast to date.

Conversion of Sql query to linq

I am trying to convert sql query for select to linq query using EF in MVC but really got stuck with an error.
In SQL I'm able to get 6 records for my query,similarly when I try to convert this to linq it shows some error.
Following is my query in SQL:
SELECT
PurchaseOrderMaster.*, PurchaseOrderDetails.*, Vendor.*,
BusinessUnit.*, InvoiceMaster.*, TenantEmployee.*
FROM
PurchaseOrderMaster
INNER JOIN
PurchaseOrderDetails ON PurchaseOrderMaster.TenantID = PurchaseOrderDetails.TenantID
AND PurchaseOrderMaster.PurchaseOrderNumber = PurchaseOrderDetails.PurchaseOrderNumber
AND PurchaseOrderMaster.PurchaseOrderDate = PurchaseOrderDetails.PurchaseOrderDate
INNER JOIN
InvoiceMaster ON PurchaseOrderMaster.TenantID = InvoiceMaster.TenantID
AND PurchaseOrderMaster.PurchaseOrderNumber = InvoiceMaster.PurchaseOrderNumber
AND PurchaseOrderMaster.PurchaseOrderDate = InvoiceMaster.PurchaseOrderDate
INNER JOIN
BusinessUnit ON PurchaseOrderMaster.TenantID = BusinessUnit.TenantID
AND PurchaseOrderMaster.BusinessUnitID = BusinessUnit.BusinessUnitID
INNER JOIN
TenantEmployee ON PurchaseOrderMaster.TenantID = TenantEmployee.TenantID
INNER JOIN
Vendor ON PurchaseOrderMaster.TenantID = Vendor.TenantID
AND PurchaseOrderMaster.VendorID = Vendor.VendorID
For this query I am able to get 6 records .
And my linq query is:
return (from pom in db.PurchaseOrderMaster
join pod in db.PurchaseOrderDetails on pom.TenantID equals pod.TenantID
where pom.PurchaseOrderNumber == pod.PurchaseOrderNumber && pom.PurchaseOrderDate == pod.PurchaseOrderDate
join inv in db.InvoiceMaster on pom.TenantID equals inv.TenantID
where pom.PurchaseOrderNumber == inv.PurchaseOrderNumber && pom.PurchaseOrderDate == inv.PurchaseOrderDate
join bu in db.BusinessUnit on pom.BusinessUnitID equals bu.BusinessUnitID
join te in db.TenantEmployee on pom.TenantID equals te.TenantID
join v in db.Vendor on pom.TenantID equals v.TenantID
where pom.VendorID == v.VendorID
orderby pom.PurchaseOrderNumber ascending, pom.PurchaseOrderDate descending
select new { pom, pod, inv, bu, te, v }).ToList();
At the time of debugging,following is the error that I'm getting:
{"Invalid column name 'invoiceMasterModel_TenantID'.\r\nInvalid column name 'invoiceMasterModel_PurchaseOrderNumber'.\r\nInvalid column name 'invoiceMasterModel_PurchaseOrderDate'.\r\nInvalid column name 'invoiceMasterModel_InvoiceNumber'.\r\nInvalid column name 'invoiceMasterModel_InvoiceDate'.\r\nInvalid column name 'tenantEmployeeModel_TenantID'.\r\nInvalid column name 'tenantEmployeeModel_EmployeeID'."}
Inside Invoice Table it is not able to find some of the columns and hence throwing the error according to me..
I tried with many possible ways but was unable to solve this.
Any ideas..?
Problem was with my Entity.
What I did is,I added my entity again and according to that I recreated models for the associated tables removing the earlier ones.
It solved my problem finally .
I found this link Entity Framework 5 Invalid Column Name error related to somewhat similar problem.
Here also similar kind of error happened after the date time field. Check if your datetime field PurchaseOrderDate is nullable.
Many tools exist that can convert your sql queries to linq, in case you don't wanna write it urself. Try the following sites, works well in my case:
http://www.sqltolinq.com/
http://www.linqpad.net/

Unable to retrieve record from join table & conditional where clause

My case description:
In my C# and LINQ to SQL application, I am implementing FeserWard.Controls Intellibox. For a sales of handphone, user will type in handphone's IMEI in the intellibox and the box will do search in Table Handphone to look for the user input IMEI and finally display the exact match IMEI record.
Problem: I want to filter out all the (Handphone.IMEI) with status=Available (Item.I_Status="Available"), and from there, when user typing in IMEI, the intellibox list will do search only from the Available IMEI.
SQL
select h.HP_ID, h.IMEI, h.Colour, i.I_Status
from Item i, Handphone h
where i.I_ID = h.HP_ID AND i.I_Status='Available'
I want to replace IEnumerable DoSearch's LINQ with this but stuck.
var availableIMEISearch = from i in dataContext.Items.ToList()
join h in dataContext.Handphones.ToList()
on i.I_ID equals h.HP_ID
where(h.IMEI.StartsWith(searchTerm, StringComparison.OrdinalIgnoreCase)) && (i.I_Status=="Available")
select new { i, h };
return availableIMEISearch;
Current workable method: IEnumerable DoSearch
DataClasses1DataContext dataContext = new DataClasses1DataContext();
public IEnumerable DoSearch(string searchTerm, int maxResults, object extraInfo)
{
var imeiSearch = dataContext.Handphones.ToList()
.Where(h => h.IMEI.StartsWith(searchTerm, StringComparison.OrdinalIgnoreCase));
return imeiSearch;
}
Table1:
Item (PK = I_ID)
Table2:
Handphone (PK,FK1 = HP_ID), where Item.I_ID = Handphone.HP_ID
As Joh Skeet(A SO Hero) mentioned, you should not use ToList in query, because it will promt query to be executed, and you will be fetching full tables data to client joining them on client site. Let Linq generate query expression with join, and send it to DB in order to make join on DB side, delayed execution. Less sideeffects.
You didn't mentioned whether you need case sensitive search of searchterm, but I assume that insensitive is much more convenient, so just remove StringComparison.
As you only need Handphones info -> better exclude i from select term, select only what you will use.
And the last add ToList to return result in order to fetch data.
So here is your new query:
var availableIMEISearch = from i in dataContext.Items
join h in dataContext.Handphones
on i.I_ID equals h.HP_ID
where(h.IMEI.StartsWith(searchTerm) && (i.I_Status=="Available")
select h;
return availableIMEISearch.ToList();
If that still doesn't help.
Verify that you have the data in both tables with join condition;
Open SSMS, run the SQL Server Profiler and chech the query generated. Try to run that query manually to find out whether the data is fetched.

Format DateTime in an EF query

I have a database table that holds information for received files. One of the columns is a DateTime that specifies when the file was received. I need to get a distinct list of months with the year (MM/YYYY) for files received. I need to get it out of this table. There can be thousands of records in this table so the way I have done it, in Oracle, is in my select statement I format the datetime as MM/YYYY and do a sort desc with a distinct clause on it. This give me a list of just the months that a file was received. Very fast and efficient.
Now I need to do this using EFv4....here's the query I used in Oracle. Anyone know how I can translate it using one of EFs ways of querying?
select distinct
to_char( i.date_received, 'MMYYYY')) MonthAndYear
from table1
order by MonthAndYear desc
Well, don't do it like Oracle. Do it like LINQ.
var q = (from i in Context.Table1
select new
{
Month = i.date_received.Month,
Year = i.date_received.Year
}).Distinct();
To get a DateTime out:
var r = q.AsEnumerable().Select(d => new DateTime(d.Year, d.Month, 1));

Categories