I have the code:
dt = collListItems.GetDataTable().AsEnumerable()
.Where(a => Convert.ToString(a["Expertise"]).Contains(expertise) && Convert.ToString(a["Office"]) == office)
.CopyToDataTable();
filteredCount = dt.Rows.Count();
How should I best handle the event when there are no rows that match? Currently I get "The source contains no DataRows" but I want to set filteredCount to 0 in that case.
Thanks in advance.
Edit: I know a try..catch works but is there a more elegant way?
You certainly do not want to use a try/catch for this. Try/Catch should be used in truly exceptional circumstances, you do not want to have it drive your control flow. In nearly all situations, there are better methods that are built right into the language/library or require minimal effort to code.
In this case, you want to capture the table beforehand so that you do not invoke the GetDataTable() method more times than necessary, because we're going to need it if the query does not include any results. You could also optionally include ToList() on the query if the query itself is expensive or long-running, so you only need to do that once.
Afterwards, it's a matter of testing if there are any rows in the result. If so, you can safely copy to a datatable. Otherwise, just clone the structure of the original table (will not include the rows), so that in either case, you have a proper table structure and can inspect the row count, bind it to a control, etc., and there are no surprises.
var table = collListItems.GetDataTable();
var rows = table.AsEnumerable().Where(...); // optionally include .ToList();
var dt = rows.Any() ? rows.CopyToDataTable() : table.Clone();
int filteredCount = dt.Rows.Count;
How about this solution :
DataRow[] filtered_rows = data.Tables[0].Select(filter_string);
if(filtered_rows.Length > 0)
{
filtered_data = filtered_rows.CopyToDataTable();
}
else
{
filtered_data.Clear();
}
data.Tables[0] is the source table and filtered_data is the resultant table.
you can first judge whether there are rows that match:
var rows = collListItems.GetDataTable().AsEnumerable()
.Where(a => Convert.ToString(a["Expertise"]).Contains(expertise) && Convert.ToString(a["Office"]) == office);
DataTable dt = table.Clone();
if (rows.Count() > 0)
dt = rows.CopyToDataTable();
I think this would be a simpler solution:
var Adj = (from c in View.AdjustmentsDataSource.AsEnumerable()
where c["Adjustment"] != System.DBNull.Value
select c);
if (Adj == null || Adj.Count() == 0)
return;
DataTable dtChanges = Adj.CopyToDataTable();
var results = from myRow in dtL1Users.AsEnumerable()
where (Convert.ToInt32(myRow["No_x0020_of_x0020_L1_x0020_Remin"]) >= Convert.ToInt32(maxL1Escalation) && Convert.ToDateTime(myRow["L2_x0020_Last_x0020_Escalated_x0"]) < DateTime.Now.Date.AddDays(-Convert.ToInt32(reminderinterval)))
select myRow;
foreach (var v in results)
{
collEligible = results.CopyToDataTable<DataRow>();
break;
}
Below code works for me. please try
DataRow []dataRow = dataTable.Select(query, seq);
if (dataRow != null && dataRow.Length > 0)
{
return dataTable.Select(query, seq).CopyToDataTable();
}
Reusable generic solution:
Create an extension method:
public static IEnumerable<T> NullIfEmpty<T>(this IEnumerable<T> source) => source.Count() > 0 ? source : null;
Now you can call this extension method like:
var newTable = dataRows.NullIfEmpty()?.CopyToDataTable();
//Here dataRows can be of any IEnumerable collection such as EnumerableRowCollection<DataRow> in case of DataTable
You can use the extension method Any(): to check before using CopyToDataTable() to avoid No Datarow found exception
IEnumerable<DataRow> result = <<Some Linq Query>>;
if (result.Any())
{
}
Related
Hi Everyone I have the following query. On occasion, there are no results for this query, and I will need it to return a 0, rather than an error.
var count= dt.AsEnumerable().Where(x => x.Field<string>("names").Equals(name) && x.Field<string>("port").Equals("true")).Count();
I have tried adding ?? 0 but the compiler doesn't like that.
thanks for the help!
Enumerable.Count doesn't throw an error if the sequence is empty, it returns 0. Do you instead mean that dt can be null? So either the DataTable is null or one of the strings is null. You don't need to use String.Equals you can use == to compare strings meaningfully, then you don't get an exception. You can also use this shorter way using the overload of Count:
if(dt == null) return 0;
return dt.AsEnumerable()
.Count(x => x.Field<string>("names") == name && x.Field<string>("port") == "true");
Do this:
var count=dt != null ? dt.AsEnumerable().Where(x => x.Field<string>("names").Equals(name) && x.Field<string>("port").Equals("true")).Count() : 0;
It will simply check if dt is null before any operations on dt are executed.
Yoda comparison for handle case when you have null in database:
var count= dt.AsEnumerable()
.Where(x => name.Equals(x.Field<string>("names"))
&& "true".Equals(x.Field<string>("port")))
.Count();
Morning guys.
Using C sharp .net4, and MS Visual Studio 2010.
I have Developed a duplication checker for my windows form program.
It works perfectly and Is virtually Instant on my Datagrid when there are a couple hundred records.
The problem I've noticed is that when there are 6000 records displayed it is not efficient enough at all and takes minutes.
I was wandering if anyone has some good tips to make this method a lot faster either improving upon the existing design or, a different method all together that I've over looked.
Your help is once again much appreciated!
Here's the code:
public void CheckForDuplicate()
{
DataGridViewRowCollection coll = ParetoGrid.Rows;
DataGridViewRowCollection colls = ParetoGrid.Rows;
IList<String> listParts = new List<String>();
int count = 0;
foreach (DataGridViewRow item in coll)
{
foreach (DataGridViewRow items in colls)
{
count++;
if ((items.Cells["NewPareto"].Value != null) && (items.Cells["NewPareto"].Value != DBNull.Value))
{
if ((items.Cells["NewPareto"].Value != DBNull.Value) && (items.Cells["NewPareto"].Value != null) && (items.Cells["NewPareto"].Value.Equals(item.Cells["NewPareto"].Value)))
{
if ((items.Cells["Part"].Value != DBNull.Value) && (items.Cells["Part"].Value != null) && !(items.Cells["Part"].Value.Equals(item.Cells["Part"].Value)))
{
listParts.Add(items.Cells["Part"].Value.ToString());
dupi = true; //boolean toggle
}
}
}
}
}
MyErrorGrid.DataSource = listParts.Select(x => new { Part = x }).ToList();
}
Any Questions let me know and I will do my best to answer them.
If you can, you should try and do this on the underlying data rather than on the UI objects - however I have a hunch that you're seeding it from a set of DataRows, in which case you might not be able to do that.
I think a big part of the issue here is the repeated dereferencing of the cells by name, and the fact that you repeatedly deference the second set of cells. So do it all up front:
var first = (from row in coll.Cast<DataGridViewRow>()
let newpareto = row.Cells["NewPareto"].Value ?? DBNull.Value
let part = row.Cells["Part"].Value ?? DBNull.Value
where newpareto != DBNull.Value && part != DBNull.Value
select new
{ newpareto = newpareto, part = part }).ToArray();
//identical - so a copy-paste job (if not using anonymous type we could refactor)
var second = (from row in colls.Cast<DataGridViewRow>()
let newpareto = row.Cells["NewPareto"].Value ?? DBNull.Value
let part = row.Cells["Part"].Value ?? DBNull.Value
where newpareto != DBNull.Value && part != DBNull.Value
select new
{ newpareto = newpareto, part = part }).ToArray();
//now produce our list of strings
var listParts = (from f in first
where second.Any(v => v.newpareto.Equals(f.newpareto)
&& !v.part.Equals(f.part))
select f.part.ToString()).ToList(); //if you want it as a list.
There is an approach that will make this much more efficient. You need to compute a hash of each item. Items with different hashes can't possibly be duplicates.
Once you have the hashes, you could either sort by hash or use a data structure with efficient keyed retrieval (like Dictionary<TKey,TValue>) to find all the duplicates.
I need some help with a linq query.
Basically, I have a datatable with some columns being enumerated as datarows. I need to get all the rows that match a string[][]. Example below.
string[0][0] = ColumnHeader1
string[0][1] = Hello
string[1][0] = ColumnHeader2
string[1][1] = World
Thus, I need to return all the rows that have ColumnHeader1 CONTAINING "Hello" AND ColumnHeader2 CONTAINING "WORLD".
Any ideas to get me started?
Here's an idea applied to a DataTable:
DataRow[] Filter(DataTable dt, string[][] filter)
{
var data = dt.Rows
.Select()
.Where(dr => filter.All (s => dr[s[0]].ToString().Equals(s[1]))
.ToArray();
return data;
}
If you are enumerating a DataRow, you can use something like this:
string[][] filter;
foreach (DataRow dr in dt.Rows)
{
if (filter.All(s => dr[s[0]].ToString().Equals(s[1])))
// this row should be included
}
If I understand the question correctly, I think this will return the rows you want:
var rows = from r in table.AsEnumerable()
where r.Field<string>(filter[0][0]) == filter[0][1]
&& r.Field<string>(filter[1][0]) == filter[1][1]
select r;
Sorry, Im just learning LINQ and am relatively new at it.
Is it possible to convert the following into LINQ?
foreach (DataRow gradeCount in GraceTable.Rows)
{
if (Convert.ToDecimal(obtMarksRow["Percentage"]) >=
(Convert.ToDecimal(gradeCount["EXG_MARKS_ABOVE"])) &&
(Convert.ToDecimal(obtMarksRow["Percentage"]) <=
Convert.ToDecimal(gradeCount["EXG_MARKS_BELOW"])))
{
string Grade = Convert.ToString(gradeCount["EXG_GRADE_NAME"]);
}
}
Edit : sorry i missed for each loop in ma query and obtMarksRow comes from one more loop which is outside this
I wrote the query like this
var gradeValue = from DataRow gradeRow in GraceTable.Rows
let marksAbove = gradeRow.Field<decimal>("EXG_MARKS_ABOVE")
let marksBelow = gradeRow.Field<decimal>("EXG_MARKS_BELOW")
where obtMarksRow.Field<decimal>("Percentage") >= marksAbove && obtMarksRow.Field<decimal>("Percentage") <= marksBelow
select gradeRow.Field<string>("EXG_GRADE_NAME");
but i am getting the value (gradeValue.ToString() ) as "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Data.DataRow,System.String]"
Whats wrong ?
No, it isn't possible. As the commenters point out, LINQ is for querying collections of things. You don't appear to have a collection here: just an if statement and an assignment.
Furthermore, be careful about trying to convert things to LINQ unnecessarily. As you start to understand LINQ better, you'll find yourself naturally using it for a variety of purposes. But starting off with the assumption that code will be better with LINQ is probably a fallacy.
Edit
As mentioned earlier, LINQ is about querying a collection for a set of results. If you only want one result, you can use Single, First, SingleOrDefault, or FirstOrDefault to get it out of the resulting collection.
var gradeValues = from DataRow gradeRow in GraceTable.Rows
let marksAbove = gradeRow.Field<decimal>("EXG_MARKS_ABOVE")
let marksBelow = gradeRow.Field<decimal>("EXG_MARKS_BELOW")
where obtMarksRow.Field<decimal>("Percentage") >= marksAbove && obtMarksRow.Field<decimal>("Percentage") <= marksBelow
select gradeRow.Field<string>("EXG_GRADE_NAME");
var firstGradeValue = gradeValues.First(); // will throw exception if there were no matches.
Console.WriteLine(firstGradeValue);
Try the following:
var grades = from r in GraceTables.Rows
where obtMarksRow.Field<decimal>("Percentage") >=
r.Field<decimal>("EXG_MARKS_ABOVE") &&
obtMarksRow.Field<decimal>("Percentage") <=
r.Field<decimal>("EXG_MARKS_BELOW")
select r.Field<string>("EXG_GRADE_NAME");
You shouldn't use Linq per se but you should use the DatasetExtensions, brought in alongside linq, to get your columns from the DataRow in a type safe way without the need to convert them, i.e.
if (obtMarksRow.Field<decimal>("Percentage") >= (Convert.ToDecimal(gradeCount["EXG_MARKS_ABOVE"])) && etc...
Basically i want to merge two Iqueryable to one Iqueryable and then return the complete record set after my loop ends. It runs perfectly but in the end my objret have nothing but when i debug the loop obj have some records. wht im doing wrong
IQueryable<MediaType> objret = Enumerable.Empty<MediaType>().AsQueryable();
var typ = _db.MediaTypes.Where(e => e.int_MediaTypeId != 1 && e.int_MediaTypeId_FK == null).ToList();
for (int i = 0; i < typ.Count; i++)
{
IQueryable<MediaType> obj = _db.MediaTypes.Where(e => e.bit_IsActive == true && e.int_MediaTypeId_FK == typ[i].int_MediaTypeId);
IQueryable<MediaType> obj1 = _db.MediaTypes.Where(e => e.int_OrganizationId == Authorization.OrganizationID && e.bit_IsActive == true && e.int_MediaTypeId_FK == typ[i].int_MediaTypeId);
if (obj1.Count() > 0)
obj.Concat(obj1);
if(obj.Count() > 0)
objret.Concat(obj);
}
return objret;
Just like the other query operators, Concat doesn't change the existing sequence - it returns a new sequence.
So these lines:
if (obj1.Count() > 0)
obj.Concat(obj1);
if(obj.Count() > 0)
objret.Concat(obj);
should be
if (obj1.Count() > 0)
objret = objret.Concat(obj1);
if(obj.Count() > 0)
objret = objret.Concat(obj);
I'm not sure how well IQueryable is going to handle this, given that you're mixing LINQ to SQL (? maybe Entities) with Enumerable.AsQueryable, mind you. Given that you're already executing the queries to some extent due to the Count() calls, have you considered building up a List<T> instead?
(You don't need to execute the Count() at all - just call List<T>.AddRange(obj1) and ditto for obj.)
As jeroenh mentioned, ideally it would be nice to use a solution which could do it all that the database without looping at all in your C# code.
I think you should not do this with a for loop. The code you posted will go to the db for every single active mediatype twice to get Count() and additionally, twice to get actual results.
Checking the Count() property is not necessary: concatenating empty result sets has no additional effect.
Furthermore, I think what you're trying to achieve can be done with a single query, something along the lines of (not tested):
// build up the query
var rootTypes = _db.MediaTypes.Where(e => e.int_MediaTypeId != 1 && e.int_MediaTypeId_FK == null);
var activeChildren = _db.MediaTypes
.Where(e => e.bit_IsActive);
var activeChildrenForOrganization = _db.MediaTypes
.Where(e => e.int_OrganizationId == Authorization.OrganizationID && e.bit_IsActive);
var q = from types in rootTypes
join e in activeChildren
on types.int_MediaTypeId equals e.int_MediaTypeId_FK into joined1
join e in activeChildrenForOrganization
on types.int_MediaTypeId equals e.int_MediaTypeId_FK into joined2
select new {types, joined1, joined2};
// evaluate the query and concatenate the results.
// This will only go to the db once
return q.ToList().SelectMany(x => x.joined1.Concat(x.joined2));