query cannot be enumerated more than once - linq

I want to do the following
var totalNoOfRows = result.First().TotalNumberOfCount;
And finally do something like that
bookssList.AddRange(retResult.Select(r => r.ToBook()));
where ToBook is extended method
but I always get The result of a query cannot be enumerated more than once.
if (result != null)
{
var totalNoOfRows = result.First().TotalNumberOfCount;
pagingContext.ItemsTotal = totalNoOfRows != null ? int.Parse(totalNoOfRows.ToString()) : 0;
var retResult = result.ToList();
// pagingContext.ItemsTotal = totalcount.Value != null ? int.Parse(totalcount.Value.ToString()) : 0;
bookssList.AddRange(retResult.Select(r => r.ToBook()));
}

Hard to guess what are you doing, and how these snippets relate to each other, but if you can enumerate a collection only once, then call ToArray first:
var resultCopy = result.ToArray();
//... any number of operations on resultCopy
Note that calling First also counts as enumerating. So you need to enumerate and copy the collection even before this.

Try changing the code to this, so you only enumerate result once:
var retResult = result.ToList();
var totalNoOfRows = retResult.First().TotalNumberOfCount; //You are now using LINQ on the list, not the query!
pagingContext.ItemsTotal = totalNoOfRows != null ? int.Parse(totalNoOfRows.ToString()) : 0;
// pagingContext.ItemsTotal = totalcount.Value != null ? int.Parse(totalcount.Value.ToString()) : 0;
Logger.LogInfo("Search Payments GetPaymentsWithCount stored procedure result not null and count=" + totalcount);
bookssList.AddRange(retResult.Select(r => r.ToBook()));

Related

Linq to Objects - query objects for any non-numeric data

I am trying to write some logic to determine if all values of a certain property of an object in a collection are numeric and greater than zero. I can easily write this using ForEach but I'd like to do it using Linq to Object. I tried this:
var result = entity.Reports.Any(
x =>
x.QuestionBlock == _question.QuestionBlock
&& (!string.IsNullOrEmpty(x.Data)) && Int32.TryParse(x.Data, out tempVal)
&& Int32.Parse(x.Data) > 0);
It does not work correctly. I also tried this, hoping that the TryParse() on Int32 will return false the first time it encounter a string that cannot be parsed into an int. But it appears the out param will contain the first value string value that can be parsed into an int.
var result = entity.GranteeReportDataModels.Any(
x =>
x.QuestionBlock == _question.QuestionBlock
&& (!string.IsNullOrEmpty(x.Data)) && Int32.TryParse(x.Data, out tempVal));
Any help is greatly appreciated!
If you want to test if "all" values meet a condition, you should use the All extension method off IEnumerable<T>, not Any. I would write it like this:
var result = entity.Reports.All(x =>
{
int result = 0;
return int.TryParse(x.Data, out result) && result > 0;
});
I don't believe you need to test for an null or empty string, because int.TryPrase will return false if you pass in a null or empty string.
var allDataIsNatural = entity.Reports.All(r =>
{
int i;
if (!int.TryParse(r.Data, out i))
{
return false;
}
return i > 0;
});
Any will return when the first row is true but, you clearly say you would like to check them all.
You can use this extension which tries to parse a string to int and returns a int?:
public static int? TryGetInt(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
Then this query works:
bool all = entity.Reports.All(x => {
if(x.QuestionBlock != _question.QuestionBlockint)
return false;
int? data = x.Data.TryGetInt();
return data.HasValue && data.Value > 0;
});
or more readable (a little bit less efficient):
bool all = entityReports
.All(x => x.Data.TryGetInt().HasValue && x.Data.TryGetInt() > 0
&& x.QuestionBlock == _question.QuestionBlockint);
This approach avoids using a local variable as out parameter which is an undocumented behaviour in Linq-To-Objects and might stop working in future. It's also more readable.

I cant make Linq compare geodistances?

Im getting the error:
'new GeoCoordinate(MyEntity.Lat, MyEntity.Lon).GetDistanceTo(14.4416616666667, 5.71794583333333)' is not supported in a 'Where' Mobile Services query expression.
From the following query:
GeoCoordinate mypos = new GeoCoordinate(MainPage.myEntity.Lat, MainPage.myEntity.Lon);
var items = await App.MobileService
.GetTable<MyEntity>()
.Where(MyEntity => MyEntity.Id != MainPage.myEntity.Id && MyEntity.IsSelected == MainPage.myEntity.IsSelected
&& (MyEntity.Lat != 0 && MyEntity.Lon != 0)
&& new GeoCoordinate(MyEntity.Lat, MyEntity.Lon).GetDistanceTo(mypos) <= 5000)
.Take(20)
.ToListAsync();
I'm trying to get 20 entities from a table that is not me and within 5 km of my position.
One thing you need to be aware is that the line expression is not executed locally. It's instead translated into a query string which is sent to the server - otherwise you'd be downloading a lot more data than you need (which you still can do, by the way). Given that, there's no way to translate the method GeoCoordinate.GetDistanceTo into something which can be sent over the wire.
There are some operations which are supported in the server, such as arithmetic (+, -, *, /), and you could potentially implement something similar to what you need. Something like the code below should work (haven't tried it yet). Notice that you'll need to do the conversion between distance in coordinates (lat / lon) and whatever unit is returned by the GetDistanceTo method.
GeoCoordinate mypos = new GeoCoordinate(MainPage.myEntity.Lat, MainPage.myEntity.Lon);
double maxDistance = 1;
var items = await App.MobileService
.GetTable<MyEntity>()
.Where(e => e.Id != MainPage.myEntity.Id &&
e.IsSelected == MainPage.myEntity.IsSelected &&
(e.Lat != 0 && e.Lon != 0) &&
( ((e.Lat - mypos.Lat) * (e.Lat - mypos.Lat) +
(e.Lon - mypos.Lon) * (e.Lon - mypos.Lon)) < maxDistance )
.Take(20)
.ToListAsync();
Or, another alternative as I mentioned before would be to simply not to filter by the distance on that call, and filter it afterwards. Something like the code below:
GeoCoordinate mypos = new GeoCoordinate(MainPage.myEntity.Lat, MainPage.myEntity.Lon);
List<MyEntity> items = new List<MyEntity>();
int skip = 0;
while (items.Count < 20) {
var temp = await App.MobileService
.GetTable<MyEntity>()
.Where(e => e.Id != MainPage.myEntity.Id &&
e.IsSelected == MainPage.myEntity.IsSelected &&
(e.Lat != 0 && e.Lon != 0)
.Skip(skip)
.Take(20)
.ToListAsync();
if (temp.Count == 0) break;
foreach (var item in temp)
{
if (new GeoCoordinate(item.Lat, item.Lon).GetDistanceTo(mypos) <= 5000)
{
items.Add(item);
}
}
}

Combining Sub Queries Into 1 Query Linq

Is there a way I can rewrite the following query to make it just one query?
try
{
var fileIds = (from f in context.SignalTo
where f.SignalFileID == 2
select new { f.GFileID }).ToList();
foreach (var id in fileIds)
{
var pp = (from p in context.ProjectFiles
where p.FileID == id.GFileID && p.ProjectID == ProjectID
select p);
if (pp != null)
{
ProjectFiles projectFile =(ProjectFiles) pp;
projectFile.MStatus = Status;
projectFile.DateLastUpdated = DateTime.Now;
context.SaveChanges();
}
}
}
You can combine the two query parts of your code into one.
You would then need to loop over the result set, making your updates. You would then call context.SaveChanges to submit all changes in one batch.
I can't tell if your existing code actually runs or compiles, but you need something like this:
Get the list of file ids you're interested in:
var fileIds = from f in context.SignalTo
where f.SignalFileID == 2
select f.GFileID;
fileIds is at this point an IQueryable where I assume T is an Int. No query has been excuted yet.
Now you can do
var pps = from p in context.ProjectFiles
where fileIds.Contains(p.FileID) && p.ProjectID == ProjectID
select p;
Still no query executed.
Then iterate over the result set
foreach( var pp in pps ) // query executed now
{
pp.MStatus = Status;
pp.DateLastUpdated = DateTime.Now;
}
context.SaveChanges(); // batch of updates executed now

LINQ: Build a where clause at runtime to include ORs ( || )?

I need to build a where clause at runtime but I need to do an OR with the where clause. Is this possible?
Here is my code. Basically "filter" is a enum Bitwise, son hence filter could be equal to more than 1 of the following. Hence I need to build up the where clause.
If I execute the WHEREs separately then imagine if I do the Untested first, and it returns 0 records that means I can't execute a where on the Tested because its now 0 records.
I will put some pseudo-code below:
string myWhere = "";
if ((filter & Filters.Tested) == Filters.Tested)
{
if (myWhere != "" ) myWhere =myWhere + "||";
myWhere = myWhere " Status == "Tested";
}
if ((filter & Filters.Untested) == Filters.Untested)
{
if (myWhere != "" ) myWhere =myWhere + "||";
myWhere = myWhere " Status == "Untested";
}
if ((filter & Filters.Failed) == Filters.Failed)
{
if (myWhere != "" ) myWhere =myWhere + "||";
myWhere = myWhere " Status == "Failed";
}
// dataApplications = a List of items that include Tested,Failed and Untested.
// dataApplication.Where ( myWhere) --- Confused here!
Is this possible?
I don't want to include lots of "IFs" because there are lots of combinations i.e. no filter, filter= tested Only, filter = Untested and Tested ... and lots more.
If you have this:
IEnumerable<MyType> res = from p in myquery select p;
You can define a
var conditions = new List<Func<MyType, bool>>();
conditions.Add(p => p.PropertyOne == 1);
conditions.Add(p => p.PropertyTwo == 2);
res = res.Where(p => conditions.Any(q => q(p)));
And now the trick to make Lists of Funcs of anonymous objects (and you can easily change it to "extract" the type of anonymous objects)
static List<Func<T, bool>> MakeList<T>(IEnumerable<T> elements)
{
return new List<Func<T, bool>>();
}
You call it by passing the result of a LINQ query. So
var res = from p in elements select new { Id = p.Id, Val = p.Value };
var conditions = MakeList(res);
var statusTexts = new List<string>(); // Add desired status texts
dataApplication.Where(item =>
statusTexts.Any(status => item.Status == status))
Use HashSet<> for statuses, then .Contains will be O(1) instead of usual O(n) for List<>:
var statuses = new HashSet<string>() {"a", "b", "c"};
var list = new[] {
new { Id = 1, status = "a"},
new { Id = 2, status = "b"},
new { Id = 3, status = "z"}
};
var filtered = list.Where(l => statuses.Contains(s => l.status == s));

using if else with LINQ Where

I want to generate dynamic query to check manage the where clause with number of parameters available...if some parameter is null i don't want to include it in the where clause
var test = from p in _db.test
where if(str1 != null){p.test == str} else i dnt wanna check p.test
I have around 14 parameters for the where clause
need help,
thanks
You can do it in steps:
// set up the "main query"
var test = from p in _db.test select _db.test;
// if str1 is not null, add a where-condition
if(str1 != null)
{
test = test.Where(p => p.test == str);
}
In addition to #Fredrik's answer, you can also use the short-circuit rules when evaluating boolean expressions like so:
var test = from p in _db.test
where str1 == null || p.test == str1;
Edit If you have lots of strings to test, (str1, str2, etc...) then you can use the following, which will be translated to an SQL IN clause:
var strings = new List<string>();
if (str1 != null) strings.Add(str1);
if (str2 != null) strings.Add(str2);
if (str3 != null) strings.Add(str3);
...
var test = from p in _db.test
where strings.Contains(p.test);
It's even easier if your strings are already in a collection (which, if you've got 14 of them, I assume they would be...)
Consider param1 and param2 are the parameters. Your query should be as under:
string param1 = "Value1";
string param2 = "Value2";
var q = from bal in context.FxBalanceDetails
where (string.IsNullOrEmpty(param1) || bal.Column1 == param1)
&& (string.IsNullOrEmpty(param2) || bal.Column2 == param2)
select bal;
This will ensure that the where clause gets applied for the particular parameter only when it is not null.
var test =
from p in _db.test
where p.str1 != null ? p.str1 : ""
select p;
Do you check the strings against the same Field of the entity?
If so you can write something like:
var strings = new[] { "foo", "bar", "ok", "", null };
var query = dataContext.YourTable.AsQueryable();
query = strings.Where(s => !string.IsNullOrEmpty(s))
.ToList()
.Aggregate(query, (q, s) => q.Where(e => e.YourField == s));
EDIT:
The previous solution is overcomplicated:
var strings = new[] { "foo", "bar", "ok", "", null }.Where(s => !string.IsNullOrEmpty(s))
.ToList();
var query = dataContext.YourTable.Where(e => strings.Contains(e.YourField));

Resources