Converting a VB code with an Aggregate clause to C# - c#

I've been trying to convert the following the code from VB to C#:
Dim rowsnotfound As DataRow() = (From rowstb2 As DataRow In dsNew.Tables("parts").Rows.OfType(Of DataRow)() Where (Aggregate rowstb1 As DataRow In dsOld.Tables("parts").Rows.OfType(Of DataRow)() Where ((rowstb1.Item("TRANSACTION")) = (rowstb2.Item("TRANSACTION")) And (rowstb1.Item("DESCRIPTION")) = (rowstb2.Item("DESCRIPTION")) And (rowstb1.Item("QTY")) = (rowstb2.Item("QTY")) And (rowstb1.Item("PART_NUM")) = (rowstb2.Item("PART_NUM"))) Into Count()) = 0).ToArray
I know this will appear as a long strand of code on the computer so here is a snippet of the aggregate portion of the code so you can read it easier:
(Aggregate rowstb1 As DataRow In dsOld.Tables("parts").Rows.OfType(Of DataRow)() Where ((rowstb1.Item("TRANSACTION")) = (rowstb2.Item("TRANSACTION")) And (rowstb1.Item("DESCRIPTION")) = (rowstb2.Item("DESCRIPTION")) And (rowstb1.Item("QTY")) = (rowstb2.Item("QTY")) And (rowstb1.Item("PART_NUM")) = (rowstb2.Item("PART_NUM"))) Into Count())
I researched the aggregate clause and know the basics of how it functions. However, I am having a tremendous amount of trouble converting it to C#. Any help will be greatly appreciated.

What you are doing appears to be equivalent of a SQL LEFT JOIN operation where you want to find elements in the First collection that are not in the Second. You can do this with out having to use an aggregate by using the following:
IEnumerable<DataRow> newRows = dsNew.Tables["parts"].Rows.OfType<DataRow>();
IEnumerable<DataRow> oldRows = dsOld.Tables["parts"].Rows.OfType<DataRow>();
DataRow[] rowsNotFound = newRows
.GroupJoin(oldRows,
o => new
{
Transaction = o.Field<int>("TRANSACTION"),
Description = o.Field<string>("DESCRIPTION"),
Quantity = o.Field<int>("QTY"),
PartNumber = o.Field<string>("PART_NUM")
},
i => new
{
Transaction = i.Field<int>("TRANSACTION"),
Description = i.Field<string>("DESCRIPTION"),
Quantity = i.Field<int>("QTY"),
PartNumber = i.Field<string>("PART_NUM")
},
(o, i) => new {NewRow = o, OldRows = i})
.SelectMany(g => g.OldRows.DefaultIfEmpty(), (g, oldRow) => oldRow == null ? g.NewRow : null)
.Where(r => r != null)
.ToArray();
I didn't know the data types so I guessed based on the field names.

I think it'll be something like:
DataRow[] rowsnotfound = (
from DataRow rowstb2 in dsNew.Tables("parts").Rows.OfType<DataRow>()
where ((
from DataRow rowstb1 in dsOld.Tables("parts").Rows.OfType<DataRow>()
where (rowstb1["TRANSACTION"] == rowstb2["TRANSACTION"]
&& rowstb1["DESCRIPTION"] == rowstb2["DESCRIPTION"]
&& rowstb1["QTY"] == rowstb2["QTY"]
&& rowstb1["PART_NUM"] == rowstb2["PART_NUM"])
select rowstb1).Count()) == 0).ToArray();

Related

How to search with Linq query in multiples columns and bind into datagridview?

I am using C# and Entity Framework and I would like to select in database with some filter condition. Which comes from a simple SQL query like this:
SELECT *
FROM EMPLOYEE
WHERE ACTIVE = 1 (FNAME LIKE '%KEY%' OR LNAME LIKE '%KEY%' OR ADDRESS LIKE '%KEY%')
ORDER BY LASTUPDAATE DESC;
I using in Linq query as below:
var query = (from e in db.TBLEMPLOYEE
where (e.ACTIVE == 1 AND
(e.FNAME.Contains(text.ToString().Trim())
|| e.LNAME.Contains(text.ToString().Trim())
|| e.ADDRESS.Contains(text.ToString().Trim())))
select e).OrderByDescending(e => c.LASTUPDATE);
if (query.Any())
{
int i = 0;
foreach (EMPLOYEE item in query)
{
i += 1;
int newrow = grid.Rows.Add();
grid.Rows[newrow].Cells[0].Value = item.ID.ToString();
grid.Rows[newrow].Cells[1].Value = i.ToString();
grid.Rows[newrow].Cells[2].Value = item.FNAME.ToString();
grid.Rows[newrow].Cells[3].Value = item.LNAME.ToString();
grid.Rows[newrow].Cells[4].Value = item.ACTIVE.ToString();
}
}
But I get an error in the linq query while running:
The function evaluation requires all threads to run.
Unable to evaluate the expression. Operation not supported. Unknown error: 0x80070057.
Any suggestions please?
Thank you in advance.
Ada.
Fix your query:
var seachText=text.Trim();
var query =db.TBLEMPLOYEE
.Where( e=> (e.ACTIVE == 1)
&& ( ( EF.Functions.Like(e.FNAME,$"%{seachText}%")
|| ( EF.Functions.Like(e.LNAME,$"%{seachText}%")
|| ( EF.Functions.Like(e.ADDRESS,$"%{seachText}%") ) )
.OrderByDescending(c => c.LASTUPDATE)
.ToList();
Here is the working.
var query = (from e in db.TBLEMPLOYEE
where (e.ACTIVE == 1 AND
(e.FNAME.Contains(text.ToString().Trim())
|| e.LNAME.Contains(text.ToString().Trim())
|| e.ADDRESS.Contains(text.ToString().Trim())))
select e).OrderByDescending(e => c.LASTUPDATE).ToList();
Thank you very much all.

Linq to SQL grouping with embedded lists causes too many queries

I am developing a query to grab and join some SQL tables in C# and am having some trouble with grouping and enumerables within the dataset. My query is below. This gives me the data in the format I'm looking for, but it takes way too long when I try to add the enumerated list as indicated below. When I look under the hood I can see it is executing way too many SQL queries. I'd like to get it to just one. Using LinqPad:
void Main()
{
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
orderby a.StartTime
group new {a, b} by new
{
a.TestGuid,
a.Name,
a.Description,
a.StartTime,
a.Duration,
a.NumAgents,
a.NumHosts,
a.PassFail,
a.ResultsFilePath,
a.Splunk
}
into g
let scenarioStart = g.Min(s => s.a.StartTime) ?? g.Min(s => s.a.DateCreated)
let testCases = g.Select(s => s.b)
orderby scenarioStart
select new
{
TestGuid = g.Key.TestGuid,
ScenarioRun = new
{
Name = g.Key.Name,
Description = g.Key.Description,
StartTime = scenarioStart,
Duration = g.Key.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = g.Key.NumHosts,
Result = g.Key.PassFail,
ResultsFilePath = g.Key.ResultsFilePath,
SplunkLink = g.Key.Splunk,
// PROBLEM: Causes too many queries:
TestRuns = from t in testCases select t.TestCaseId
}
}).ToLookup(g => g.TestGuid, g => g.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();
}
The "TestRuns" line is causing the excessive queries. Any idea what I am doing wrong here?
Thanks for any insight.
Tough answer to test but I think we can avoid the grouping and multiple queries with something like this: (https://msdn.microsoft.com/en-us/library/bb311040.aspx)
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
into testGroup
select new
{
TestGuid = a.TestGuid,
ScenarioRun = new
{
Name = a.TestGuid,
Description = a.Description,
StartTime = a.StartTime ?? a.DateCreated,
Duration = a.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = a.NumHosts,
Result = a.PassFail,
ResultsFilePath = a.ResultsFilePath,
SplunkLink = a.Splunk,
// PROBLEM: Causes too many queries:
TestRuns =testGroup
}
}).OrderBy(x=>x.StartTime).ToLookup(x => x.TestGuid, x => x.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();

Better way of assigning the variable during LINQ Query

I have this piece of code:
//This is coming from an Excell sheet
var ListOfPropertyElements = dataInternal
.Select(element => new
{
PersonName = DC.EncryptToString((string)element.PersonName),
KeyDate = (DateTime)element.KeyDate
})
.Distinct().ToList();
List<int> idList = new List<int>();//This is used to delete records
//trying to check do I have records in SQL with the ListOfPropertyElements
foreach (var listitems in ListOfPropertyElements)
{
var temp = dbContext.tbl_person
.Where(item => item.ItemName == listitems.personName &&
item.KeyDate == listitems.KeyDate)
.Select(item => item.personID)
.ToArray();
if (temp.Length > 0)
{
idList.Add(temp[0]);
}
}
As an end result I am getting a list of integers. What bothers me the way how I fill that idList variable. During LINQ execution, I convert the result to Array, then bounce it back to list, along with if defense.
Is there a more elegant way to do this? I dont like my Rambo style at all :(
You can directly populate idList:-
List<int> idList = dbContext.tbl_person
.Where(item => ListOfPropertyElements.Select(x => x.PersonName)
.Contains(item.PersonName)
&& ListOfPropertyElements.Select(x => x.KeyDate)
.Contains(item.KeyDate)))
.Select(item => item.personID).ToList();
How about this? I haven't compiled it. And I assume you aren't overly concerned with optimizing the number of iterations.
//Excel sheet data
var ListOfPropertyElements = dataInternal.Select(element => new { PersonName = DC.EncryptToString((string)element.PersonName),
KeyDate = (DateTime)element.KeyDate }).Distinct();
//Filtered Id's
var idList = dbContext.tbl_person.Where(item => ListOfPropertyElements.Any(pElement => item.ItemName == pElement.PersonName && item.KeyDate == pElement.KeyDate))
.Select(fItem => fItem.personID).ToList();

Using Let in Lambda Expression while querying from a datatable

I have a data table and I need to convert the contents of that data table to a class List and I used Linq for that.It worked well.But I was asked to convert that linq query to Lambda Expression and there I had a Little trouble while using Let.I will the sample code.
Working linq query:
var NewUser = (from dt in dsMappedDataFields.Tables[0].Rows.Cast<DataRow>()
let tempDetails = dt.Field<string>("Name") == "Rojer" ? "NW" : "India"
let tempNumber = tempDetails == "India" ? "918956" : "0456"
select new User
{
Name = dt.Field<string>("Name"),
Age = dt.Field<int>("Age"),
Details = tempDetails,
Number = tempNumber
}).ToList();
Lambda expression:
var User = dsMappedDataFields.Tables[0].Rows.Cast<DataRow>().
Select(dr =>
new User
{
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = dr.Field<string>("Details"),
Number = dr.Field<string>("Number")
}).ToList();
As you can see I have to check some conditions before converting the data to list which I have done earlier.. Please do help me with solving this issue.
The lambda you pass to Select can have a block, so you can add any kind of code there:
var User = dsMappedDataFields.Tables[0].Rows
.Cast<DataRow>()
.Select(dr =>
{
var tempDetails = dt.Field<string>("Name") == "Rojer" ? "NW" : "India";
var tempNumber = tempDetails == "India" ? "918956" : "0456";
return new User
{
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = tempDetails,
Number = tempNumber,
};
})
.ToList();
We can use lambda expression in this case :
var User = dsMappedDataFields.Tables[0].Rows.Cast <DataRow>().
Select(dr = > new User {
Name = dr.Field<string>("Name"),
Age = dr.Field<int>("Age"),
Details = dr.Field<string>("Name") == "Rojer" ? "NW" : "India",
Number = dr.Field<string>("Name") == "Rojer" ? 0456 : 918956,
}).ToList();
I tried the sample it works as you need.
Thanks,
Narasimha

How to write this LINQ Query

Hi i have a datatable with following fields
DAT_START
GROUPBY
TXT_LATITTUDE
TXT_LONGITUDE
INT_DIRECTION
INT_CALL_DATA_TYPE
LNG_DURATION
And following is the LINQ query i am using
var data = (from r in dt.AsEnumerable()
where ((r.Field<DateTime>("DAT_START").TimeOfDay.Hours < 20) && (r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= 4))
group r by new { CID = r["GroupBy"], CLatitude = r["TXT_LATITUDE"], CLongitude = r["TXT_LONGITUDE"],CDirection = r["INT_DIRECTION"],
CCallType = r["INT_CALL_DATA_TYPE"],CDuration = r["LNG_DURATION"] }
into groupedTable
select new
{
CellID = groupedTable.Key.CID,
CallCount = groupedTable.Count(),
Longitude = groupedTable.Key.CLongitude,
Latitude = groupedTable.Key.CLatitude,
Direction = groupedTable.Key.CDirection,
CallType = groupedTable.Key.CCallType,
Duration = groupedTable.Key.CDuration
}).OrderByDescending(s => s.CallCount);
It gives me result like this
CellID = 4057,CallCount = 84,Longitude = "",Latitude = "",Direction = "Incoming",CallType = "Voice",Duration = 50
CellID = 4057,CallCount = 8,Longitude = "",Latitude = "",Direction = "Outgoing",CallType = "Voice",Duration =97
CellID = 4057,CallCount = 56,Longitude = "",Latitude = "",Direction = "Incoming",CallType ="SMS" ,Duration = 0
CellID = 4057,CallCount = 41,Longitude = "",Latitude = "",Direction = "Outgoing",CallType = "SMS",Duration = 0
Now i want result like this
CellID = 4057, TotalCommCount = 204, TotalDuration = 147, INSMSCount = 56,OutSMSCount = 41, INVoiceCount = 84,OutVoiceCount = 8,InVoiceDuration =50,OutVoiceDuration = 47
How can i do this. I am struck over here..
It looks like you're grouping by far too much at the moment, which is why you're getting multiple rows. I suspect you want something like this:
from r in dt.AsEnumerable()
where r.Field<DateTime>("DAT_START").TimeOfDay.Hours < 20 &&
r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= 4
group r r["GroupBy"] into g
select new
{
CellID = g.Key,
TotalCommCount = g.Count(),
TotalDuration = g.Sum(r => r.Field<long>("LNG_DURATION")),
InSMSCount = g.Count(r => r.Field<string>("DIRECTION") == "Incoming" &&
r.Field<string>("CALL_TYPE") == "SMS"),
OutSMSCount = g.Count(r => r.Field<string>("DIRECTION") == "Outgoing" &&
r.Field<string>("CALL_TYPE") == "SMS"),
InVoiceCount = g.Count(r => r.Field<string>("DIRECTION") == "Incoming" &&
r.Field<string>("CALL_TYPE") == "Voice"),
OutVoiceCount = g.Count(r => r.Field<string>("DIRECTION") == "Outgoing" &&
r.Field<string>("CALL_TYPE") == "Voice"),
InVoiceDuration = g.Where(r => r.Field<string>("DIRECTION") == "Incoming" &&
r.Field<string>("CALL_TYPE") == "Voice")
.Sum(r => r.Field<long>("DURATION"))
OutVoiceDuration = g.Where(r => r.Field<string>("DIRECTION") == "Outgoing" &&
r.Field<string>("CALL_TYPE") == "Voice"),
.Sum(r => r.Field<long>("DURATION"))
} into summary
order by summary.TotalCommCount descending
select summary;
I suggest you create a view in your SQL database where you can simply aggregate what you need and then you use that view in your Linq query - this will perform fast and it is easy to write.
Sketch of design:
Since the description of your requirements is not totally clear, I have made assumptions.
Create a SQL view such as (it's just to show the idea, change it according to your needs):
CREATE VIEW [dbo].[vSumDurations]
AS
select c.CellId, j1.SumDuration1 as TotalDuration,
j2.SumDuration2 as GroupedDuration, j1.CallCount
from (select distinct t.CID as CellId from [dbo].[YourTable] t) c
left join (select t1.CID as CellId, sum(LNG_Duration) as SumDuration1,
count(*) as CallCount from [dbo].[YourTable] t1
Group By t1.CID) j1
on c.CellId=j1.CellId
left join (select t2.CID as CellId, sum(LNG_Duration) as SumDuration2
from [dbo].[YourTable] t2
Group by t2.CID, t2.Direction, t2.CallType) j2
on c.CellId=j2.CellId
(You know you can easily add this view to your *.EDMX by using "update model from database" in the context menu of the entity diagram page and then tick-mark the view vSumDurations in the "tables/views" section of the dialog which pops up.)
After this preparation your Linq query is very simple because everything is already done in the view. Hence, the code looks like:
var dc = this; // in LinqPad you can set your data context
var data = (from d in dc.vSumDurations select d).OrderByDescending(s => s.CallCount);
data.Dump();
Note: The example is made for LinqPad - you will need a different data context in your real code. Linqpad does not need dc, but it is easier if you declare it for convenience reasons because in your real code you have to provide it.

Categories